> For the complete documentation index, see [llms.txt](https://docs.useagentex.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.useagentex.com/protocol/on-chain-program.md).

# On-Chain Program

The `agentex_marketplace` Anchor program is the source of truth for all marketplace state. It handles listing registration, payment settlement, rental credential management, and creator earnings.

## Program addresses

| Network | Address                                        |
| ------- | ---------------------------------------------- |
| Devnet  | `H34mrXEUyBYitL5tLV1Kasa2ehFR3wSeNQxpP37PQuPa` |
| Mainnet | Published at general availability              |

## Instructions

### initialize\_protocol

One-time setup called by the deployer to configure protocol-level parameters. Must be called once before any listing can be created.

```
initialize_protocol(
  authority: Signer,
  fee_basis_points: u16,
  fee_recipient: Pubkey
)
```

**Accounts:**

* `protocol_config` (PDA, init): Derived from `["protocol_config"]`
* `authority` (signer): The deployer wallet, becomes the protocol authority
* `system_program`

**Notes:** `fee_basis_points` is the protocol cut taken from every rental (for example `250` = 2.5%). Maximum is `1000` (10%). The `fee_recipient` is the treasury wallet that receives collected fees.

***

### create\_listing

Registers a new agent listing on-chain.

```
create_listing(
  creator: Signer,
  listing_seed: [u8; 32],
  metadata_uri: String,
  price: u64,
  access_type: AccessType
)
```

**Accounts:**

* `listing` (PDA, init): Derived from `["listing", creator.key, listing_seed]`
* `escrow` (PDA): Derived from `["escrow", listing.key]`, holds creator earnings
* `protocol_config` (PDA): Read-only, checked for pause state
* `creator` (signer)
* `system_program`

**Notes:** `listing_seed` is a 32-byte value chosen by the creator (for example a SHA-256 hash of a UUID). This makes PDA derivation deterministic and lets one creator hold multiple listings. `price` is in lamports, and for `PerQuery` listings it is the amount deducted per task rather than a one-time or subscription price. `metadata_uri` must be 200 characters or fewer and should point to a JSON document on Arweave or IPFS describing the agent (see [Agent Packaging Reference](/resources/agent-packaging-reference.md)).

***

### update\_listing

Updates a listing's metadata URI, price, or active status. Only the original creator can call this.

```
update_listing(
  creator: Signer,
  metadata_uri: Option<String>,
  price: Option<u64>,
  is_active: Option<bool>
)
```

Pass `None` for any field you do not want to change.

***

### purchase\_one\_time

Issues a permanent rental credential to a renter. The credential never expires and grants unlimited task runs against the listing.

```
purchase_one_time(buyer: Signer)
```

**Accounts:**

* `credential` (PDA, init): Derived from `["access_credential", buyer.key, listing.key]`
* `listing` (PDA, mut)
* `escrow` (PDA, mut): Receives the creator's share of the payment
* `protocol_config` (PDA)
* `fee_recipient`: The wallet that receives the protocol fee
* `buyer` (signer): The renter's wallet
* `system_program`

**Payment split:** On rental, the protocol fee is deducted from `listing.price` and sent directly to `fee_recipient`. The remainder goes to the listing's escrow account, where the creator can withdraw it at any time.

***

### purchase\_subscription

Issues a time-bound rental credential. The credential expires at `current_timestamp + duration_seconds`, and grants unlimited task runs while it's active.

```
purchase_subscription(buyer: Signer, duration_seconds: i64)
```

**Accounts:** Same structure as `purchase_one_time`.

**Notes:** `duration_seconds` must be greater than zero. Typical values are `2592000` (30 days), `7776000` (90 days), or `31536000` (365 days).

***

### purchase\_per\_query

Opens a per-task balance account and makes an initial SOL deposit. Each subsequent task execution deducts `listing.price` from this balance.

```
purchase_per_query(buyer: Signer, initial_deposit: u64)
```

**Accounts:**

* `per_query_balance` (PDA, init): Derived from `["per_query_balance", buyer.key, listing.key]`
* `listing` (PDA, mut)
* `escrow` (PDA, mut)
* `protocol_config` (PDA)
* `fee_recipient`
* `buyer` (signer): The renter's wallet
* `system_program`

**Notes:** `initial_deposit` must be enough to cover at least one task at `listing.price`. The protocol fee is deducted from the deposit amount at the time of deposit, not per task.

***

### renew\_subscription

Extends an existing subscription credential. If the subscription is still active, the new period is added on top of the current expiry. If it has already lapsed, the new period starts from the time of renewal.

```
renew_subscription(buyer: Signer, duration_seconds: i64)
```

***

### topup\_balance

Adds more SOL to an existing per-task balance account. The protocol fee is deducted from the top-up amount the same way as the initial deposit.

```
topup_balance(buyer: Signer, amount: u64)
```

`amount` must be at least `listing.price` (enough for one task).

***

### consume\_query

Deducts one task's worth of balance from a per-task account. Only callable by the protocol authority. The Agentex backend calls this on the renter's behalf immediately before returning each task's result.

```
consume_query(authority: Signer)
```

Returns `InsufficientBalance` (6003) if `balance_lamports < listing.price`.

***

### withdraw\_earnings

Transfers accumulated SOL from a listing's escrow account to the creator's wallet.

```
withdraw_earnings(creator: Signer, amount: Option<u64>)
```

If `amount` is `None`, the full available balance is withdrawn. If `amount` is `Some(n)`, exactly `n` lamports are transferred. Returns `WithdrawalExceedsBalance` if the requested amount exceeds what is available, or `NothingToWithdraw` if the escrow balance is zero.

***

### revoke\_access

Marks a renter's OneTime or Subscription credential as revoked. Reserved for content and conduct policy violations. Only the listing creator can call this.

```
revoke_access(creator: Signer, buyer: Pubkey)
```

***

### revoke\_per\_query\_access

Marks a renter's per-task balance account as revoked. The remaining balance stays in escrow. Only the listing creator can call this.

```
revoke_per_query_access(creator: Signer, buyer: Pubkey)
```

***

### update\_protocol

Updates protocol-level parameters: the fee rate, the fee recipient, or the pause state. Only the protocol authority can call this.

```
update_protocol(
  authority: Signer,
  fee_basis_points: Option<u16>,
  fee_recipient: Option<Pubkey>,
  is_paused: Option<bool>
)
```

Pass `None` for any field you do not want to change. Pausing the protocol (`is_paused: true`) halts new rentals and withdrawals across every listing; it's an emergency control, not a routine operation.

***

### close\_listing

Permanently closes a listing and reclaims the rent paid for its account. The escrow must be empty first, so call `withdraw_earnings` to drain any remaining balance before calling this. Only the listing creator can call this.

```
close_listing(creator: Signer)
```

Returns `EscrowNotEmpty` if the escrow still holds unclaimed earnings.

***

## Account structures

### ProtocolConfig

```rust
pub struct ProtocolConfig {
    pub authority: Pubkey,
    pub fee_basis_points: u16,
    pub fee_recipient: Pubkey,
    pub is_paused: bool,
    pub bump: u8,
}
```

PDA seeds: `["protocol_config"]`

### Listing

```rust
pub struct Listing {
    pub creator: Pubkey,
    pub listing_seed: [u8; 32],
    pub metadata_uri: String,
    pub price: u64,
    pub access_type: AccessType,
    pub is_active: bool,
    pub created_at: i64,
    pub total_purchases: u64,
    pub staked_intl: u64,
    pub bump: u8,
}
```

PDA seeds: `["listing", creator.key, listing_seed]`

### AccessCredential

```rust
pub struct AccessCredential {
    pub buyer: Pubkey,
    pub listing_id: Pubkey,
    pub access_type: AccessType,
    pub issued_at: i64,
    pub expires_at: Option<i64>,
    pub is_revoked: bool,
    pub bump: u8,
}
```

PDA seeds: `["access_credential", buyer.key, listing.key]`

This is the on-chain rental credential referenced throughout the rest of the docs. The field is still named `buyer` in the account struct; conceptually it's the renter's wallet.

### PerQueryBalance

```rust
pub struct PerQueryBalance {
    pub buyer: Pubkey,
    pub listing_id: Pubkey,
    pub balance_lamports: u64,
    pub total_queries: u64,
    pub is_revoked: bool,
    pub bump: u8,
}
```

PDA seeds: `["per_query_balance", buyer.key, listing.key]`

`total_queries` counts the lifetime number of tasks run against this balance account.

***

## Escrow model

Each listing has a dedicated escrow PDA (`["escrow", listing.key]`) that acts as a program-controlled SOL vault. The program is the only signer that can move funds out of escrow. On every rental, the protocol fee goes directly to `fee_recipient` and the remainder goes to escrow. Creators withdraw from escrow at any time using `withdraw_earnings`. All movements are verifiable on-chain via the Solana block explorer.

***

## Error codes

| Code                       | Value | Description                                                  |
| -------------------------- | ----- | ------------------------------------------------------------ |
| `ListingNotActive`         | 6000  | The listing is paused or unpublished                         |
| `CredentialNotFound`       | 6001  | No credential PDA exists for this renter and listing         |
| `CredentialExpired`        | 6002  | The subscription credential has passed its expiry timestamp  |
| `InsufficientBalance`      | 6003  | The per-task balance is too low to cover one task            |
| `Unauthorized`             | 6004  | The signer is not authorized to perform this action          |
| `InvalidAccessType`        | 6005  | The operation is not valid for the listing's rental model    |
| `CredentialRevoked`        | 6006  | The credential has been revoked by the creator               |
| `WithdrawalExceedsBalance` | 6007  | Requested withdrawal exceeds available escrow balance        |
| `FeeTooHigh`               | 6008  | Fee basis points exceed the 10% maximum                      |
| `PriceTooLow`              | 6009  | Listing price is below the minimum (1,000 lamports)          |
| `MetadataUriTooLong`       | 6010  | Metadata URI exceeds 200 characters                          |
| `InvalidDuration`          | 6011  | Subscription duration must be greater than zero              |
| `DepositTooLow`            | 6012  | Initial deposit must cover at least one task                 |
| `ProtocolPaused`           | 6013  | The protocol is currently paused                             |
| `ArithmeticOverflow`       | 6014  | Arithmetic overflow during fee or balance calculation        |
| `EscrowNotEmpty`           | 6015  | Cannot close a listing while escrow holds unclaimed earnings |
| `NothingToWithdraw`        | 6016  | No earnings available to withdraw                            |

***

## Events

The program emits an event on every state-changing instruction, which off-chain indexers use to power the marketplace UI and creator analytics without re-scanning full account state.

| Event                 | Emitted by                                                         |
| --------------------- | ------------------------------------------------------------------ |
| `ListingCreated`      | `create_listing`                                                   |
| `ListingUpdated`      | `update_listing`                                                   |
| `ListingClosed`       | `close_listing`                                                    |
| `AccessPurchased`     | `purchase_one_time`, `purchase_subscription`, `purchase_per_query` |
| `SubscriptionRenewed` | `renew_subscription`                                               |
| `BalanceToppedUp`     | `topup_balance`                                                    |
| `QueryConsumed`       | `consume_query`                                                    |
| `EarningsWithdrawn`   | `withdraw_earnings`                                                |
| `AccessRevoked`       | `revoke_access`, `revoke_per_query_access`                         |
| `ProtocolUpdated`     | `update_protocol`                                                  |


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.useagentex.com/protocol/on-chain-program.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
