# Contract reference

> Every method of the Probarion vault and factory contracts on NEAR: kind, caller, deposit, arguments, return value, events and error codes.

Every method of the vault and factory contracts: who may call it, the deposit it needs, its arguments and return value, the events it emits and the codes it fails with.

Status: Live · Network: Testnet · Reviewed: 2026-11-11 · Built from: 4ab41ca1436589c22073fa842ee46f970415e3d5

Canonical page: https://probarion.xyz/docs/build/contract-reference

The vault and the factory are the two contracts Probarion ships, written in Rust with near-sdk. Every vault runs a recorded build of the code in `contracts/vault/src`, and the factory in `contracts/factory/src/lib.rs` creates vaults from a version of that code. A test reads both sources and fails when a method is added or removed, or when its kind, deposit rule, arguments, return type, events or error codes change without its row, so these tables follow the code in the repository.

## How to read the tables

- **Kind**: a `view` takes `&self`. It reads state, changes nothing and runs as a free RPC query, with no transaction and no key. A `call` takes `&mut self` and runs in a signed transaction. An `init` runs once, when the contract is deployed. A `private callback` is the second half of a cross-contract call, and only the contract itself may call it: near-sdk answers any other caller with `Method <name> is private`.
- **Who may call**: the vault checks the caller before the deposit, so a stranger who attaches nothing gets [`NotOwner`](https://probarion.xyz/docs/build/errors.md#NotOwner) or `NotApprover`, not `RequiresOneYocto`. [Vaults, roles and keys](https://probarion.xyz/docs/concepts/vaults-roles-and-keys.md) explains each role and each key.
- **Deposit**: "Exactly 1 yoctoNEAR" means any other amount, none included, fails with [`RequiresOneYocto`](https://probarion.xyz/docs/build/errors.md#RequiresOneYocto). Only a full-access key can attach a deposit, which is [why most owner calls need one](https://probarion.xyz/docs/concepts/vaults-roles-and-keys.md#why-most-owner-calls-need-exactly-1-yoctonear). A call whose deposit reads None is not `#[payable]`, and near-sdk refuses an attached deposit on it with `Method <name> doesn't accept deposit`. A view has no such guard.
- **Arguments** are one JSON object with the names shown. A `U128` or a `U64` travels as a decimal string, such as `"6000000"`, and amounts are in the token's base units. A public key is a string such as `ed25519:...`. A paged view takes `from_index`, a position counted from 0, and `limit`, and returns at most 100 items. Mandate, spend, anchor and version ids count from 1.
- **Returns**: a `Promise` or a `PromiseOrValue` resolves when its receipts finish, and the transaction's result is the value of the last one, such as the callback's `SpendResult`. Null is JSON `null`.
- **Events emitted** names the NEP-297 events a call logs as `EVENT_JSON:` lines, the vault's under the standard `probarion` and the factory's under `probarion_factory`. An event that a private callback logs, such as a payout's last event, comes in a later receipt of the same transaction, and the cell names that callback. A callback that finds the call before it failed can log another event, or none. [Events](https://probarion.xyz/docs/build/events.md) lists each event's fields.
- **Errors** lists the `PROBARION:<Code>` panics a caller can meet, in the order the method checks them. Each code has its own anchor on [Errors](https://probarion.xyz/docs/build/errors.md), such as [`/docs/build/errors#NotAgent`](https://probarion.xyz/docs/build/errors.md#NotAgent). A panic reverts every change the call made. The factory panics with the vault's codes. A few defensive checks cannot fail once the method's own checks have passed, such as those inside a payout and its callbacks, and the tables leave them out.

## Vault methods

Each row is anchored at `vault-<method>`, such as [`vault-spend`](#vault-spend). The rows run in this order: config, owner controls, mandates, keys, spends, holds and anchors.

| Method | Kind | Who may call | Deposit | Arguments | Returns | Events emitted | Errors |
| - | - | - | - | - | - | - | - |
| `new` | init | Once, at deploy. The caller is stored as `factory` unless it is the vault account itself | None | `owner: AccountId`, `approvers: Vec<AccountId>`, `usdc: AccountId`, `intents: AccountId`, `allowed_assets: Vec<AccountId>`, `daily_backstop: U128`, `held_ttl_ns: U64` | None. It stores the vault unfrozen, with no agents, no ledger key and no mandates | None | `InvalidInput`: no approvers or more than 10, more than 10 allowed assets, a zero `daily_backstop`, or a `held_ttl_ns` of 0 or over 30 days |
| `get_config` | view | Anyone | None | None | `VaultConfig`: `owner`, `approvers`, `usdc`, `intents`, `allowed_assets`, `daily_backstop`, `held_ttl_ns`, `frozen`, `factory`, `version`, `ledger_key` | None | None |
| `freeze` | call | The owner, from a full-access key or a function-call key that may call the vault | None | None | None. New spends and approvals are refused until `unfreeze` | `frozen` | `NotOwner` |
| `unfreeze` | call | The owner, from a full-access key | Exactly 1 yoctoNEAR | None | None | `unfrozen` | `NotOwner`, `RequiresOneYocto` |
| `withdraw` | call | The owner, from a full-access key | Exactly 1 yoctoNEAR | `amount: U128`, in USDC base units | `Promise`: the USDC `ft_transfer` to the owner. A failed transfer fails the transaction | `withdrawn` | `NotOwner`, `RequiresOneYocto`, `InvalidInput` for a zero amount |
| `withdraw_intents` | call | The owner, from a full-access key | Exactly 1 yoctoNEAR | `token: AccountId`, `amount: U128` | `Promise`: the NEAR Intents `ft_withdraw` of `amount` of `token` from the vault's balance there to the owner's NEAR account | `withdrawn` | `NotOwner`, `RequiresOneYocto`, `InvalidInput` for a zero amount |
| `create_mandate` | call | The owner, from a full-access key | The mandate's storage, plus 0.00125 NEAR for a NEAR payee's USDC registration. The excess goes back to the caller | `input: MandateInput`, with `payee`, `purpose_hash`, `max_per_payment`, `max_total`, `max_uses`, `expires_at_ns`, `approval_above`, `agent_pk`, `label` | `u64`: the new mandate's id, counting from 1 | `mandate_created` | `NotOwner`, `InvalidInput`, `AssetNotAllowed`, `InsufficientDeposit` |
| `revoke_mandate` | call | The owner, from a full-access key | Exactly 1 yoctoNEAR | `mandate_id: u64` | None. Payouts already sent keep their reservation until their callback | `mandate_revoked` | `NotOwner`, `RequiresOneYocto`, `NoMandate`, `MandateInactive` when it is already revoked |
| `get_mandate` | view | Anyone | None | `mandate_id: u64` | `Option<MandateView>`: the input's fields plus `id`, `spent`, `reserved`, `uses`, `status` and `created_at_ns`, or null | None | None |
| `list_mandates` | view | Anyone | None | `from_index: u64`, `limit: u64` | `Vec<MandateView>`: up to 100 mandates, from position `from_index` | None | None |
| `add_agent` | call | The owner, from a full-access key | Exactly 1 yoctoNEAR | `public_key: PublicKey`, `allowance: U128`, `label: String` | None. The vault adds the key to itself, allowed to call only `spend`, with `allowance` yoctoNEAR of gas | `agent_added` | `NotOwner`, `RequiresOneYocto`, `InvalidInput`: a zero allowance, a label over 64 bytes, 16 agents already, or a key that is already an agent key or the ledger key |
| `remove_agent` | call | The owner, from a full-access key | Exactly 1 yoctoNEAR | `public_key: PublicKey` | None. The vault deletes the key from its account | `agent_removed` | `NotOwner`, `RequiresOneYocto`, `InvalidInput` when the key is not an agent key |
| `set_ledger_key` | call | The owner, from a full-access key | Exactly 1 yoctoNEAR | `public_key: PublicKey`, `allowance: U128` | None. The vault deletes the previous ledger key, then adds this one, allowed to call only `anchor` | `ledger_key_set` | `NotOwner`, `RequiresOneYocto`, `InvalidInput`: a zero allowance, or a key that is already an agent key or the ledger key |
| `list_agents` | view | Anyone | None | None | `Vec<AgentView>`: `public_key`, `label` and `added_at_ns` of every agent key | None | None |
| `check_spend` | view | Anyone. It checks the request as if `signer_pk` had signed it | None | `request: SpendRequest`, `signer_pk: PublicKey` | `Verdict`: `ok`, `code` and `would_hold` | None | None. A refusal comes back as the bare code in `code` |
| `spend` | call | An agent key: the vault account signs with a key `add_agent` added | None | `request: SpendRequest`, with `mandate_id`, `amount`, `invoice_hash`, `ref`, `decision_hash` | `PromiseOrValue<SpendResult>`: `held` at once, or the payout, which resolves to `paid`, `burned` or `failed` | `spend_held`, or `spend_submitted` and then, from `on_transfer` or `on_withdraw`, `spend_paid`, `spend_burned` or `spend_failed` | `NotAgent` when the call does not come from the vault itself, then the codes of the 14 spend checks |
| `on_transfer` | private callback | The vault itself, after its USDC `ft_transfer` to a NEAR payee | None | `spend_id: u64` | `SpendResult`: `failed` when the transfer failed, else `paid` | `spend_paid` or `spend_failed` | None |
| `on_withdraw` | private callback | The vault itself, after its NEAR Intents `ft_withdraw` to a Base or Solana payee | None | `spend_id: u64` | `SpendResult`: `failed` when the `ft_withdraw` failed or returned `"0"`, else `burned`, whatever else it returned | `spend_burned` or `spend_failed` | None |
| `get_spend` | view | Anyone | None | `spend_id: u64` | `Option<SpendView>`: the request's fields plus `id`, `agent_pk`, `destination`, `status`, `created_at_ns`, `held_until_ns` and `memo`, or null | None | None |
| `list_spends` | view | Anyone | None | `from_index: u64`, `limit: u64` | `Vec<SpendView>`: up to 100 spends, from position `from_index` | None | None |
| `approve` | call | An approver, from a full-access key | Exactly 1 yoctoNEAR | `spend_id: u64` | `PromiseOrValue<SpendResult>`: always the payout, which resolves to `paid`, `burned` or `failed` | `spend_approved`, `spend_submitted`, and then, from `on_transfer` or `on_withdraw`, `spend_paid`, `spend_burned` or `spend_failed` | `NotApprover`, `RequiresOneYocto`, `Frozen`, `NotHeld`, `HoldExpired`, `MandateInactive` |
| `reject` | call | An approver, from a full-access key | Exactly 1 yoctoNEAR | `spend_id: u64` | None. It releases the reservation, the use and the invoice hash. The ref stays used | `spend_rejected` | `NotApprover`, `RequiresOneYocto`, `NotHeld` |
| `expire_held` | call | Anyone, once the spend's `held_until_ns` has passed | None | `spend_id: u64` | None. It releases the spend as `reject` does, and the spend then reads `expired` | `spend_expired` | `NotHeld`, `HoldNotExpired` |
| `anchor` | call | The ledger key: the vault account signs with the key `set_ledger_key` added | None | `seq: u64`, `root: String`, `count: u64` | None. It stores the anchor with its signing key and the block time | `ledger_anchored` | `NotLedgerKey`, `BadSeq` unless `seq` is one above the last, `InvalidInput`: a `root` that is not 64 lowercase hex characters, or a `count` of 0 or below the last one |
| `list_anchors` | view | Anyone | None | `from_index: u64`, `limit: u64` | `Vec<AnchorView>`: `seq`, `root`, `count`, `at_ns` and `ledger_key` of up to 100 anchors | None | None |

The 14 checks behind `spend` and `check_spend` are listed in the order they run on [How a payment is checked](https://probarion.xyz/docs/concepts/payment-checks.md#the-14-checks-in-order), and [Holds and approvals](https://probarion.xyz/docs/concepts/holds-and-approvals.md) follows a held spend to its end.

`spend` and `anchor` are signed with the vault's own function-call keys, the agent key and the ledger key. Neither key may call any other method or attach a deposit.

> **Owner signs**
>
> The owner signs `create_mandate`, `revoke_mandate`, `add_agent`, `remove_agent`, `set_ledger_key`, `freeze`, `unfreeze`, `withdraw` and `withdraw_intents` from their own wallet. Each one needs a full-access key except `freeze`, which takes no deposit so that the owner can stop the vault fast.
>
> _The owner signs this transaction. Probarion never signs it._

> **Approver signs**
>
> An approver signs `approve` or `reject` from their own wallet, with exactly 1 yoctoNEAR attached. One approver is enough, and the owner's signature counts only when the owner is on the approver list.
>
> _An approver signs this transaction. Probarion never signs it._

## Factory methods

The factory creates each vault as `<name>.<factory>`, such as `acme.vaults.probarion.testnet`, with no access keys, from its current version. No factory method calls a vault after `create_vault`, so the factory cannot change, upgrade or take funds from a vault it created. Each row is anchored at `factory-<method>`, such as [`factory-create_vault`](#factory-create_vault).

| Method | Kind | Who may call | Deposit | Arguments | Returns | Events emitted | Errors |
| - | - | - | - | - | - | - | - |
| `new` | init | Once, at the factory's deploy | None | `owner: AccountId`, `usdc: AccountId`, `intents: AccountId`, `allowed_assets: Vec<AccountId>`, `min_deposit: U128` | None. It stores the factory with no versions and no vaults | None | `InvalidInput` for more than 10 allowed assets |
| `get_config` | view | Anyone | None | None | `FactoryConfig`: `owner`, `usdc`, `intents`, `allowed_assets`, `min_deposit`, and `current_version`, null before the first version | None | None |
| `create_vault` | call | Anyone. The caller becomes the new vault's owner | At least `min_deposit`, plus the code's storage for an embedded version. All of it goes to the new vault | `name: String`, `approvers: Vec<AccountId>`, `daily_backstop: U128`, `held_ttl_ns: U64` | `Promise`: it resolves to the new vault's account id, `<name>.<factory>`, or to null when the batch failed, such as for a taken name | `vault_created`, from `on_vault_created`, only when the batch succeeded | `InvalidInput`: a `name` outside `^[a-z0-9][a-z0-9_-]{1,30}$` or not a valid account id, init values the vault's `new` refuses, or no version yet. Then `InsufficientDeposit` |
| `on_vault_created` | private callback | The factory itself, after the `create_vault` batch | None | `name: String`, `owner: AccountId`, `deposit: U128`, `version: u32` | `Option<AccountId>`: the vault's account id, or null after a failed batch, when it sends `deposit` back to `owner` | `vault_created` | None |
| `list_vaults` | view | Anyone | None | `from_index: u64`, `limit: u64` | `Vec<VaultRecord>`: `account_id`, `owner`, `version` and `created_at_ns` of up to 100 vaults | None | None |
| `vault_version` | view | Anyone | None | `account_id: AccountId` | `Option<u32>`: the version the factory created that vault from, or null for an account it did not create | None | None |
| `versions` | view | Anyone | None | None | `Vec<VersionView>`: `index`, `code_hash`, `mode`, `notes` and `at_ns` of every version. The last one is current | None | None |
| `store_code` | call | The factory owner, from a full-access key | Exactly 1 yoctoNEAR | None. The call's raw input is the wasm | `String`: the code's base58 sha256, which is its NEAR code hash | None | `NotOwner`, `RequiresOneYocto`, `InvalidInput` for an empty input |
| `add_version` | call | The factory owner, from a full-access key | Exactly 1 yoctoNEAR | `code_hash: String`, `mode: String`, `notes: String` | `u32`: the new version's index, counting from 1. It becomes the current version | `version_added` | `NotOwner`, `RequiresOneYocto`, `InvalidInput`: notes over 256 bytes, a hash that is not 32 bytes in base58, a `mode` other than `global` or `embedded`, or embedded code that was never stored |
| `publish_global` | call | The factory owner, from a full-access key | Exactly 1 yoctoNEAR | None | `Promise`: it deploys the current version's stored wasm as a global contract, paid from the factory's balance | None | `NotOwner`, `RequiresOneYocto`, `InvalidInput` unless the current version is embedded and its code is stored |

> **Owner signs**
>
> The account that calls `create_vault` becomes the new vault's owner and pays its deposit from its own wallet. The approvers are named in the same call, and no vault method changes them later.
>
> _The owner signs this transaction. Probarion never signs it._

A version's mode trades each vault's deposit against the factory's own cost. An `embedded` version gives every new vault its own copy of the wasm, so every vault pays for the storage of that copy. A `global` version points every new vault at one global contract by its code hash, so the factory pays once, when the code is published. A version's life runs `store_code`, then `add_version` in `embedded` mode, and, to go global, `publish_global` and an `add_version` in `global` mode with the same hash. A version applies only to the vaults created after it: a vault keeps the code it was created with.

## Read a contract

The views need no account and no key. With near-cli-rs:

```bash
near contract call-function as-read-only vaults.probarion.testnet get_config json-args '{}' network-config testnet now
near contract call-function as-read-only vaults.probarion.testnet versions json-args '{}' network-config testnet now
near contract call-function as-read-only v0.probarion.testnet list_mandates json-args '{"from_index":0,"limit":100}' network-config testnet now
```

## Deployed on testnet

`contracts/deployments/testnet.json` records these accounts:

- `vaults.probarion.testnet` is the factory. Its owner is `probarion-owner.testnet`, its `min_deposit` is 0.5 NEAR, and it creates vaults named `<name>.vaults.probarion.testnet`. The record lists only embedded versions: there is no global version on testnet.
- `v0.probarion.testnet` is the first vault. Its deploy script ran `new` as the vault account itself, so its `get_config` shows `factory` as null.
- `usdc.probarion.testnet` is the test USDC token. `base-usdc.probarion.testnet` and `sol-usdc.probarion.testnet` stand in for the Base and Solana assets inside `intents-stub.probarion.testnet`, the stand-in for NEAR Intents. All of them are subaccounts of `probarion.testnet`, and [Testnet and mainnet](https://probarion.xyz/docs/start/testnet-and-mainnet.md) explains the stand-ins.

No method returns the code hash of its own account: NEAR reports that with the account. To see which code a factory vault was made from, read its version with `vault_version`, then that version's `code_hash` in `versions`. [Reproducible builds](https://probarion.xyz/docs/trust/reproducible-builds.md) lists the commit and the code hash of each recorded build of the vault and the factory, from `contracts/code-hashes.json`, and [check f](https://probarion.xyz/docs/trust/verifier-checks.md#check-f) of the verifier compares a vault's code hash with the recorded vault builds and, for a vault whose `factory` is its network's factory, with that factory's versions. `v0.probarion.testnet` and the vaults made from the factory's version 1 run two different recorded builds, while the tables on this page describe the source in the repository.

On mainnet, `contracts/deployments/mainnet.json` records `v0.probarion.near`, deployed and locked on 2026-11-11, with code hash `3cQXYDvfGLRMveCVAHmh1exiuZKDXWqD8cUhqdEs9MF1`. Its owner is `probarion.near`. Its deploy ran `new` as the vault account itself, so its `get_config` shows `factory` as null. The record also names `vaults.probarion.near`, the mainnet factory, and records no deploy of it: the factory is not deployed.

## What the tables leave out

- Gas and the contracts' limits: [Limits and costs](https://probarion.xyz/docs/build/limits-and-costs.md).
- Each event's fields: [Events](https://probarion.xyz/docs/build/events.md).
- What each code means and what to do next: [Errors](https://probarion.xyz/docs/build/errors.md).
- The CLI commands that print these calls for a wallet to sign: [CLI reference](https://probarion.xyz/docs/build/cli-reference.md).
- The workspace's other contracts, `test-ft` and `intents-stub`: testnet stand-ins for the USDC token and for NEAR Intents, deployed as the accounts above. [Testnet and mainnet](https://probarion.xyz/docs/start/testnet-and-mainnet.md) explains them.
- The calls the vault makes to other contracts: `ft_transfer` and `storage_deposit` on the USDC token, and `ft_withdraw` on NEAR Intents. Those contracts define them.
- The JSON shape of a mandate and of a spend request: [Mandates](https://probarion.xyz/docs/concepts/mandates.md) shows every mandate field, and [How a payment is checked](https://probarion.xyz/docs/concepts/payment-checks.md#what-the-agent-can-ask-for) shows what an agent can ask for.

## Related pages

- [Vaults, roles and keys](https://probarion.xyz/docs/concepts/vaults-roles-and-keys.md): who holds which key, and why `freeze` is the one owner call with no deposit.
- [Holds and approvals](https://probarion.xyz/docs/concepts/holds-and-approvals.md): `approve`, `reject` and `expire_held` in use.
- [The ledger and anchors](https://probarion.xyz/docs/concepts/ledger-and-anchors.md): what `anchor` pins on chain.
- [Verify a vault yourself](https://probarion.xyz/docs/trust/verify-a-vault.md): the verifier reads these views.
- The sources: [`contracts/vault/src`](https://github.com/probarion/probarion/tree/main/contracts/vault/src), [`contracts/factory/src/lib.rs`](https://github.com/probarion/probarion/blob/main/contracts/factory/src/lib.rs) and [`contracts/deployments/testnet.json`](https://github.com/probarion/probarion/blob/main/contracts/deployments/testnet.json).
