Yamale docs ← back to the site

x/treasury

Programmable custody: shared funds with roles, spending policies, time locks and vesting schedules, where committed funds cannot be spent by anyone.

Transactions

MsgAssignRole

/blockchain.treasury.v1.MsgAssignRole

Signed by the admin field.

AssignRole grants an address a role over a treasury.

Field Type Description
admin string
treasury_id uint64
address string
role Role

MsgClaimLock

/blockchain.treasury.v1.MsgClaimLock

Signed by the beneficiary field.

ClaimLock releases whatever has vested to the beneficiary.

Field Type Description
beneficiary string
lock_id uint64

MsgCreateLock

/blockchain.treasury.v1.MsgCreateLock

Signed by the admin field.

CreateLock commits treasury funds to a beneficiary on a schedule.

Field Type Description
admin string
treasury_id uint64
beneficiary string
denom string
amount string
lock_type LockType
start_time int64
cliff_time int64
end_time int64
release_intervals uint64
revocable bool

MsgCreateTreasury

/blockchain.treasury.v1.MsgCreateTreasury

Signed by the creator field.

CreateTreasury opens a new treasury.

Field Type Description
creator string
name string
admin string admin defaults to the creator when empty.

MsgDeposit

/blockchain.treasury.v1.MsgDeposit

Signed by the depositor field.

Deposit moves funds from an account into a treasury.

Field Type Description
depositor string
treasury_id uint64
amount repeated Coin

MsgDisputeEscrow

/blockchain.treasury.v1.MsgDisputeEscrow

Signed by the party field.

MsgDisputeEscrow freezes a lock and refers it to the named moderator.

Either party may send it. That symmetry is what removes the need for a deadline: a seller facing a buyer who has gone quiet escalates rather than waiting forever, and a buyer who received nothing does the same.

Field Type Description
party string
lock_id uint64
reason string

MsgOpenEscrow

/blockchain.treasury.v1.MsgOpenEscrow

Signed by the depositor field.

--- conditional locks (escrow) ----------------------------------------

Field Type Description
depositor string
beneficiary string
moderator string Named at creation, never afterwards.
amount Coin
memo string What is being bought, so a moderator reading the case later knows.
amount_commitment bytes 6-8 mirror MsgSpend, and matter more here than there. An escrow is two private parties and a consumer purchase: the amount is nobody else's business, and "what is being bought" is a description of a person's life written to a public ledger that cannot forget it. Unpopulated for now; numbered now because escrow is the first thing on this chain that a member of the public will use with their own money.
amount_range_proof bytes
metadata_hash bytes

MsgReleaseEscrow

/blockchain.treasury.v1.MsgReleaseEscrow

Signed by the depositor field.

MsgReleaseEscrow pays the beneficiary. Only the depositor may send it.

The buyer confirming is the whole condition. Nobody else can confirm on their behalf — not the seller, not the moderator, not the treasury admin.

Field Type Description
depositor string
lock_id uint64

MsgResolveEscrow

/blockchain.treasury.v1.MsgResolveEscrow

Signed by the moderator field.

MsgResolveEscrow is the moderator deciding an open case.

Their only power, and only on a lock somebody actually disputed. A moderator who could act on a quiet lock would be a custodian with a different name.

Field Type Description
moderator string
lock_id uint64
pay_beneficiary bool true pays the beneficiary, false returns the money to the depositor. There is no third option: a case that stays open forever is the failure this whole mechanism exists to prevent.

MsgRevokeLock

/blockchain.treasury.v1.MsgRevokeLock

Signed by the admin field.

RevokeLock cancels a revocable lock, returning the unreleased portion.

Field Type Description
admin string
lock_id uint64

MsgRevokeRole

/blockchain.treasury.v1.MsgRevokeRole

Signed by the admin field.

RevokeRole removes an address's role.

Field Type Description
admin string
treasury_id uint64
address string

MsgSetAdmin

/blockchain.treasury.v1.MsgSetAdmin

Signed by the admin field.

SetAdmin transfers administrative control of a treasury.

Field Type Description
admin string
treasury_id uint64
new_admin string

MsgSetPaused

/blockchain.treasury.v1.MsgSetPaused

Signed by the sender field.

SetPaused freezes or unfreezes a treasury.

Field Type Description
sender string
treasury_id uint64
paused bool

MsgSetSpendPolicy

/blockchain.treasury.v1.MsgSetSpendPolicy

Signed by the admin field.

SetSpendPolicy sets the spending constraints for one denom.

Field Type Description
admin string
policy SpendPolicy

MsgSpend

/blockchain.treasury.v1.MsgSpend

Signed by the spender field.

Spend moves funds out of a treasury to a recipient.

Field Type Description
spender string
treasury_id uint64
recipient string
amount repeated Coin
memo string memo records why the payment was made, for the audit trail.
amount_commitment bytes 6-8 are reserved for confidentiality, unpopulated and unverified. See docs/scope/confidentiality.md and MsgSendPayment, which these mirror. A treasury spend is public by default and that is deliberate: for donor disbursement, subsidy and public payroll the auditability is the product. The numbers are taken anyway, because the exception the design already anticipates — commercial escrow — is a spend like any other, and by the time it is wanted the treasuries holding real money will have written spends that cannot be re-encoded. memo is the field with a problem today rather than later: it is free text on a value transfer, which is where a payroll run acquires an employee's name, and it goes to a ledger with no erasure path. metadata_hash is where that moves.
amount_range_proof bytes
metadata_hash bytes

MsgUpdateParams

/blockchain.treasury.v1.MsgUpdateParams

Signed by the authority field.

UpdateParams defines a (governance) operation for updating the module parameters. The authority defaults to the x/gov module account.

Field Type Description
authority string authority is the address that controls the module (defaults to x/gov unless overwritten).
params Params NOTE: All parameters must be supplied.

Queries

ClaimableAmount

GET /yamale/blockchain/treasury/v1/lock/{lock_id}/claimable

ClaimableAmount reports what a lock would release if claimed right now.

Request:

Field Type Description
lock_id uint64

Response:

Field Type Description
claimable string claimable is releasable right now; vested is the cumulative amount the schedule has unlocked so far, including what was already claimed.
vested string
remaining string

GetLock

GET /yamale/blockchain/treasury/v1/lock/{id}

GetLock queries one lock by id.

Request:

Field Type Description
id uint64

Response:

Field Type Description
lock Lock

GetSpendPolicy

GET /yamale/blockchain/treasury/v1/treasury/{treasury_id}/policy

GetSpendPolicy queries the spending policy for one treasury and denom.

Request:

Field Type Description
treasury_id uint64
denom string

Response:

Field Type Description
policy SpendPolicy

GetTreasury

GET /yamale/blockchain/treasury/v1/treasury/{id}

GetTreasury queries one treasury by id.

Request:

Field Type Description
id uint64

Response:

Field Type Description
treasury Treasury

ListLock

GET /yamale/blockchain/treasury/v1/lock

ListLock queries all locks.

Request:

Field Type Description
pagination PageRequest

Response:

Field Type Description
lock repeated Lock
pagination PageResponse

ListRole

GET /yamale/blockchain/treasury/v1/treasury/{treasury_id}/roles

ListRole queries the role assignments of one treasury.

Request:

Field Type Description
treasury_id uint64
pagination PageRequest

Response:

Field Type Description
role repeated RoleAssignment
pagination PageResponse

ListTreasury

GET /yamale/blockchain/treasury/v1/treasury

ListTreasury queries all treasuries.

Request:

Field Type Description
pagination PageRequest

Response:

Field Type Description
treasury repeated Treasury
pagination PageResponse

LocksByBeneficiary

GET /yamale/blockchain/treasury/v1/beneficiary/{beneficiary}/locks

LocksByBeneficiary queries the locks payable to one address, so a beneficiary can find what is owed to them without knowing any treasury id.

Request:

Field Type Description
beneficiary string
pagination PageRequest

Response:

Field Type Description
lock repeated Lock
pagination PageResponse

LocksByTreasury

GET /yamale/blockchain/treasury/v1/treasury/{treasury_id}/locks

LocksByTreasury queries the locks held by one treasury.

Request:

Field Type Description
treasury_id uint64
pagination PageRequest

Response:

Field Type Description
lock repeated Lock
pagination PageResponse

Params

GET /yamale/blockchain/treasury/v1/params

Params queries the parameters of the module.

Response:

Field Type Description
params Params params holds all the parameters of this module.

SpendCapacity

GET /yamale/blockchain/treasury/v1/treasury/{treasury_id}/capacity

SpendCapacity reports how much may still be spent in the current period.

Request:

Field Type Description
treasury_id uint64
denom string

Response:

Field Type Description
remaining_this_period string remaining_this_period is what the period limit still allows; available is what the treasury actually holds unlocked. A spend is bounded by whichever is smaller, which is why both are reported.
available string
per_transaction_limit string
period_resets_at int64

TreasuryBalances

GET /yamale/blockchain/treasury/v1/treasury/{treasury_id}/balances

TreasuryBalances reports total, locked and available amounts per denom.

Request:

Field Type Description
treasury_id uint64

Response:

Field Type Description
balances repeated DenomBalance

State

DenomBalance

DenomBalance is one denom's position in a treasury.

Field Type Description
denom string
total string
locked string
available string available is total minus locked: what may actually be spent.

Lock

Lock commits treasury funds to a beneficiary on a schedule.

Creating a lock does not transfer anything; it moves funds from the treasury's available balance into its locked balance. The beneficiary pulls what has vested by claiming, so the treasury never needs to run a scheduler and a beneficiary who never claims costs the chain nothing.

Field Type Description
id uint64
treasury_id uint64
beneficiary string beneficiary is the only address that may claim from this lock.
denom string
total_amount string total_amount is the full commitment; released_amount is how much of it the beneficiary has already claimed. The difference is what remains locked.
released_amount string
start_time int64 start_time is when the schedule begins, in Unix seconds.
cliff_time int64 cliff_time is the earliest moment anything may be claimed. Before it, a vesting lock releases nothing at all — that is the whole point of a cliff. Set equal to start_time for no cliff.
end_time int64 end_time is when the lock is fully released.
release_intervals uint64 release_intervals splits vesting into discrete tranches rather than a continuous drip: 4 over a year means quarterly. Zero or one means continuous, releasing proportionally to elapsed time.
lock_type LockType
revocable bool revocable lets the admin cancel the lock and return the unreleased portion to available. Already-vested funds are never clawed back, whether or not the beneficiary has claimed them — a promise that can be withdrawn retroactively is not a promise.
active bool active is false once the lock is fully claimed or revoked. Inactive locks are retained rather than deleted so the disbursement history stays auditable.
created_at_height uint64
depositor string depositor funded the lock and is the only account that may release it. Held separately from the treasury's admin on purpose: in an escrow the buyer is not an administrator of anything, and an admin who could release somebody else's escrow would be the trusted party this design exists to remove.
moderator string moderator decides a disputed lock, and is named when the lock is created rather than chosen afterwards. Both parties see who will judge them before they commit money, which is the whole of what makes this honest. A moderator has exactly one power: deciding an open case. They cannot release, refund, or touch a lock nobody has disputed.
dispute DisputeState
dispute_reason string Why the case was opened, kept because a moderator judging between two strangers has nothing else to go on.
dispute_opened_by string Which side escalated. "The seller cannot get confirmed" and "the buyer says it never came" are different disputes with the same shape.

RoleAssignment

RoleAssignment grants an address a role over one treasury.

Field Type Description
treasury_id uint64
address string
role Role

SpendPolicy

SpendPolicy constrains what a ROLE_SPENDER may move out of a treasury in one denom, without needing an admin decision per payment.

The policy is what makes a spender role safe to hand out: it bounds the blast radius of a compromised operational key to one period's limit, rather than the whole treasury.

Field Type Description
treasury_id uint64
denom string
per_transaction_limit string per_transaction_limit caps a single spend. Empty means no cap.
period_limit string period_limit caps the total spent within one window, and period_seconds is the window length. Empty period_limit means no cap. The window is fixed, not rolling: it resets when period_seconds elapses since the window opened, which keeps the accounting to a single stored value per denom.
period_seconds uint64
allowlist repeated string allowlist restricts destinations. Empty means any destination is allowed; non-empty means only these are.
blocklist repeated string blocklist forbids destinations outright. It is checked after the allowlist, so an address in both is denied — the safer reading of a contradiction.

SpendWindow

SpendWindow tracks consumption of a SpendPolicy's period_limit.

Field Type Description
treasury_id uint64
denom string
spent string spent is the amount already used in the current window.
window_start int64 window_start is when the current window opened, in Unix seconds.

Treasury

Treasury is a pool of funds under programmable control.

Funds deposited into a treasury are held by the treasury module account, not by the admin's own address. That indirection is what makes the locked/available split enforceable: because no ordinary bank transfer can reach the balance, the only way out is a treasury message, and every treasury message checks the available balance first.

Field Type Description
id uint64
name string name is a human label, for display only.
admin string admin manages roles, policies and locks. Point this at an x/group policy address to get M-of-N control with a full on-chain approval trail; a plain account address gives single-key control.
paused bool paused halts spending and claiming without unwinding any state, so a compromise can be contained while the admins decide what to do.
created_at_height uint64

TreasuryBalance

TreasuryBalance is the module's ledger of what one treasury holds in one denom, and how much of it is already committed to locks.

available = total - locked, and it is available that every outbound path checks. Once funds back a lock they cannot be spent by anyone, including the admin and including a proposal that clears its signing threshold.

Field Type Description
treasury_id uint64
denom string
total string
locked string

Value types

DisputeState

DisputeState tracks a conditional lock that somebody has escalated.

Value Meaning
DISPUTE_STATE_NONE
DISPUTE_STATE_OPEN Frozen. Neither release nor refund happens until the moderator decides.
DISPUTE_STATE_RESOLVED

LockType

LockType selects how a lock releases its funds over time.

Value Meaning
LOCK_TYPE_UNSPECIFIED LOCK_TYPE_UNSPECIFIED is the unset default and is never valid.
LOCK_TYPE_TIME LOCK_TYPE_TIME releases the whole amount at once, when end_time passes. Use it for a simple escrow or a delayed disbursement.
LOCK_TYPE_VESTING LOCK_TYPE_VESTING releases progressively between cliff_time and end_time. Use it for grants, payroll and token allocations.
LOCK_TYPE_CONDITIONAL LOCK_TYPE_CONDITIONAL releases when the depositor says so, not when a clock says so. This is escrow: a buyer commits the money, the seller ships knowing it exists, and the funds move on confirmation. Deliberately no deadline. An automatic release rewards precisely the seller who ships nothing and waits, and it lands hardest on buyers who are ill, travelling or offline. Instead either party may open a case, which is why silence is not a weapon: the seller escalates rather than waiting forever.

Role

Role is what an address may do to a treasury.

Proposing, voting and executing are deliberately absent: on this chain that machinery belongs to x/group, which already records every approval and rejection as an attributable on-chain event. Setting a treasury's admin to a group policy composes the two — the group decides, the treasury enforces.

Value Meaning
ROLE_UNSPECIFIED ROLE_UNSPECIFIED is the unset default and grants nothing.
ROLE_ADMIN ROLE_ADMIN may manage roles and policies, create locks, and revoke revocable locks. Equivalent to the treasury's admin address.
ROLE_SPENDER ROLE_SPENDER may move funds out directly, without an admin decision, so long as the spend policy allows it. This is the role that makes routine operational payments possible without a governance round trip.
ROLE_PAUSER ROLE_PAUSER may pause and unpause the treasury. Held separately from admin so an emergency responder can freeze funds without also being able to move them.

Parameters

Changed by governance through MsgUpdateParams. Defaults are the values a chain starts with at genesis.

Parameter Default Description
max_locks_per_treasury 500 max_locks_per_treasury bounds how many active locks one treasury may hold. Releasing funds walks a treasury's locks, so an unbounded count would let anyone make that walk arbitrarily expensive.
max_role_assignments_per_treasury 100 max_role_assignments_per_treasury bounds the size of a treasury's access control list, for the same reason.
min_lock_seconds 60 min_lock_seconds is the shortest duration a lock may run for. A lock that ends the moment it is created commits nothing and only wastes state.
max_spend_policy_addresses 200 max_spend_policy_addresses bounds the combined size of a spend policy's allowlist and blocklist. Both are scanned on every spend and stored indefinitely, so leaving them unbounded would let a treasury make its own payments arbitrarily expensive to validate and bloat state while doing it.
fee_operating_treasury_id 0 fee_operating_treasury_id names the treasury that network fees are paid into on a deployment that has no native token. The destination is a treasury rather than an address because fees are operating income of the deployment, and income that lands in somebody's account is spendable by whoever holds that key. A treasury puts it behind the same roles, spend policies and M-of-N group control as every other committed balance, and leaves the audit trail those produce. Nothing reads this unless route_fees_to_operating_treasury is set.
route_fees_to_operating_treasury false route_fees_to_operating_treasury turns fee routing on. It is a separate field rather than a zero sentinel on the id above, because in proto3 a uint64 of 0 is indistinguishable from a field nobody set, and whether zero names a treasury is x/treasury's numbering convention rather than this parameter's. Ids start at one today, so zero would work; if that ever changed, every chain that had not configured routing would start paying its fees into whichever treasury was created first, and nothing here would have changed to say so.

Errors

Every way a transaction to this module can be rejected.

Code Name Message
1100 ErrInvalidSigner expected gov account as only signer for proposal message
1101 ErrTreasuryNotFound treasury not found
1102 ErrUnauthorized signer is not authorized to perform this action on this treasury
1103 ErrInvalidAmount invalid coin amount
1104 ErrInsufficientFunds treasury does not have enough available balance
1105 ErrLockNotFound lock not found
1106 ErrInvalidSchedule invalid lock schedule
1107 ErrLockInactive lock is no longer active
1108 ErrNotRevocable lock is not revocable
1109 ErrNothingToClaim nothing has vested yet
1110 ErrTreasuryPaused treasury is paused
1111 ErrSpendLimit spend exceeds the policy limit
1112 ErrDestinationDenied destination is not permitted by the spend policy
1113 ErrLimitReached treasury has reached a configured maximum
1114 ErrInvalidRole invalid role
1115 ErrSelfEscrow an escrow cannot pay its own depositor
1116 ErrModeratorIsParty the moderator cannot be the buyer or the seller
1117 ErrNotEscrow that lock is not an escrow
1118 ErrNotDepositor only the depositor may release an escrow
1119 ErrNotParty only the buyer or the seller may open a case
1120 ErrNotModerator only the named moderator may decide this case
1121 ErrEscrowDisputed this escrow is under review and cannot be released directly
1122 ErrAlreadyDisputed a case is already open on this escrow
1123 ErrNoOpenCase there is no open case on this escrow
1124 ErrNoReason a case must say what happened
1125 ErrLockClosed that lock is already settled