Sign a balance statement
How a custodial distributor signs the balance statement Yutai stores as its attestation: the two kinds of statement, what one payload carries, the exact message to sign, how fresh a statement must be and the signature Yutai verifies. The formats every value travels in are the identifiers page's: chains, instruments, quantities, instants and the prefixed ids.
Yutai does not take statements from real distributors yet. The statements it records run on simulated data, so nothing a real distributor sends reaches it; the rules on this page are the ones its stored statements already follow.
Kinds of statement
Yutai learns your users' tokenized holdings from statements your distributor signs and sends. Every statement is one kind. The kinds Yutai's registry names:
- Push
push - A statement your system sends as the account's positions change: it carries the current position of every instrument it names.
- Snapshot
snapshot - The account's full statement: it names every instrument the account holds, so anything a push missed is caught.
What one payload carries
One statement carries the facts it binds, under the bounds the contract fixes:
tenantId- The Yutai member the statement is about: the one whose holder the account belongs to. It is the member's own prefixed id, the identifiers page's form.
venueId- The Yutai member your distributor runs under: the one whose registered key verifies your signature.
accountRef- Your distributor's own reference for the account, the one its link to the holder carries. It carries 1 to 128 characters of letters, digits, dot, underscore, colon and hyphen; a statement whose reference carries anything else is refused.
payloadId- The statement's own id, yours to mint. It carries 1 to 128 characters of letters, digits, dot, underscore, colon and hyphen. One statement per id: a second delivery of the same id with the same signature answers the first statement and writes nothing, and an id sent again with other content is refused.
kind- One of the two kinds above.
attestedAt- The instant your system attested the statement: ISO 8601 in UTC, the identifiers page's instants.
positions- The current position of every instrument the statement names, one line each: the instrument's id, the one the instrument listing carries, and the exact base units you hold, as one whole number. At most 64 lines, each instrument once.
movements- The account's history, one line per movement. At most 256 lines; a longer history arrives across several statements. Each movement carries its own id (yours, once across your accounts), its kind (the four below), the instrument, the base units it moved and the instant it happened at. No movement happens after the instant the statement attests.
One movement carries:
- Buy
buy - Tokens moved in on a purchase at your distributor. No wallet is named.
- Sell
sell - Tokens moved out on a sale at your distributor. No wallet is named.
- Transfer in
transfer_in - Tokens moved in from a wallet: the wallet they came from is named. It may also name the on-chain transaction that carried them.
- Transfer out
transfer_out - Tokens moved out to a wallet: the wallet they went to is named. It may also name the on-chain transaction that carried them.
The message
You sign the statement's own message, built from the payload's facts in one fixed order: the header lines, then one Position line per position and one Movement line per movement, in the payload's own order. Instants are ISO 8601 in UTC and amounts are the exact base-unit digits, so the signer and the verifier build byte-identical text. The message over this page's sample payload:
Yutai venue attestation
Tenant: tnt_01J9ZR2C4E6G8H0J2K4M6P8ZAB
Venue: tnt_01J9ZR3D5F7H9J1K3M5P7Z9BCD
Account: acct-4177
Payload: stmt-1001
Kind: snapshot
Attested: 2026-10-09T04:00:00.000Z
Position: tki_01J9ZR4E6G8H0J2K4M6P8ZACEX 1250000
Position: tki_01J9ZR5F7H9J1K3M5P7Z9BDFFA 300
Movement: mov-0091 buy tki_01J9ZR4E6G8H0J2K4M6P8ZACEX 250000 2026-10-08T15:12:00.000Z
Movement: mov-0092 transfer_in tki_01J9ZR5F7H9J1K3M5P7Z9BDFFA 100 2026-10-08T18:40:00.000Z 4Nd1mBQ6MJ6ViN5jM2Z4atPozHvGvQtME5hkmZjpSXRVHow fresh a statement must be
- The freshness window
- Yutai accepts a statement at most 300 after the instant it attests. An older one is refused, however it was signed.
- The clock skew
- Your attested instant may sit at most 60 ahead of the instant Yutai receives the statement. Further ahead is refused.
- The hourly cap
- Yutai accepts at most 60 per account per hour, so a backend stuck in a retry loop cannot flood one account. The same statement delivered again inside that hour is answered as the first one and does not count twice.
The signature
You sign the statement's message with the signing half of the key your distributor registered. Yutai verifies every statement against that registered public key before anything is stored; a statement that does not verify is refused and audited, and nothing of it is stored.
- The scheme
- Ed25519, the one scheme the registry carries: a public-key scheme, so Yutai verifies with your registered public key and never holds a signing half.
- The key
- The public half of your signing key: 43 characters of letters, digits, underscore and hyphen, the unpadded base64url of 32 bytes. Only the public half is stored; the signing half stays with you.
- The signature
- The signature of the message above: 86 characters of letters, digits, underscore and hyphen, the unpadded base64url of 64 bytes.
- The key id
- Your statement may name the key that signed it. When it names none, Yutai verifies against your distributor's first key, k1. A key id carries 1 to 32 characters of letters, digits, dot, underscore and hyphen.
- Replacing the key
- When you replace the signing key, the new one starts at the instant you name and the old one keeps verifying until the instant you name too: at most 72 hours after the new one starts. Both verify side by side during that overlap, so a statement signed just before the switch still lands.
The one scheme the registry carries: ed25519. A second scheme extends the registry's list; a caller's own string is refused.
What Yutai refuses
Every refusal below answers with nothing stored: the statement leaves no trail, and the refusal itself is the only record of it.
- No signature
- A statement that carries no signature is refused.
- No registered key
- A statement from a distributor whose key is not registered is refused.
- A signature that does not verify
- A signature that does not verify over the message above, under the registered key, is refused.
- The account reference
- A reference outside its own rule, the one the field above carries, is refused.
- The account's link
- The account must carry an active link on the statement's member, made no later than the instant the statement attests. A statement about an account with none, or attested before its link took effect, is refused.
- The distributor's scope
- Every instrument the statement names must be one your distributor's own listing carries. A statement naming any instrument outside it is refused whole.
- The registry
- Every instrument must be one the statement's member's registry holds as active. One the registry has paused or retired refuses the whole statement.