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 |
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. |
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/{denom}
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/{denom}
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 |
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
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. |
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. |
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 |