How Pot works on NEAR (simulation)
This is a simulation. Nothing is signed or sent, and every account, key and hash is made up, but each step follows Pot's real contracts exactly. Each chapter links to a real one on testnet.
Part 1: Start a Pot
Maya's phone signed one request to the Pot Hub, and our relayer paid for it. The Hub created Lisbon Trip as its own Sputnik DAO with no keys: its only seats are the members and the Hub.
Chapter 1 of 6
Maya starts a Pot
You're Maya
Every Pot is its own Sputnik DAO with no keys at all. Its only seats are the members and the Pot Hub.
Invite friends to Lisbon Trip
This is what your group will see.
Link works until Nov 14 · 10 more people can join with it
Invite link
usepot.xyz/j/lisbon-trip-7k2f?i=…#•••••
Under the hood
Simulated- 1 signature by a person
- 1 transaction
- 6 receipts
- 0 NEAR paid by people
Where we start
Maya signed in with Google earlier. Our relayer created her account u-7k2m9qxa.users.potprev.testnet with 0 NEAR and her phone's device key. She has no Pots yet.
What happens, step by step
- Step 1 of 8In the app
Maya names the Pot and picks a cover.
- Step 2 of 8In the app
She keeps the suggested rules: up to $100 without asking, $200 per person in total, bigger spends go to the group.
- Step 3 of 8In the app
Her phone signs one request: “Pot Hub, create Lisbon Trip for me.” No popup, no fee.
- Step 4 of 8
This is all she signed. The Hub is the only contract it can reach.
What Maya signed
Simulated valuesdevice key · ed255…qWntAsk the Pot Hub to create Lisbon Trip with Trip rules. Attach no money. Valid for the next 200 blocks (about 2 minutes).
- Signersender_id
- u-7k2m9qxa.
users. potprev. testnetMaya's account - Callsreceiver_id
- pots.
potprev. testnetthe Pot Hub: the only contract this can call - Methodactions[0].FunctionCall
- create_potone call
- Gasgas
- 300 Tgasthe most it may use; the relayer buys it
- Depositdeposit
- 0Maya attaches no money
Show every field
- Noncenonce
- 275000000000001this key's signature counter; a replay fails
- Valid untilmax_block_height
- 275000200valid for about 2 minutes
- Public keypublic_key
- ed25519:6rFmed3Kxeaqp8Ki7oN8Ad3cCg3S8W5sVqXi9eRYqWntMaya's phone key
- Signaturesignature
- ed25519:5ZjudkhXmVS8RfNX9EwFBxVCw34pyueyUCYjaRiDMwnY2HoLNv9VisfgQyCBkinmW9c4KWpgULBGZLBcxZJqjiGo
- Argumentsslug
- lisbon-trip-7k2f
- Argumentsname
- Lisbon Trip
- Argumentspreset
- trip
- Argumentsrules
- nullnull means the hub's default rules ($100 at a time, $200 per person)
- Argumentsinvite_pk
- ed25519:H87bUxGg3BCKBoTMQk6Z4A41kuTyWMgarVGK45qSyv66the public half of the link's key
- Argumentsemoji
- 🇵🇹
- Argumentsfloat
- null
- How it's signedNEP-366
- 6KcNCen8ZUGzBYKry6QpAgAtcSp9WET7EzhqVUbh2KbDsha256(6e 01 00 40 ‖ borsh(DelegateAction)), then ed25519. The 4-byte NEP-366 prefix makes it unusable as an ordinary transaction.
Raw JSON
{ "delegate_action": { "sender_id": "u-7k2m9qxa.users.potprev.testnet", "receiver_id": "pots.potprev.testnet", "actions": [ { "FunctionCall": { "method_name": "create_pot", "args": { "slug": "lisbon-trip-7k2f", "name": "Lisbon Trip", "preset": "trip", "rules": null, "invite_pk": "ed25519:H87bUxGg3BCKBoTMQk6Z4A41kuTyWMgarVGK45qSyv66", "emoji": "🇵🇹", "float": null }, "args_base64": "eyJzbHVnIjoibGlzYm9uLXRyaXAtN2syZiIsIm5hbWUiOiJMaXNib24gVHJpcCIsInByZXNldCI6InRyaXAiLCJydWxlcyI6bnVsbCwiaW52aXRlX3BrIjoiZWQyNTUxOTpIODdiVXhHZzNCQ0tCb1RNUWs2WjRBNDFrdVR5V01nYXJWR0s0NXFTeXY2NiIsImVtb2ppIjoi8J+HtfCfh7kiLCJmbG9hdCI6bnVsbH0=", "gas": "300000000000000", "deposit": "0" } } ], "nonce": "275000000000001", "max_block_height": "275000200", "public_key": "ed25519:6rFmed3Kxeaqp8Ki7oN8Ad3cCg3S8W5sVqXi9eRYqWnt" }, "signature": "ed25519:5ZjudkhXmVS8RfNX9EwFBxVCw34pyueyUCYjaRiDMwnY2HoLNv9VisfgQyCBkinmW9c4KWpgULBGZLBcxZJqjiGo"}
What the app posts to our relayer
IAAAAHUtN2sybTlxeGEudXNlcnMucG90cHJldi50ZXN0bmV0FAAAAHBvdHMucG90cHJldi50ZXN0bmV0AQAAAAIKAAAAY3JlYXRlX3BvdLAAAAB7InNsdWciOiJsaXNib24tdHJpcC03azJmIiwibmFtZSI6Ikxpc2JvbiBUcmlwIiwicHJlc2V0IjoidHJpcCIsInJ1bGVzIjpudWxsLCJpbnZpdGVfcGsiOiJlZDI1NTE5Okg4N2JVeEdnM0JDS0JvVE1RazZaNEE0MWt1VHlXTWdhclZHSzQ1cVN5djY2IiwiZW1vamkiOiLwn4e18J+HuSIsImZsb2F0IjpudWxsfQDAbjHZEAEAAAAAAAAAAAAAAAAAAAAAAAEwUG0c+gAAiCtkEAAAAAAAVuiYNECWv5AeQiYDZZxa5BBnvnAh5X6YWlqkdTS32ykA5E0xwz3vfYdQTBDDdEZc1ufawOMnWTKAut1DIVpCvlNe/A5NbIHRR650ux/AVKf1NdwgxW+oxoNDbLZTtFv4CA==
- Step 5 of 8
Our relayer puts her request in its own transaction and pays for it. Maya still has 0 NEAR.
What our relayer sent
Simulated valuesThe relayer's transaction
- Signersigner_id
- users.
potprev. testnetRelayer - Receiverreceiver_id
- u-7k2m9qxa.
users. potprev. testnetMaya - Noncenonce
- 274990000000001
- Block hashblock_hash
- sim-3WPv…EnDo
- Transactionhash
- sim-9PED…NLya
Checks before sending
- Allowed contract and methodpots.potprev.testnet · create_pot
- Deposit within the cap0
- Gas exactly as allowed300 Tgas
- Not expired275000200 ≤ 275000000 + 600
- Nonce is new275000000000001
- Signature is valided25519
- Within the daily limitswithin daily limits
Who paid
Relayer · users.
potprev. testnet ≈ 0.0085 NEAR of gas
Pot Hub · pots.
potprev. testnet 0.0500 NEAR float for the Pot's own account
0.00125 NEAR to open the Pot's dollar balance
Maya · u-7k2m9qxa.
users. potprev. testnet Maya: 0 NEAR. Their account has 0 NEAR before and after.
Copied from a real testnet run of the same call. A simulation can't burn gas.
Raw JSON
{ "hash": "sim-9PEDmUEZyt9bpNZA87zdwzF39CHMNLya", "signer_id": "users.potprev.testnet", "public_key": "ed25519:3q2rinP5DL1aiB33YTh372bu3K3Tf9kwNyAUjEm4xzF3", "nonce": "274990000000001", "receiver_id": "u-7k2m9qxa.users.potprev.testnet", "block_hash": "sim-3WPvNxDTar4wD1gnH1vGNE8yPjGnEnDo", "actions": [ { "Delegate": { "tx": "c1.create" } } ], "checks": [ { "check": "allowlist", "value": "pots.potprev.testnet · create_pot", "ok": true }, { "check": "deposit_cap", "value": "0", "ok": true }, { "check": "gas", "value": "300 Tgas", "ok": true }, { "check": "expiry", "value": "275000200 ≤ 275000000 + 600", "ok": true }, { "check": "nonce", "value": "275000000000001", "ok": true }, { "check": "signature", "value": "ed25519", "ok": true }, { "check": "quotas", "value": "within daily limits", "ok": true } ]}
What happened on NEAR, receipt by receipt
Receipts 1–2 of 6sim-9PED…NLyaSimulated
- ·Phone → RelayerThe signed request goes to our relayer (POST /api/relay), off-chain
- 1Relayer → MayaDelegate: runs Maya's signed request
- 2Maya → Pot Hubcreate_pot300 Tgas
- Step 6 of 8
The Hub creates a new account for the Pot, gives it 0.05 NEAR to store its records, and installs the Sputnik DAO code with two seats: Maya, and the Hub.
What happened on NEAR, receipt by receipt
Receipt 3 of 6sim-9PED…NLyaSimulated
- 3Pot Hub → Lisbon Tripone receipt, 4 actionsCreateAccountTransfer 0.05 NEARUseGlobalContract Ea8tHXFS…new20 Tgas
Good to know
The Pot's account has no access keys. Nobody can sign for it, not even us. It can only do what its members and the Hub's seat approve.
- Step 7 of 8
The Hub writes the Pot into its list and opens a USDC (test) balance for it, so it can hold dollars.
What happened on NEAR, receipt by receipt
Receipts 4–6 of 6sim-9PED…NLyaSimulated
- 4Callback from Lisbon Trip to Pot Hubcallbackon_pot_created40 Tgas
- 5Pot Hub → USDC (test)storage_deposit
(Lisbon Trip)10 Tgas0.00125 NEARreturned 0.00125 NEAR - 6Callback from USDC (test) to Pot Hubcallbackon_dao_registered10 Tgasreturned ok
Show 6 gas refunds
- 7Gas refund to the relayer · 0.02114 NEAR
- 8Gas refund to the relayer · 0.01073 NEAR
- 9Gas refund to the relayer · 0.01071 NEAR
- 10Gas refund to the relayer · 0.01873 NEAR
- 11Gas refund to the relayer · 0.23118 NEAR
- 12Gas refund to the relayer · 0.00046 NEAR
- Step 8 of 8In the app
Maya gets a link to drop in the group chat. The secret part after # never reaches a server.
Meanwhile
Sam joined from the same link (proposal #0, approved by the Hub's seat like Leo's in chapter 2), and Maya and Sam each added $150. Lisbon Trip has $300 of $450.
After this chapter
What changed
| Field | Before | After |
|---|---|---|
| The Pot's account (changed) | doesn't exist | a Sputnik DAO, no access keys, 0.05 NEAR float |
| Members (DAO role member) (changed) | — | Maya |
| Group approvals needed (changed) | — | 1 of 1 |
| Proposals (changed) | — | none yet |
| Pot status (changed) | none | Open |
| Hub ledger: put in (changed) | — | Maya $0.00 |
| Hub ledger: spent without asking (changed) | — | Maya $0.00 |
Raw JSON
{ "before": { "pot": null, "dao": null }, "after": { "pot": { "status": "open", "members": [ { "account": "u-7k2m9qxa.users.potprev.testnet", "joinIndex": 0, "contributed": "0", "instantUsed": "0" } ], "ledger": { "totalContributed": "0", "spentInstant": "0", "spentVote": "0", "reserved": "0" }, "requests": [] }, "dao": { "exists": true, "members": [ "u-7k2m9qxa.users.potprev.testnet" ], "rulebot": [ "pots.potprev.testnet" ], "threshold": { "k": 1, "n": 1 }, "lastProposalId": 0, "proposals": [], "floatNear": "0.05", "policy": { "roles": [ { "name": "member", "kind": { "Group": [ "u-7k2m9qxa.users.potprev.testnet" ] }, "permissions": [ "*:VoteApprove", "*:VoteReject" ], "vote_policy": {} }, { "name": "rulebot", "kind": { "Group": [ "pots.potprev.testnet" ] }, "permissions": [ "transfer:AddProposal", "transfer:VoteApprove", "transfer:Finalize", "add_member_to_role:AddProposal", "add_member_to_role:VoteApprove", "call:AddProposal", "call:Finalize" ], "vote_policy": { "transfer": { "weight_kind": "RoleWeight", "quorum": "1", "threshold": [ 1, 1 ] }, "add_member_to_role": { "weight_kind": "RoleWeight", "quorum": "1", "threshold": [ 1, 1 ] } } } ], "default_vote_policy": { "weight_kind": "RoleWeight", "quorum": "1", "threshold": [ 1, 2 ] }, "proposal_bond": "0", "proposal_period": "604800000000000", "bounty_bond": "0", "bounty_forgiveness_period": "86400000000000" } } }}
Balances
| Account | Before | After | Change |
|---|---|---|---|
| Maya | $0.00 | $0.00 | no change |
| Lisbon Trip | $0.00 | $0.00 | no change |
| Pot Hub | $0.00 | $0.00 | no change |
NEAR (fees and storage)
- Maya0 NEAR
- Pot Hub− 0.05125 NEAR
- Relayer− ≈ 0.00848 NEAR
Copied from a real testnet run of the same call. A simulation can't burn gas.
Raw JSON
{ "before": { "u-7k2m9qxa.users.potprev.testnet": { "usdc": "0", "near": "0" }, "pots.potprev.testnet": { "usdc": "0" } }, "after": { "u-7k2m9qxa.users.potprev.testnet": { "usdc": "0", "near": "0" }, "pots.potprev.testnet": { "usdc": "0" }, "lisbon-trip-7k2f.pots.potprev.testnet": { "usdc": "0" } }}
Who paid
Relayer · users.
potprev. testnet ≈ 0.0085 NEAR of gas
Pot Hub · pots.
potprev. testnet 0.0500 NEAR float for the Pot's own account
0.00125 NEAR to open the Pot's dollar balance
Maya · u-7k2m9qxa.
users. potprev. testnet Maya: 0 NEAR. Their account has 0 NEAR before and after.
Copied from a real testnet run of the same call. A simulation can't burn gas.
See a real one on testnet
Maya creating the reference Pot, Reference Trip (reference-trip-2gzw.pots.potprev.testnet), with one signed call to the Hub, as here. Its hub is newer than this simulation, so the same call also turns on one OK up to $300, earned trust and pay links, and logs one more event (rule_extras_set).
Real · testnet (opens nearblocks.io in a new tab)DMStMzsUAkBQph7BZuos3ADmhizyxitrjQUugybMMXXw
Words in this chapter
23 words
- DAO
- A shared account run by rules instead of a person. Here every Pot is one, and its members approve what it does.
- Sputnik DAO
- NEAR's standard DAO contract. Pot uses its official code, installed by hash, unchanged.
- Pot Hub
- Pot's own contract (pots.potprev.testnet). It creates each Pot, checks the rules, holds the rulebot seat and keeps the ledger of who put in what.
- rulebot
- The Pot Hub's seat in every Pot. One vote from it approves transfers and new members, and the Hub only casts it when the Pot's rules allow.
- role / seat
- A named group in the DAO with its own powers. A Pot has two: member (the friends) and rulebot (only the Pot Hub).
- policy
- The DAO's rulebook: its roles, their powers, thresholds and the 7-day voting period.
- threshold
- How many approvals a role needs. Members need a majority: 2 of 3 here.
- quorum
- The minimum number of approvals ever accepted. Pot sets it to 1, so nothing can pass with zero votes.
- delegate action
- A request the user signs but doesn't send (NEP-366). Someone else wraps it in a transaction and pays for it; contracts still see the user as the caller.
- relayer
- Pot's server account (users.potprev.testnet) that sends signed requests and pays their gas, so users need 0 NEAR. It refuses anything outside an allowlist.
- transaction
- What gets submitted to NEAR. Here the relayer signs it, and the user's request rides inside.
- receipt
- One step of a transaction's work, run by one account. A transaction fans out into receipts as contracts call each other.
- callback (then)
- A receipt that runs after another finishes and reads its result.
- gas / Tgas
- The work a call may use, bought in NEAR. 1 Tgas is a trillion gas, about 0.0001 NEAR. Unused gas is refunded.
- deposit
- NEAR attached to a call. In Pot it is 0 everywhere except that 1 yoctoNEAR.
- nonce
- A counter on each key. Every signature uses a higher one, so an old signature can't be replayed.
- max_block_height
- The block after which a signed request expires: about 2 minutes here.
- device key
- The key Pot keeps on your phone (non-extractable). It signs your requests without a popup.
- ed25519 signature
- The kind of digital signature NEAR uses: 64 bytes that prove a key signed exactly this message.
- invite key
- A key made for one invite link. Its secret lives after the # in the link; the Hub stores only its public half.
- account (named)
- A NEAR account like u-3p8w2kdn.users.potprev.testnet. Pot creates it for you with 0 NEAR; your name lives off-chain.
- global contract
- Contract code stored once on NEAR and used by many accounts by its hash. Every Pot uses Sputnik's this way, for 0.012 NEAR instead of 5.4.
- USDC (test)
- Pot's test dollar token on testnet (NEP-141, 6 decimals). It has no value.
Part 2: Join and chip in
Joining is a DAO proposal that the Hub approves with its one seat, only if the link's signature checks out. Leo's $150 then passes through the Hub, which writes down who put in what.
Chapter 2 of 6
Leo joins from the link
You're Leo
Joining is a DAO decision too, but a pre-approved one: the Hub's single seat adds a member only when the invite signature checks out.
You're in!
Welcome to Lisbon Trip. The suggested amount is $150 each.
Under the hood
Simulated- 2 signatures by people
- 1 transaction
- 8 receipts
- 0 NEAR paid by people
Where we start
Leo tapped the link and signed in with Google. Our relayer created u-3p8w2kdn.users.potprev.testnet with 0 NEAR, added his device key and paid his USDC registration. The Pot has Maya and Sam, $300, and Sam's join (proposal #0) approved. Group approvals need 2 of 2.
- In the Pot
- $300.00
- Members (DAO role member)
- Maya, Sam
- Group approvals needed
- 2 of 2
Meanwhile
Sam joined from the same link (proposal #0, approved by the Hub's seat like Leo's in chapter 2), and Maya and Sam each added $150. Lisbon Trip has $300 of $450.
What happens, step by step
- Step 1 of 8In the app
Leo sees who invited him, who's in, and the rules, before he commits to anything.
- Step 2 of 8In the app
Joining takes two signatures, both on Leo's phone.
- Step 3 of 8
First, the secret in the link signs “Leo joins Lisbon Trip”. That proves he got the link, and it only works for Leo.
What the link signed
Simulated values- MessageUTF-8
- pot-join:v1:lisbon-trip-7k2f.pots.potprev.testnet:u-3p8w2kdn.users.potprev.testnet
- Key?i=
- H87bUxGg3BCKBoTMQk6Z4A41kuTyWMgarVGK45qSyv66the public half of the link's key
- Signaturebase64 (88)
- VDsqmpk7X/BygwjjmxmJBhNYbuoUGlJ0wcgYLbcCUyNLNXBDeWLck6BaHNf4TNQQ5GlJNAl53RiugvacFgn3CA==
- Why it can't be reused
- Domain, Pot and Leo's account are all in the message, so it can't be reused for another Pot, another network or another person.Raw ed25519 over the message's UTF-8 bytes: no hashing, no envelope. The key's seed is the secret after # in the link.
Raw JSON
{ "message": "pot-join:v1:lisbon-trip-7k2f.pots.potprev.testnet:u-3p8w2kdn.users.potprev.testnet", "public_key": "ed25519:H87bUxGg3BCKBoTMQk6Z4A41kuTyWMgarVGK45qSyv66", "signature": "VDsqmpk7X/BygwjjmxmJBhNYbuoUGlJ0wcgYLbcCUyNLNXBDeWLck6BaHNf4TNQQ5GlJNAl53RiugvacFgn3CA=="}
- Step 4 of 8
Then his own key signs the request to the Hub, with that proof inside.
What Leo signed
Simulated valuesdevice key · ed255…HJirAsk the Pot Hub to add me to Lisbon Trip, with the link's proof inside. Attach no money. Valid for about 2 minutes.
- Signersender_id
- u-3p8w2kdn.
users. potprev. testnetLeo's account - Callsreceiver_id
- pots.
potprev. testnetthe Pot Hub: the only contract this can call - Methodactions[0].FunctionCall
- joinone call
- Gasgas
- 100 Tgasthe most it may use; the relayer buys it
- Depositdeposit
- 0Leo attaches no money
Show every field
- Noncenonce
- 275010024000001this key's signature counter; a replay fails
- Valid untilmax_block_height
- 275010224valid for about 2 minutes
- Public keypublic_key
- ed25519:GM9zgxaPnBHvJ5hvWTJJwbiPoAvRj6z6nhghxJfZHJirLeo's phone key
- Signaturesignature
- ed25519:2uPPoDiAwxRYMduUxgUGKYPWZ6RTMujkrLU1xTmQnrUWiQMEigFKfbVkaU2RY2mkCiFybFc1iaZarFeTLSB5bWG2
- Argumentspot
- lisbon-trip-7k2f.
pots. potprev. testnetLisbon Trip - Argumentsinvite_pk
- ed25519:H87bUxGg3BCKBoTMQk6Z4A41kuTyWMgarVGK45qSyv66the public half of the link's key
- Argumentssignature
- VDsqmpk7X/BygwjjmxmJBhNYbuoUGlJ0wcgYLbcCUyNLNXBDeWLck6BaHNf4TNQQ5GlJNAl53RiugvacFgn3CA==
- How it's signedNEP-366
- DtYFbbeNgkbzDvDnkmAFbLFfCpxRanBiivWvq1ErtPiHsha256(6e 01 00 40 ‖ borsh(DelegateAction)), then ed25519. The 4-byte NEP-366 prefix makes it unusable as an ordinary transaction.
Raw JSON
{ "delegate_action": { "sender_id": "u-3p8w2kdn.users.potprev.testnet", "receiver_id": "pots.potprev.testnet", "actions": [ { "FunctionCall": { "method_name": "join", "args": { "pot": "lisbon-trip-7k2f.pots.potprev.testnet", "invite_pk": "ed25519:H87bUxGg3BCKBoTMQk6Z4A41kuTyWMgarVGK45qSyv66", "signature": "VDsqmpk7X/BygwjjmxmJBhNYbuoUGlJ0wcgYLbcCUyNLNXBDeWLck6BaHNf4TNQQ5GlJNAl53RiugvacFgn3CA==" }, "args_base64": "eyJwb3QiOiJsaXNib24tdHJpcC03azJmLnBvdHMucG90cHJldi50ZXN0bmV0IiwiaW52aXRlX3BrIjoiZWQyNTUxOTpIODdiVXhHZzNCQ0tCb1RNUWs2WjRBNDFrdVR5V01nYXJWR0s0NXFTeXY2NiIsInNpZ25hdHVyZSI6IlZEc3FtcGs3WC9CeWd3ampteG1KQmhOWWJ1b1VHbEowd2NnWUxiY0NVeU5MTlhCRGVXTGNrNkJhSE5mNFROUVE1R2xKTkFsNTNSaXVndmFjRmduM0NBPT0ifQ==", "gas": "100000000000000", "deposit": "0" } } ], "nonce": "275010024000001", "max_block_height": "275010224", "public_key": "ed25519:GM9zgxaPnBHvJ5hvWTJJwbiPoAvRj6z6nhghxJfZHJir" }, "signature": "ed25519:2uPPoDiAwxRYMduUxgUGKYPWZ6RTMujkrLU1xTmQnrUWiQMEigFKfbVkaU2RY2mkCiFybFc1iaZarFeTLSB5bWG2"}
What the app posts to our relayer
IAAAAHUtM3A4dzJrZG4udXNlcnMucG90cHJldi50ZXN0bmV0FAAAAHBvdHMucG90cHJldi50ZXN0bmV0AQAAAAIEAAAAam9pbtkAAAB7InBvdCI6Imxpc2Jvbi10cmlwLTdrMmYucG90cy5wb3RwcmV2LnRlc3RuZXQiLCJpbnZpdGVfcGsiOiJlZDI1NTE5Okg4N2JVeEdnM0JDS0JvVE1RazZaNEE0MWt1VHlXTWdhclZHSzQ1cVN5djY2Iiwic2lnbmF0dXJlIjoiVkRzcW1wazdYL0J5Z3dqam14bUpCaE5ZYnVvVUdsSjB3Y2dZTGJjQ1V5TkxOWEJEZVdMY2s2QmFITmY0VE5RUTVHbEpOQWw1M1JpdWd2YWNGZ24zQ0E9PSJ9AEB6EPNaAAAAAAAAAAAAAAAAAAAAAAAAAUrKwh76AACwUmQQAAAAAADkCRcg2kie3vC1TqTJt/e6WB8ntuwCrkJS2qd74ubk3wBfMVl2jzoPiLxNjwCb4iCK6e2gBZxe+Qi2MEKfs2rj2Fr8NkREugY1YkeOqJwyorgCBZUm19lXDRWA7PbAK1YL
- Step 5 of 8
The Hub checks the link is valid, then files a request in the Pot: “add Leo as a member”.
What our relayer sent
Simulated valuesThe relayer's transaction
- Signersigner_id
- users.
potprev. testnetRelayer - Receiverreceiver_id
- u-3p8w2kdn.
users. potprev. testnetLeo - Noncenonce
- 274990000000002
- Block hashblock_hash
- sim-EWnp…VkzS
- Transactionhash
- sim-CitN…TEAE
Checks before sending
- Allowed contract and methodpots.potprev.testnet · join
- Deposit within the cap0
- Gas exactly as allowed100 Tgas
- Not expired275010224 ≤ 275010024 + 600
- Nonce is new275010024000001
- Signature is valided25519
- Within the daily limitswithin daily limits
Who paid
Relayer · users.
potprev. testnet ≈ 0.0018 NEAR of gas
Pot Hub · pots.
potprev. testnet 0.00513 NEAR storage prepay for the new proposal
Leo · u-3p8w2kdn.
users. potprev. testnet Leo: 0 NEAR. Their account has 0 NEAR before and after.
Copied from a real testnet run of the same call. A simulation can't burn gas.
Raw JSON
{ "hash": "sim-CitNE7iCYLfwDH6Fqdodk8GJQuGeTEAE", "signer_id": "users.potprev.testnet", "public_key": "ed25519:3q2rinP5DL1aiB33YTh372bu3K3Tf9kwNyAUjEm4xzF3", "nonce": "274990000000002", "receiver_id": "u-3p8w2kdn.users.potprev.testnet", "block_hash": "sim-EWnp1BucDhZGNzfqDUf3QHRCoRhuVkzS", "actions": [ { "Delegate": { "tx": "c2.join" } } ], "checks": [ { "check": "allowlist", "value": "pots.potprev.testnet · join", "ok": true }, { "check": "deposit_cap", "value": "0", "ok": true }, { "check": "gas", "value": "100 Tgas", "ok": true }, { "check": "expiry", "value": "275010224 ≤ 275010024 + 600", "ok": true }, { "check": "nonce", "value": "275010024000001", "ok": true }, { "check": "signature", "value": "ed25519", "ok": true }, { "check": "quotas", "value": "within daily limits", "ok": true } ]}
What happened on NEAR, receipt by receipt
Receipts 1–3 of 8sim-CitN…TEAESimulated
- ·Phone → RelayerThe signed request goes to our relayer (POST /api/relay), off-chain
- 1Relayer → LeoDelegate: runs Leo's signed request
- 2Leo → Pot Hubjoin
(Lisbon Trip)100 Tgas - 3Pot Hub → Lisbon Tripone receipt, 2 actionsTransfer 0.00513 NEARadd_proposal10 Tgasreturned 1
- Step 6 of 8
The Hub approves it with its seat. For adding members it is the only vote needed, so Leo is in at once.
ApprovedProposal #1 in Lisbon Tripa Sputnik DAO
Add Leo as a member
Pot join via invite
· filed by Pot HubWho can decide this
Members (3)
weren't asked
Pot rules seat (1)- Pot Hub approved
- needs 1
How “1” is computed
Role rulebot, 1 seat (the Pot Hub), threshold [1, 1] for transfers and new members: min(1 × 1 ÷ 1 + 1, 1) = 1. The member role was never asked.
Raw JSON
{ "id": 1, "proposer": "pots.potprev.testnet", "description": "Pot join via invite", "kind": { "AddMemberToRole": { "member_id": "u-3p8w2kdn.users.potprev.testnet", "role": "member" } }, "status": "Approved", "vote_counts": { "rulebot": [ "1", "0", "0" ] }, "votes": { "pots.potprev.testnet": "Approve" }, "submission_time": "1794048017400000000", "last_actions_log": [ { "block_height": "275010029" }, { "block_height": "275010031" } ]}
What happened on NEAR, receipt by receipt
Receipts 4–6 of 8sim-CitN…TEAESimulated
- 4Callback from Lisbon Trip to Pot Hubcallbackon_join_added
(Lisbon Trip)60 Tgas - 5Pot Hub → Lisbon Tripact_proposal
(#1, VoteApprove)20 Tgas - 6Lisbon Trip → Pot HubTransfer 0
- Step 7 of 8
Now three people can approve, and bigger spends need two of them.
What changed
What changed in the Pot and the Hub Field Before After The Pot's account a Sputnik DAO, no access keys, 0.05 NEAR float a Sputnik DAO, no access keys, 0.05 NEAR float Members (DAO role member) (changed) Maya, Sam Maya, Sam, Leo Group approvals needed (changed) 2 of 2 2 of 3 Proposals (changed) #0 Add member · Approved #0 Add member · Approved #1 Add member · Approved Pot status Open Open Hub ledger: put in (changed) Maya $150.00 · Sam $150.00 Maya $150.00 · Sam $150.00 · Leo $0.00 Hub ledger: spent without asking (changed) Maya $0.00 · Sam $0.00 Maya $0.00 · Sam $0.00 · Leo $0.00 Raw JSON
{ "before": { "pot": { "status": "open", "members": [ { "account": "u-7k2m9qxa.users.potprev.testnet", "joinIndex": 0, "contributed": "150000000", "instantUsed": "0" }, { "account": "u-4hn8c2wd.users.potprev.testnet", "joinIndex": 1, "contributed": "150000000", "instantUsed": "0" } ], "ledger": { "totalContributed": "300000000", "spentInstant": "0", "spentVote": "0", "reserved": "0" }, "requests": [] }, "dao": { "exists": true, "members": [ "u-7k2m9qxa.users.potprev.testnet", "u-4hn8c2wd.users.potprev.testnet" ], "rulebot": [ "pots.potprev.testnet" ], "threshold": { "k": 2, "n": 2 }, "lastProposalId": 1, "proposals": [ { "id": 0, "proposer": "pots.potprev.testnet", "description": "Pot join via invite", "kind": { "AddMemberToRole": { "member_id": "u-4hn8c2wd.users.potprev.testnet", "role": "member" } }, "status": "Approved", "vote_counts": { "rulebot": [ "1", "0", "0" ] }, "votes": { "pots.potprev.testnet": "Approve" }, "submission_time": "1794042306600000000", "last_actions_log": [ { "block_height": "275000511" }, { "block_height": "275000513" } ] } ], "floatNear": "0.05" } }, "after": { "pot": { "status": "open", "members": [ { "account": "u-7k2m9qxa.users.potprev.testnet", "joinIndex": 0, "contributed": "150000000", "instantUsed": "0" }, { "account": "u-4hn8c2wd.users.potprev.testnet", "joinIndex": 1, "contributed": "150000000", "instantUsed": "0" }, { "account": "u-3p8w2kdn.users.potprev.testnet", "joinIndex": 2, "contributed": "0", "instantUsed": "0" } ], "ledger": { "totalContributed": "300000000", "spentInstant": "0", "spentVote": "0", "reserved": "0" }, "requests": [] }, "dao": { "exists": true, "members": [ "u-7k2m9qxa.users.potprev.testnet", "u-4hn8c2wd.users.potprev.testnet", "u-3p8w2kdn.users.potprev.testnet" ], "rulebot": [ "pots.potprev.testnet" ], "threshold": { "k": 2, "n": 3 }, "lastProposalId": 2, "proposals": [ { "id": 0, "proposer": "pots.potprev.testnet", "description": "Pot join via invite", "kind": { "AddMemberToRole": { "member_id": "u-4hn8c2wd.users.potprev.testnet", "role": "member" } }, "status": "Approved", "vote_counts": { "rulebot": [ "1", "0", "0" ] }, "votes": { "pots.potprev.testnet": "Approve" }, "submission_time": "1794042306600000000", "last_actions_log": [ { "block_height": "275000511" }, { "block_height": "275000513" } ] }, { "id": 1, "proposer": "pots.potprev.testnet", "description": "Pot join via invite", "kind": { "AddMemberToRole": { "member_id": "u-3p8w2kdn.users.potprev.testnet", "role": "member" } }, "status": "Approved", "vote_counts": { "rulebot": [ "1", "0", "0" ] }, "votes": { "pots.potprev.testnet": "Approve" }, "submission_time": "1794048017400000000", "last_actions_log": [ { "block_height": "275010029" }, { "block_height": "275010031" } ] } ], "floatNear": "0.05", "policy": { "roles": [ { "name": "member", "kind": { "Group": [ "u-7k2m9qxa.users.potprev.testnet", "u-4hn8c2wd.users.potprev.testnet", "u-3p8w2kdn.users.potprev.testnet" ] }, "permissions": [ "*:VoteApprove", "*:VoteReject" ], "vote_policy": {} }, { "name": "rulebot", "kind": { "Group": [ "pots.potprev.testnet" ] }, "permissions": [ "transfer:AddProposal", "transfer:VoteApprove", "transfer:Finalize", "add_member_to_role:AddProposal", "add_member_to_role:VoteApprove", "call:AddProposal", "call:Finalize" ], "vote_policy": { "transfer": { "weight_kind": "RoleWeight", "quorum": "1", "threshold": [ 1, 1 ] }, "add_member_to_role": { "weight_kind": "RoleWeight", "quorum": "1", "threshold": [ 1, 1 ] } } } ], "default_vote_policy": { "weight_kind": "RoleWeight", "quorum": "1", "threshold": [ 1, 2 ] }, "proposal_bond": "0", "proposal_period": "604800000000000", "bounty_bond": "0", "bounty_forgiveness_period": "86400000000000" } } }}
What happened on NEAR, receipt by receipt
Receipts 7–8 of 8sim-CitN…TEAESimulated
- 7Pot Hub → Lisbon Tripget_proposal
(#1)10 Tgasreturned #1 Approved - 8Callback from Lisbon Trip to Pot Hubcallbackon_join_status
(Lisbon Trip)10 Tgasreturned status active
Show 8 gas refunds
- 9Gas refund to the relayer · 0.01079 NEAR
- 10Gas refund to the relayer · 0.00020 NEAR
- 11Gas refund to the relayer · 0.02040 NEAR
- 12Gas refund to the relayer · 0.01072 NEAR
- 13Gas refund to the relayer · 0.01061 NEAR
- 14Gas refund to the relayer · 0.01785 NEAR
- 15Gas refund to the relayer · 0.02860 NEAR
- 16Gas refund to the relayer · 0.00046 NEAR
- 7Pot Hub → Lisbon Tripget_proposal
- Step 8 of 8In the app
Leo is in, without ever paying a fee.
After this chapter
Balances
| Account | Before | After | Change |
|---|---|---|---|
| Maya | $0.00 | $0.00 | no change |
| Sam | $0.00 | $0.00 | no change |
| Leo | $0.00 | $0.00 | no change |
| Lisbon Trip | $300.00 | $300.00 | no change |
| Pot Hub | $0.00 | $0.00 | no change |
NEAR (fees and storage)
- Leo0 NEAR
- Pot Hub− 0.00513 NEAR
- Relayer− ≈ 0.00179 NEAR
Copied from a real testnet run of the same call. A simulation can't burn gas.
Raw JSON
{ "before": { "u-7k2m9qxa.users.potprev.testnet": { "usdc": "0", "near": "0" }, "u-4hn8c2wd.users.potprev.testnet": { "usdc": "0", "near": "0" }, "u-3p8w2kdn.users.potprev.testnet": { "usdc": "0", "near": "0" }, "pots.potprev.testnet": { "usdc": "0" }, "lisbon-trip-7k2f.pots.potprev.testnet": { "usdc": "300000000" } }, "after": { "u-7k2m9qxa.users.potprev.testnet": { "usdc": "0", "near": "0" }, "u-4hn8c2wd.users.potprev.testnet": { "usdc": "0", "near": "0" }, "u-3p8w2kdn.users.potprev.testnet": { "usdc": "0", "near": "0" }, "pots.potprev.testnet": { "usdc": "0" }, "lisbon-trip-7k2f.pots.potprev.testnet": { "usdc": "300000000" } }}
Who paid
Relayer · users.
potprev. testnet ≈ 0.0018 NEAR of gas
Pot Hub · pots.
potprev. testnet 0.00513 NEAR storage prepay for the new proposal
Leo · u-3p8w2kdn.
users. potprev. testnet Leo: 0 NEAR. Their account has 0 NEAR before and after.
Copied from a real testnet run of the same call. A simulation can't burn gas.
See a real one on testnet
Sam joining the reference Pot with its invite link: the same join as Leo's here, approved by the Hub's seat.
Real · testnet (opens nearblocks.io in a new tab)CTk22gc9qp2fumUjFRKCaznd4DihZpkZfy68z9KCykTr
Words in this chapter
14 words
- invite key
- A key made for one invite link. Its secret lives after the # in the link; the Hub stores only its public half.
- ed25519 signature
- The kind of digital signature NEAR uses: 64 bytes that prove a key signed exactly this message.
- delegate action
- A request the user signs but doesn't send (NEP-366). Someone else wraps it in a transaction and pays for it; contracts still see the user as the caller.
- relayer
- Pot's server account (users.potprev.testnet) that sends signed requests and pays their gas, so users need 0 NEAR. It refuses anything outside an allowlist.
- Pot Hub
- Pot's own contract (pots.potprev.testnet). It creates each Pot, checks the rules, holds the rulebot seat and keeps the ledger of who put in what.
- proposal
- A request filed in the DAO, like “pay Maya $84” or “add Leo”. It runs only once enough seats approve it.
- rulebot
- The Pot Hub's seat in every Pot. One vote from it approves transfers and new members, and the Hub only casts it when the Pot's rules allow.
- role / seat
- A named group in the DAO with its own powers. A Pot has two: member (the friends) and rulebot (only the Pot Hub).
- threshold
- How many approvals a role needs. Members need a majority: 2 of 3 here.
- receipt
- One step of a transaction's work, run by one account. A transaction fans out into receipts as contracts call each other.
- callback (then)
- A receipt that runs after another finishes and reads its result.
- storage prepay
- NEAR the Hub sends with each proposal, so the Pot can pay for the bytes it stores.
- account (named)
- A NEAR account like u-3p8w2kdn.users.potprev.testnet. Pot creates it for you with 0 NEAR; your name lives off-chain.
- gas / Tgas
- The work a call may use, bought in NEAR. 1 Tgas is a trillion gas, about 0.0001 NEAR. Unused gas is refunded.
Chapter 3 of 6
Leo adds his $150
You're Leo
The money passes through the Hub and lands in the Pot in one chain of receipts. The Hub writes down who put in what, which makes fair refunds possible later.
Lisbon Trip
In the Pot
$450.00
You'd get back $150 · You put in $150
$450 of $450 collected
Today
Leo added $150
+ $150.00Maya added $150
+ $150.00Sam added $150
+ $150.00
Under the hood
Simulated- 1 signature by a person
- 1 transaction
- 6 receipts
- 0 NEAR paid by people
Where we start
Leo has $150.00 of demo dollars, which the faucet (treasury.potprev.testnet) sent him off-screen. The Pot has $300.00 of $450.
- In the Pot
- $300.00
- Members (DAO role member)
- Maya, Sam, Leo
- Group approvals needed
- 2 of 3
What happens, step by step
- Step 1 of 7In the app
Leo adds his $150 share. The sheet shows the result before he taps.
- Step 2 of 7In the app
His phone signs one request, this time to the dollar token itself.
- Step 3 of 7
He asks USDC to send $150 to the Pot Hub, with a note saying which Pot it's for.
What Leo signed
Simulated valuesdevice key · ed255…HJirAsk USDC (test) to send $150.00 to the Pot Hub for Lisbon Trip. Attach exactly 1 yoctoNEAR. Valid for about 2 minutes.
- Signersender_id
- u-3p8w2kdn.
users. potprev. testnetLeo's account - Callsreceiver_id
- usdc.
potprev. testnetthe dollar token itself - Methodactions[0].FunctionCall
- ft_transfer_callone call
- Amountargs.amount = "150000000"
- $150.00$150.00 (USDC has 6 decimals)
- Gasgas
- 100 Tgasthe most it may use; the relayer buys it
- Depositdeposit
- 1 yoctoNEAR1 yoctoNEAR, which token transfers require; the relayer pays it
Show every field
- Noncenonce
- 275010024000002this key's signature counter; a replay fails
- Valid untilmax_block_height
- 275010232valid for about 2 minutes
- Public keypublic_key
- ed25519:GM9zgxaPnBHvJ5hvWTJJwbiPoAvRj6z6nhghxJfZHJirLeo's phone key
- Signaturesignature
- ed25519:3vCL7oSRirSLFQuc7A73kyiMdgX8dgreJNPxotTHYDardSXUP6wrjJprWYPjYXxBrU7rV8CQX9baiR5avJmHUQna
- Argumentsreceiver_id
- pots.
potprev. testnetPot Hub - Argumentsamount
- 150000000$150.00 (USDC has 6 decimals)
- Argumentsmsg
- {"pot":"lisbon-trip-7k2f.pots.potprev.testnet"}
- How it's signedNEP-366
- Ar7qTuRc9qk6ZTpMbzq9cri5R3TgqCRqKHHo2VSMJHm4sha256(6e 01 00 40 ‖ borsh(DelegateAction)), then ed25519. The 4-byte NEP-366 prefix makes it unusable as an ordinary transaction.
Raw JSON
{ "delegate_action": { "sender_id": "u-3p8w2kdn.users.potprev.testnet", "receiver_id": "usdc.potprev.testnet", "actions": [ { "FunctionCall": { "method_name": "ft_transfer_call", "args": { "receiver_id": "pots.potprev.testnet", "amount": "150000000", "msg": "{\"pot\":\"lisbon-trip-7k2f.pots.potprev.testnet\"}" }, "args_base64": "eyJyZWNlaXZlcl9pZCI6InBvdHMucG90cHJldi50ZXN0bmV0IiwiYW1vdW50IjoiMTUwMDAwMDAwIiwibXNnIjoie1wicG90XCI6XCJsaXNib24tdHJpcC03azJmLnBvdHMucG90cHJldi50ZXN0bmV0XCJ9In0=", "gas": "100000000000000", "deposit": "1" } } ], "nonce": "275010024000002", "max_block_height": "275010232", "public_key": "ed25519:GM9zgxaPnBHvJ5hvWTJJwbiPoAvRj6z6nhghxJfZHJir" }, "signature": "ed25519:3vCL7oSRirSLFQuc7A73kyiMdgX8dgreJNPxotTHYDardSXUP6wrjJprWYPjYXxBrU7rV8CQX9baiR5avJmHUQna"}
What the app posts to our relayer
IAAAAHUtM3A4dzJrZG4udXNlcnMucG90cHJldi50ZXN0bmV0FAAAAHVzZGMucG90cHJldi50ZXN0bmV0AQAAAAIQAAAAZnRfdHJhbnNmZXJfY2FsbHcAAAB7InJlY2VpdmVyX2lkIjoicG90cy5wb3RwcmV2LnRlc3RuZXQiLCJhbW91bnQiOiIxNTAwMDAwMDAiLCJtc2ciOiJ7XCJwb3RcIjpcImxpc2Jvbi10cmlwLTdrMmYucG90cy5wb3RwcmV2LnRlc3RuZXRcIn0ifQBAehDzWgAAAQAAAAAAAAAAAAAAAAAAAAJKysIe+gAAuFJkEAAAAAAA5AkXINpInt7wtU6kybf3ulgfJ7bsAq5CUtqne+Lm5N8Akeha51ETDK24XhNc6LZlZV4WCHggNIgBA2xHtKFvjtTRl2heFEBNHNSTbI0fLYnDef7FJA5cRB5PN9xhIxk7Bw==
- Step 4 of 7
Token transfers need one yoctoNEAR attached, a millionth of a billionth of a billionth of a NEAR. The relayer pays even that.
What our relayer sent
Simulated valuesThe relayer's transaction
- Signersigner_id
- users.
potprev. testnetRelayer - Receiverreceiver_id
- u-3p8w2kdn.
users. potprev. testnetLeo - Noncenonce
- 274990000000003
- Block hashblock_hash
- sim-5v2Q…P8gr
- Transactionhash
- sim-9BEL…rQCd
Checks before sending
- Allowed contract and methodusdc.potprev.testnet · ft_transfer_call
- Deposit within the cap1
- Gas exactly as allowed100 Tgas
- Not expired275010232 ≤ 275010032 + 600
- Nonce is new275010024000002
- Signature is valided25519
- Within the daily limitswithin daily limits
Who paid
Relayer · users.
potprev. testnet ≈ 0.0012 NEAR of gas
1 yoctoNEAR on a token transfer
Pot Hub · pots.
potprev. testnet 1 yoctoNEAR on a token transfer
Leo · u-3p8w2kdn.
users. potprev. testnet Leo: 0 NEAR. Their account has 0 NEAR before and after.
Copied from a real testnet run of the same call. A simulation can't burn gas.
Raw JSON
{ "hash": "sim-9BELGpTzFxNe2aMxNa2zVfGvqWL5rQCd", "signer_id": "users.potprev.testnet", "public_key": "ed25519:3q2rinP5DL1aiB33YTh372bu3K3Tf9kwNyAUjEm4xzF3", "nonce": "274990000000003", "receiver_id": "u-3p8w2kdn.users.potprev.testnet", "block_hash": "sim-5v2QzTJBhMYoiKgzKE8yMDpZhbTCP8gr", "actions": [ { "Delegate": { "tx": "c3.add" } } ], "checks": [ { "check": "allowlist", "value": "usdc.potprev.testnet · ft_transfer_call", "ok": true }, { "check": "deposit_cap", "value": "1", "ok": true }, { "check": "gas", "value": "100 Tgas", "ok": true }, { "check": "expiry", "value": "275010232 ≤ 275010032 + 600", "ok": true }, { "check": "nonce", "value": "275010024000002", "ok": true }, { "check": "signature", "value": "ed25519", "ok": true }, { "check": "quotas", "value": "within daily limits", "ok": true } ]}
- Step 5 of 7
USDC moves the $150 to the Hub and tells it: “this came from Leo, for Lisbon Trip”.
What happened on NEAR, receipt by receipt
Receipts 1–3 of 6sim-9BEL…rQCdSimulated
- ·Phone → RelayerThe signed request goes to our relayer (POST /api/relay), off-chain
- 1Relayer → LeoDelegate: runs Leo's signed request
- 2Leo → USDC (test)ft_transfer_call
(Pot Hub, $150.00)$150.00100 Tgas1 yoctopaid by the relayer - 3USDC (test) → Pot Hubft_on_transfer
(Leo, $150.00)≈ 91 Tgas
- Step 6 of 7
The Hub checks Leo is a member, passes the $150 on to the Pot, and writes down “Leo put in $150”.
What happened on NEAR, receipt by receipt
Receipts 4–6 of 6sim-9BEL…rQCdSimulated
- 4Pot Hub → USDC (test)ft_transfer
(Lisbon Trip, $150.00)$150.0010 Tgas1 yocto - 5Callback from USDC (test) to Pot Hubcallbackon_contribution_forwarded
(Lisbon Trip, Leo, $150.00)10 Tgasreturned "0" - 6Callback from Pot Hub to USDC (test)callbackft_resolve_transfer
(Pot Hub, Leo, $150.00)5 Tgasreturned "150000000"
Show 6 gas refunds
- 7Gas refund to the relayer · 0.01070 NEAR
- 8Gas refund to the relayer · 0.01063 NEAR
- 9Gas refund to the relayer · 0.07030 NEAR
- 10Gas refund to the relayer · 0.00575 NEAR
- 11Gas refund to the relayer · 0.00235 NEAR
- 12Gas refund to the relayer · 0.00046 NEAR
Good to know
On NEAR a transaction can report success while a step inside it failed. Pot's relayer reads every receipt, here the last one: "150000000" used means the money was added.
- 4Pot Hub → USDC (test)ft_transfer
- Step 7 of 7In the app
The Pot is full. Everyone's phone shows it.
After this chapter
What changed
| Field | Before | After |
|---|---|---|
| The Pot's account | a Sputnik DAO, no access keys, 0.05 NEAR float | a Sputnik DAO, no access keys, 0.05 NEAR float |
| Members (DAO role member) | Maya, Sam, Leo | Maya, Sam, Leo |
| Group approvals needed | 2 of 3 | 2 of 3 |
| Proposals | #0 Add member · Approved #1 Add member · Approved | #0 Add member · Approved #1 Add member · Approved |
| Pot status | Open | Open |
| Hub ledger: put in (changed) | Maya $150.00 · Sam $150.00 · Leo $0.00 | Maya $150.00 · Sam $150.00 · Leo $150.00 |
| Hub ledger: spent without asking | Maya $0.00 · Sam $0.00 · Leo $0.00 | Maya $0.00 · Sam $0.00 · Leo $0.00 |
Raw JSON
{ "before": { "pot": { "status": "open", "members": [ { "account": "u-7k2m9qxa.users.potprev.testnet", "joinIndex": 0, "contributed": "150000000", "instantUsed": "0" }, { "account": "u-4hn8c2wd.users.potprev.testnet", "joinIndex": 1, "contributed": "150000000", "instantUsed": "0" }, { "account": "u-3p8w2kdn.users.potprev.testnet", "joinIndex": 2, "contributed": "0", "instantUsed": "0" } ], "ledger": { "totalContributed": "300000000", "spentInstant": "0", "spentVote": "0", "reserved": "0" }, "requests": [] }, "dao": { "exists": true, "members": [ "u-7k2m9qxa.users.potprev.testnet", "u-4hn8c2wd.users.potprev.testnet", "u-3p8w2kdn.users.potprev.testnet" ], "rulebot": [ "pots.potprev.testnet" ], "threshold": { "k": 2, "n": 3 }, "lastProposalId": 2, "proposals": [ { "id": 0, "proposer": "pots.potprev.testnet", "description": "Pot join via invite", "kind": { "AddMemberToRole": { "member_id": "u-4hn8c2wd.users.potprev.testnet", "role": "member" } }, "status": "Approved", "vote_counts": { "rulebot": [ "1", "0", "0" ] }, "votes": { "pots.potprev.testnet": "Approve" }, "submission_time": "1794042306600000000", "last_actions_log": [ { "block_height": "275000511" }, { "block_height": "275000513" } ] }, { "id": 1, "proposer": "pots.potprev.testnet", "description": "Pot join via invite", "kind": { "AddMemberToRole": { "member_id": "u-3p8w2kdn.users.potprev.testnet", "role": "member" } }, "status": "Approved", "vote_counts": { "rulebot": [ "1", "0", "0" ] }, "votes": { "pots.potprev.testnet": "Approve" }, "submission_time": "1794048017400000000", "last_actions_log": [ { "block_height": "275010029" }, { "block_height": "275010031" } ] } ], "floatNear": "0.05" } }, "after": { "pot": { "status": "open", "members": [ { "account": "u-7k2m9qxa.users.potprev.testnet", "joinIndex": 0, "contributed": "150000000", "instantUsed": "0" }, { "account": "u-4hn8c2wd.users.potprev.testnet", "joinIndex": 1, "contributed": "150000000", "instantUsed": "0" }, { "account": "u-3p8w2kdn.users.potprev.testnet", "joinIndex": 2, "contributed": "150000000", "instantUsed": "0" } ], "ledger": { "totalContributed": "450000000", "spentInstant": "0", "spentVote": "0", "reserved": "0" }, "requests": [] }, "dao": { "exists": true, "members": [ "u-7k2m9qxa.users.potprev.testnet", "u-4hn8c2wd.users.potprev.testnet", "u-3p8w2kdn.users.potprev.testnet" ], "rulebot": [ "pots.potprev.testnet" ], "threshold": { "k": 2, "n": 3 }, "lastProposalId": 2, "proposals": [ { "id": 0, "proposer": "pots.potprev.testnet", "description": "Pot join via invite", "kind": { "AddMemberToRole": { "member_id": "u-4hn8c2wd.users.potprev.testnet", "role": "member" } }, "status": "Approved", "vote_counts": { "rulebot": [ "1", "0", "0" ] }, "votes": { "pots.potprev.testnet": "Approve" }, "submission_time": "1794042306600000000", "last_actions_log": [ { "block_height": "275000511" }, { "block_height": "275000513" } ] }, { "id": 1, "proposer": "pots.potprev.testnet", "description": "Pot join via invite", "kind": { "AddMemberToRole": { "member_id": "u-3p8w2kdn.users.potprev.testnet", "role": "member" } }, "status": "Approved", "vote_counts": { "rulebot": [ "1", "0", "0" ] }, "votes": { "pots.potprev.testnet": "Approve" }, "submission_time": "1794048017400000000", "last_actions_log": [ { "block_height": "275010029" }, { "block_height": "275010031" } ] } ], "floatNear": "0.05", "policy": { "roles": [ { "name": "member", "kind": { "Group": [ "u-7k2m9qxa.users.potprev.testnet", "u-4hn8c2wd.users.potprev.testnet", "u-3p8w2kdn.users.potprev.testnet" ] }, "permissions": [ "*:VoteApprove", "*:VoteReject" ], "vote_policy": {} }, { "name": "rulebot", "kind": { "Group": [ "pots.potprev.testnet" ] }, "permissions": [ "transfer:AddProposal", "transfer:VoteApprove", "transfer:Finalize", "add_member_to_role:AddProposal", "add_member_to_role:VoteApprove", "call:AddProposal", "call:Finalize" ], "vote_policy": { "transfer": { "weight_kind": "RoleWeight", "quorum": "1", "threshold": [ 1, 1 ] }, "add_member_to_role": { "weight_kind": "RoleWeight", "quorum": "1", "threshold": [ 1, 1 ] } } } ], "default_vote_policy": { "weight_kind": "RoleWeight", "quorum": "1", "threshold": [ 1, 2 ] }, "proposal_bond": "0", "proposal_period": "604800000000000", "bounty_bond": "0", "bounty_forgiveness_period": "86400000000000" } } }}
Balances
| Account | Before | After | Change |
|---|---|---|---|
| Maya | $0.00 | $0.00 | no change |
| Sam | $0.00 | $0.00 | no change |
| Leo | $150.00 | $0.00 | − $150.00out |
| Lisbon Trip | $300.00 | $450.00 | + $150.00in |
| Pot Hub | $0.00 | $0.00 | no change |
NEAR (fees and storage)
- Leo0 NEAR
- Pot Hub− 1 yoctoNEAR
- Relayer− ≈ 0.00124 NEAR
Copied from a real testnet run of the same call. A simulation can't burn gas.
Raw JSON
{ "before": { "u-7k2m9qxa.users.potprev.testnet": { "usdc": "0", "near": "0" }, "u-4hn8c2wd.users.potprev.testnet": { "usdc": "0", "near": "0" }, "u-3p8w2kdn.users.potprev.testnet": { "usdc": "150000000", "near": "0" }, "pots.potprev.testnet": { "usdc": "0" }, "lisbon-trip-7k2f.pots.potprev.testnet": { "usdc": "300000000" } }, "after": { "u-7k2m9qxa.users.potprev.testnet": { "usdc": "0", "near": "0" }, "u-4hn8c2wd.users.potprev.testnet": { "usdc": "0", "near": "0" }, "u-3p8w2kdn.users.potprev.testnet": { "usdc": "0", "near": "0" }, "pots.potprev.testnet": { "usdc": "0" }, "lisbon-trip-7k2f.pots.potprev.testnet": { "usdc": "450000000" } }}
Who paid
Relayer · users.
potprev. testnet ≈ 0.0012 NEAR of gas
1 yoctoNEAR on a token transfer
Pot Hub · pots.
potprev. testnet 1 yoctoNEAR on a token transfer
Leo · u-3p8w2kdn.
users. potprev. testnet Leo: 0 NEAR. Their account has 0 NEAR before and after.
Copied from a real testnet run of the same call. A simulation can't burn gas.
See a real one on testnet
Maya putting $200 into the reference Pot: the same call as Leo's $150 here.
Real · testnet (opens nearblocks.io in a new tab)HsfSKzGztF3FVHpJHtrxD7TCWVUD4bZVs8XScPN6pNfD
Words in this chapter
10 words
- ft_transfer_call
- Send tokens to a contract and call it in the same step. If the contract refuses, the tokens come back.
- USDC (test)
- Pot's test dollar token on testnet (NEP-141, 6 decimals). It has no value.
- yoctoNEAR
- The smallest unit of NEAR: 10⁻²⁴ NEAR. Token transfers must attach exactly 1, to prove a full-access key signed them.
- deposit
- NEAR attached to a call. In Pot it is 0 everywhere except that 1 yoctoNEAR.
- delegate action
- A request the user signs but doesn't send (NEP-366). Someone else wraps it in a transaction and pays for it; contracts still see the user as the caller.
- relayer
- Pot's server account (users.potprev.testnet) that sends signed requests and pays their gas, so users need 0 NEAR. It refuses anything outside an allowlist.
- Pot Hub
- Pot's own contract (pots.potprev.testnet). It creates each Pot, checks the rules, holds the rulebot seat and keeps the ledger of who put in what.
- receipt
- One step of a transaction's work, run by one account. A transaction fans out into receipts as contracts call each other.
- callback (then)
- A receipt that runs after another finishes and reads its result.
- gas / Tgas
- The work a call may use, bought in NEAR. 1 Tgas is a trillion gas, about 0.0001 NEAR. Unused gas is refunded.
Part 3: Small spends
Nobody voted, yet the DAO paid: the Hub checked the rules, filed proposal #2 and approved it with its rulebot seat, 1 of 1. Then the Pot itself sent Maya $84.
Chapter 4 of 6
$84 of groceries, approved by Pot rules
You're Maya
Nobody voted, yet the DAO approved it: the Hub's seat approves only transfers it created because the rules said yes.
Spend from Lisbon Trip
Approved by Pot rules
Under the $100 instant limit
$84 sent to you
SentUnder the hood
Simulated- 1 signature by a person
- 1 transaction
- 14 receipts
- 0 NEAR paid by people
Where we start
The Pot has $450.00. Maya has used $0 of her $200 without asking. Proposals #0 and #1 are approved joins.
- In the Pot
- $450.00
- Members (DAO role member)
- Maya, Sam, Leo
- Group approvals needed
- 2 of 3
What happens, step by step
- Step 1 of 9In the app
Maya paid $84 for groceries and wants it back. The sheet already says the rules allow it.
- Step 2 of 9In the app
Her phone signs one request to the Hub.
- Step 3 of 9
She asks the Hub for $84 from Lisbon Trip, for groceries. She doesn't ask the group.
What Maya signed
Simulated valuesdevice key · ed255…qWntAsk the Pot Hub to pay me $84.00 from Lisbon Trip for groceries. Attach no money. Valid for the next 200 blocks (about 2 minutes).
- Signersender_id
- u-7k2m9qxa.
users. potprev. testnetMaya's account - Callsreceiver_id
- pots.
potprev. testnetthe Pot Hub: the only contract this can call - Methodactions[0].FunctionCall
- request_spendone call
- Amountargs.amount = "84000000"
- $84.00$84.00 (USDC has 6 decimals)
- Gasgas
- 150 Tgasthe most it may use; the relayer buys it
- Depositdeposit
- 0Maya attaches no money
Show every field
- Noncenonce
- 275000000000003this key's signature counter; a replay fails
- Valid untilmax_block_height
- 275011237valid for about 2 minutes
- Public keypublic_key
- ed25519:6rFmed3Kxeaqp8Ki7oN8Ad3cCg3S8W5sVqXi9eRYqWntMaya's phone key
- Signaturesignature
- ed25519:5pNPUesHfF6R5nev6KVdSTDAAMCBYH4uqbsHCniNdb8obiNYV2PUteWHAvxGijJ1ZNMWGuhiiuqQKmrZQRPQQBQe
- Argumentspot
- lisbon-trip-7k2f.
pots. potprev. testnetLisbon Trip - Argumentsamount
- 84000000$84.00 (USDC has 6 decimals)
- Argumentsreceiver_id
- u-7k2m9qxa.
users. potprev. testnetMaya - Argumentscategory
- groceries
- Argumentsrequest_ref
- k3d9x1a2b4c6m7n8her phone's id for this request. The note stays off-chain in Pot's database, because chain data is public.
- How it's signedNEP-366
- 21GgmVgAdcuHqEwfu6uZpdvqaiWvpWFYNUmHVYMsuzRxsha256(6e 01 00 40 ‖ borsh(DelegateAction)), then ed25519. The 4-byte NEP-366 prefix makes it unusable as an ordinary transaction.
Raw JSON
{ "delegate_action": { "sender_id": "u-7k2m9qxa.users.potprev.testnet", "receiver_id": "pots.potprev.testnet", "actions": [ { "FunctionCall": { "method_name": "request_spend", "args": { "pot": "lisbon-trip-7k2f.pots.potprev.testnet", "amount": "84000000", "receiver_id": "u-7k2m9qxa.users.potprev.testnet", "category": "groceries", "request_ref": "k3d9x1a2b4c6m7n8" }, "args_base64": "eyJwb3QiOiJsaXNib24tdHJpcC03azJmLnBvdHMucG90cHJldi50ZXN0bmV0IiwiYW1vdW50IjoiODQwMDAwMDAiLCJyZWNlaXZlcl9pZCI6InUtN2sybTlxeGEudXNlcnMucG90cHJldi50ZXN0bmV0IiwiY2F0ZWdvcnkiOiJncm9jZXJpZXMiLCJyZXF1ZXN0X3JlZiI6ImszZDl4MWEyYjRjNm03bjgifQ==", "gas": "150000000000000", "deposit": "0" } } ], "nonce": "275000000000003", "max_block_height": "275011237", "public_key": "ed25519:6rFmed3Kxeaqp8Ki7oN8Ad3cCg3S8W5sVqXi9eRYqWnt" }, "signature": "ed25519:5pNPUesHfF6R5nev6KVdSTDAAMCBYH4uqbsHCniNdb8obiNYV2PUteWHAvxGijJ1ZNMWGuhiiuqQKmrZQRPQQBQe"}
What the app posts to our relayer
IAAAAHUtN2sybTlxeGEudXNlcnMucG90cHJldi50ZXN0bmV0FAAAAHBvdHMucG90cHJldi50ZXN0bmV0AQAAAAINAAAAcmVxdWVzdF9zcGVuZKwAAAB7InBvdCI6Imxpc2Jvbi10cmlwLTdrMmYucG90cy5wb3RwcmV2LnRlc3RuZXQiLCJhbW91bnQiOiI4NDAwMDAwMCIsInJlY2VpdmVyX2lkIjoidS03azJtOXF4YS51c2Vycy5wb3RwcmV2LnRlc3RuZXQiLCJjYXRlZ29yeSI6Imdyb2NlcmllcyIsInJlcXVlc3RfcmVmIjoiazNkOXgxYTJiNGM2bTduOCJ9AGC3mGyIAAAAAAAAAAAAAAAAAAAAAAAAAzBQbRz6AAClVmQQAAAAAABW6Jg0QJa/kB5CJgNlnFrkEGe+cCHlfphaWqR1NLfbKQDw6sDmdLP1Cv6UV4RZ39pa9Io5ONeeRZHmb4ZahMd51f343LU8O8XkPvlfIt1Vc+BiqouB32EgUCMQBdriB3wL
- Step 4 of 9
The relayer sends it and pays. Maya: still 0 NEAR.
What our relayer sent
Simulated valuesThe relayer's transaction
- Signersigner_id
- users.
potprev. testnetRelayer - Receiverreceiver_id
- u-7k2m9qxa.
users. potprev. testnetMaya - Noncenonce
- 274990000000004
- Block hashblock_hash
- sim-E6kT…XeAU
- Transactionhash
- sim-DabH…zpCo
Checks before sending
- Allowed contract and methodpots.potprev.testnet · request_spend
- Deposit within the cap0
- Gas exactly as allowed150 Tgas
- Not expired275011237 ≤ 275011037 + 600
- Nonce is new275000000000003
- Signature is valided25519
- Within the daily limitswithin daily limits
Who paid
Relayer · users.
potprev. testnet ≈ 0.0030 NEAR of gas
Pot Hub · pots.
potprev. testnet 0.00586 NEAR storage prepay for the new proposal
Lisbon Trip · lisbon-trip-7k2f.
pots. potprev. testnet 1 yoctoNEAR on a token transfer
Maya · u-7k2m9qxa.
users. potprev. testnet Maya: 0 NEAR. Their account has 0 NEAR before and after.
Copied from a real testnet run of the same call. A simulation can't burn gas.
Raw JSON
{ "hash": "sim-DabHMGErVFmYeoTj1ev4vZwUfcD3zpCo", "signer_id": "users.potprev.testnet", "public_key": "ed25519:3q2rinP5DL1aiB33YTh372bu3K3Tf9kwNyAUjEm4xzF3", "nonce": "274990000000004", "receiver_id": "u-7k2m9qxa.users.potprev.testnet", "block_hash": "sim-E6kTTeKcbQtfWx7riyXz9K1vShNHXeAU", "actions": [ { "Delegate": { "tx": "c4.request" } } ], "checks": [ { "check": "allowlist", "value": "pots.potprev.testnet · request_spend", "ok": true }, { "check": "deposit_cap", "value": "0", "ok": true }, { "check": "gas", "value": "150 Tgas", "ok": true }, { "check": "expiry", "value": "275011237 ≤ 275011037 + 600", "ok": true }, { "check": "nonce", "value": "275000000000003", "ok": true }, { "check": "signature", "value": "ed25519", "ok": true }, { "check": "quotas", "value": "within daily limits", "ok": true } ]}
What happened on NEAR, receipt by receipt
Receipts 1–2 of 14sim-DabH…zpCoSimulated
- ·Phone → RelayerThe signed request goes to our relayer (POST /api/relay), off-chain
- 1Relayer → MayaDelegate: runs Maya's signed request
- 2Maya → Pot Hubrequest_spend
(Lisbon Trip, Maya, $84.00)150 Tgas
- Step 5 of 9
The Hub checks the rules: $84 is under $100, Maya has $200 left, she's paying herself and she's a member who put money in. It also reads the Pot's real balance.
How the Hub checked the rules
Check Value Result The amount is within the instant limitinstant_cap $84 ≤ $100 Yes It fits the $200 per person without askingmember_budget $0 + $84 ≤ $200 Yes The money goes to a memberinstant_payees member Yes The requester has put money incontributed $150 Yes Path: instant (within_rules) What happened on NEAR, receipt by receipt
Receipts 3–6 of 14sim-DabH…zpCoSimulated
- 3Pot Hub → USDC (test)ft_balance_of
(Lisbon Trip)5 Tgasin parallelreturned "450000000" - 4Pot Hub → USDC (test)storage_balance_of
(Maya)5 Tgasin parallelreturned 0.00125 NEAR - 5Pot Hub → Lisbon Tripget_policy10 Tgasin parallelreturned policy
- 6Callback from USDC (test) to Pot Hubcallbackon_spend_checked
(Lisbon Trip)105 Tgas
- 3Pot Hub → USDC (test)ft_balance_of
- Step 6 of 9
Inside the rules, the Hub files a payout request in the Pot: $84 to Maya.
In progressProposal #2 in Lisbon Tripa Sputnik DAO
Transfer · $84.00 USDC (test) → Maya
Pot request #0: groceries (ref k3d9x1a2b4c6m7n8)
· filed by Pot HubWho can decide this
Members (3)
weren't asked
Pot rules seat (1)- Pot Hub not yet
- needs 1
How “1” is computed
Role rulebot, 1 seat (the Pot Hub), threshold [1, 1] for transfers and new members: min(1 × 1 ÷ 1 + 1, 1) = 1. The member role was never asked.
Raw JSON
{ "id": 2, "proposer": "pots.potprev.testnet", "description": "Pot request #0: groceries (ref k3d9x1a2b4c6m7n8)", "kind": { "Transfer": { "token_id": "usdc.potprev.testnet", "receiver_id": "u-7k2m9qxa.users.potprev.testnet", "amount": "84000000", "msg": null } }, "status": "InProgress", "vote_counts": {}, "votes": {}, "submission_time": "1794048625200000000", "last_actions_log": [ { "block_height": "275011042" } ]}
What happened on NEAR, receipt by receipt
Receipts 7–8 of 14sim-DabH…zpCoSimulated
- 7Pot Hub → Lisbon Tripone receipt, 2 actionsTransfer 0.00586 NEARadd_proposal10 Tgasreturned 2
- 8Callback from Lisbon Trip to Pot Hubcallbackon_spend_added
(Lisbon Trip)80 Tgas
- Step 7 of 9In the app
The Hub approves with its seat. That one vote is enough, because the rules said yes.
ApprovedProposal #2 in Lisbon Tripa Sputnik DAO
Transfer · $84.00 USDC (test) → Maya
Pot request #0: groceries (ref k3d9x1a2b4c6m7n8)
· filed by Pot HubWho can decide this
Members (3)
weren't asked
Pot rules seat (1)- Pot Hub approved
- needs 1
How “1” is computed
Role rulebot, 1 seat (the Pot Hub), threshold [1, 1] for transfers and new members: min(1 × 1 ÷ 1 + 1, 1) = 1. The member role was never asked.
Raw JSON
{ "id": 2, "proposer": "pots.potprev.testnet", "description": "Pot request #0: groceries (ref k3d9x1a2b4c6m7n8)", "kind": { "Transfer": { "token_id": "usdc.potprev.testnet", "receiver_id": "u-7k2m9qxa.users.potprev.testnet", "amount": "84000000", "msg": null } }, "status": "Approved", "vote_counts": { "rulebot": [ "1", "0", "0" ] }, "votes": { "pots.potprev.testnet": "Approve" }, "submission_time": "1794048625200000000", "last_actions_log": [ { "block_height": "275011042" }, { "block_height": "275011046" } ]}
Good to know
Sputnik itself enforces no limit: the Hub's seat could approve any transfer. The limits live in the Hub's code, which only votes on requests it filed a moment earlier. The Hub has no keys (it was locked), so nobody can change that code.
What happened on NEAR, receipt by receipt
Receipt 9 of 14sim-DabH…zpCoSimulated
- 9Pot Hub → Lisbon Tripact_proposal
(#2, VoteApprove)40 Tgas
- Step 8 of 9
The Pot itself sends Maya $84. The Hub reads the result back to be sure.
What happened on NEAR, receipt by receipt
Receipts 10–14 of 14sim-DabH…zpCoSimulated
- 10Lisbon Trip → USDC (test)ft_transfer
(Maya, $84.00)$84.00≈ 18 Tgas1 yocto - 11Callback from USDC (test) to Lisbon Tripcallbackon_proposal_callback
(#2)≈ 18 Tgas - 12Lisbon Trip → Pot HubTransfer 0
- 13Pot Hub → Lisbon Tripget_proposal
(#2)10 Tgasreturned #2 Approved - 14Callback from Lisbon Trip to Pot Hubcallbackon_spend_status
(Lisbon Trip)10 Tgasreturned status approved
Show 14 gas refunds
- 15Gas refund to the relayer · 0.00576 NEAR
- 16Gas refund to the relayer · 0.00576 NEAR
- 17Gas refund to the relayer · 0.01071 NEAR
- 18Gas refund to the relayer · 0.01078 NEAR
- 19Gas refund to the relayer · 0.01872 NEAR
- 20Gas refund to the relayer · 0.00020 NEAR
- 21Gas refund to the relayer · 0.01845 NEAR
- 22Gas refund to the relayer · 0.00275 NEAR
- 23Gas refund to the relayer · 0.01072 NEAR
- 24Gas refund to the relayer · 0.01063 NEAR
- 25Gas refund to the relayer · 0.01783 NEAR
- 26Gas refund to the relayer · 0.01365 NEAR
- 27Gas refund to the relayer · 0.02193 NEAR
- 28Gas refund to the relayer · 0.00046 NEAR
- 10Lisbon Trip → USDC (test)ft_transfer
- Step 9 of 9In the app
About five seconds after her tap, the money is hers.
Balances
Dollars Account Before After Change Maya $0.00 $84.00 + $84.00in Sam $0.00 $0.00 no change Leo $0.00 $0.00 no change Lisbon Trip $450.00 $366.00 − $84.00out Pot Hub $0.00 $0.00 no change NEAR (fees and storage)
- Maya0 NEAR
- Lisbon Trip− 1 yoctoNEAR
- Pot Hub− 0.00586 NEAR
- Relayer− ≈ 0.00305 NEAR
Copied from a real testnet run of the same call. A simulation can't burn gas.
Raw JSON
{ "before": { "u-7k2m9qxa.users.potprev.testnet": { "usdc": "0", "near": "0" }, "u-4hn8c2wd.users.potprev.testnet": { "usdc": "0", "near": "0" }, "u-3p8w2kdn.users.potprev.testnet": { "usdc": "0", "near": "0" }, "pots.potprev.testnet": { "usdc": "0" }, "lisbon-trip-7k2f.pots.potprev.testnet": { "usdc": "450000000" } }, "after": { "u-7k2m9qxa.users.potprev.testnet": { "usdc": "84000000", "near": "0" }, "u-4hn8c2wd.users.potprev.testnet": { "usdc": "0", "near": "0" }, "u-3p8w2kdn.users.potprev.testnet": { "usdc": "0", "near": "0" }, "pots.potprev.testnet": { "usdc": "0" }, "lisbon-trip-7k2f.pots.potprev.testnet": { "usdc": "366000000" } }}
After this chapter
What changed
| Field | Before | After |
|---|---|---|
| The Pot's account | a Sputnik DAO, no access keys, 0.05 NEAR float | a Sputnik DAO, no access keys, 0.05 NEAR float |
| Members (DAO role member) | Maya, Sam, Leo | Maya, Sam, Leo |
| Group approvals needed | 2 of 3 | 2 of 3 |
| Proposals (changed) | #0 Add member · Approved #1 Add member · Approved | #0 Add member · Approved #1 Add member · Approved #2 Transfer · Approved |
| Pot status | Open | Open |
| Hub ledger: put in | Maya $150.00 · Sam $150.00 · Leo $150.00 | Maya $150.00 · Sam $150.00 · Leo $150.00 |
| Hub ledger: spent without asking (changed) | Maya $0.00 · Sam $0.00 · Leo $0.00 | Maya $84.00 · Sam $0.00 · Leo $0.00 |
Raw JSON
{ "before": { "pot": { "status": "open", "members": [ { "account": "u-7k2m9qxa.users.potprev.testnet", "joinIndex": 0, "contributed": "150000000", "instantUsed": "0" }, { "account": "u-4hn8c2wd.users.potprev.testnet", "joinIndex": 1, "contributed": "150000000", "instantUsed": "0" }, { "account": "u-3p8w2kdn.users.potprev.testnet", "joinIndex": 2, "contributed": "150000000", "instantUsed": "0" } ], "ledger": { "totalContributed": "450000000", "spentInstant": "0", "spentVote": "0", "reserved": "0" }, "requests": [] }, "dao": { "exists": true, "members": [ "u-7k2m9qxa.users.potprev.testnet", "u-4hn8c2wd.users.potprev.testnet", "u-3p8w2kdn.users.potprev.testnet" ], "rulebot": [ "pots.potprev.testnet" ], "threshold": { "k": 2, "n": 3 }, "lastProposalId": 2, "proposals": [ { "id": 0, "proposer": "pots.potprev.testnet", "description": "Pot join via invite", "kind": { "AddMemberToRole": { "member_id": "u-4hn8c2wd.users.potprev.testnet", "role": "member" } }, "status": "Approved", "vote_counts": { "rulebot": [ "1", "0", "0" ] }, "votes": { "pots.potprev.testnet": "Approve" }, "submission_time": "1794042306600000000", "last_actions_log": [ { "block_height": "275000511" }, { "block_height": "275000513" } ] }, { "id": 1, "proposer": "pots.potprev.testnet", "description": "Pot join via invite", "kind": { "AddMemberToRole": { "member_id": "u-3p8w2kdn.users.potprev.testnet", "role": "member" } }, "status": "Approved", "vote_counts": { "rulebot": [ "1", "0", "0" ] }, "votes": { "pots.potprev.testnet": "Approve" }, "submission_time": "1794048017400000000", "last_actions_log": [ { "block_height": "275010029" }, { "block_height": "275010031" } ] } ], "floatNear": "0.05" } }, "after": { "pot": { "status": "open", "members": [ { "account": "u-7k2m9qxa.users.potprev.testnet", "joinIndex": 0, "contributed": "150000000", "instantUsed": "84000000" }, { "account": "u-4hn8c2wd.users.potprev.testnet", "joinIndex": 1, "contributed": "150000000", "instantUsed": "0" }, { "account": "u-3p8w2kdn.users.potprev.testnet", "joinIndex": 2, "contributed": "150000000", "instantUsed": "0" } ], "ledger": { "totalContributed": "450000000", "spentInstant": "84000000", "spentVote": "0", "reserved": "0" }, "requests": [ { "id": 0, "path": "instant", "status": "approved", "amount": "84000000", "proposalId": 2, "category": "groceries", "requestRef": "k3d9x1a2b4c6m7n8" } ] }, "dao": { "exists": true, "members": [ "u-7k2m9qxa.users.potprev.testnet", "u-4hn8c2wd.users.potprev.testnet", "u-3p8w2kdn.users.potprev.testnet" ], "rulebot": [ "pots.potprev.testnet" ], "threshold": { "k": 2, "n": 3 }, "lastProposalId": 3, "proposals": [ { "id": 0, "proposer": "pots.potprev.testnet", "description": "Pot join via invite", "kind": { "AddMemberToRole": { "member_id": "u-4hn8c2wd.users.potprev.testnet", "role": "member" } }, "status": "Approved", "vote_counts": { "rulebot": [ "1", "0", "0" ] }, "votes": { "pots.potprev.testnet": "Approve" }, "submission_time": "1794042306600000000", "last_actions_log": [ { "block_height": "275000511" }, { "block_height": "275000513" } ] }, { "id": 1, "proposer": "pots.potprev.testnet", "description": "Pot join via invite", "kind": { "AddMemberToRole": { "member_id": "u-3p8w2kdn.users.potprev.testnet", "role": "member" } }, "status": "Approved", "vote_counts": { "rulebot": [ "1", "0", "0" ] }, "votes": { "pots.potprev.testnet": "Approve" }, "submission_time": "1794048017400000000", "last_actions_log": [ { "block_height": "275010029" }, { "block_height": "275010031" } ] }, { "id": 2, "proposer": "pots.potprev.testnet", "description": "Pot request #0: groceries (ref k3d9x1a2b4c6m7n8)", "kind": { "Transfer": { "token_id": "usdc.potprev.testnet", "receiver_id": "u-7k2m9qxa.users.potprev.testnet", "amount": "84000000", "msg": null } }, "status": "Approved", "vote_counts": { "rulebot": [ "1", "0", "0" ] }, "votes": { "pots.potprev.testnet": "Approve" }, "submission_time": "1794048625200000000", "last_actions_log": [ { "block_height": "275011042" }, { "block_height": "275011046" } ] } ], "floatNear": "0.05", "policy": { "roles": [ { "name": "member", "kind": { "Group": [ "u-7k2m9qxa.users.potprev.testnet", "u-4hn8c2wd.users.potprev.testnet", "u-3p8w2kdn.users.potprev.testnet" ] }, "permissions": [ "*:VoteApprove", "*:VoteReject" ], "vote_policy": {} }, { "name": "rulebot", "kind": { "Group": [ "pots.potprev.testnet" ] }, "permissions": [ "transfer:AddProposal", "transfer:VoteApprove", "transfer:Finalize", "add_member_to_role:AddProposal", "add_member_to_role:VoteApprove", "call:AddProposal", "call:Finalize" ], "vote_policy": { "transfer": { "weight_kind": "RoleWeight", "quorum": "1", "threshold": [ 1, 1 ] }, "add_member_to_role": { "weight_kind": "RoleWeight", "quorum": "1", "threshold": [ 1, 1 ] } } } ], "default_vote_policy": { "weight_kind": "RoleWeight", "quorum": "1", "threshold": [ 1, 2 ] }, "proposal_bond": "0", "proposal_period": "604800000000000", "bounty_bond": "0", "bounty_forgiveness_period": "86400000000000" } } }}
Who paid
Relayer · users.
potprev. testnet ≈ 0.0030 NEAR of gas
Pot Hub · pots.
potprev. testnet 0.00586 NEAR storage prepay for the new proposal
Lisbon Trip · lisbon-trip-7k2f.
pots. potprev. testnet 1 yoctoNEAR on a token transfer
Maya · u-7k2m9qxa.
users. potprev. testnet Maya: 0 NEAR. Their account has 0 NEAR before and after.
Copied from a real testnet run of the same call. A simulation can't burn gas.
See a real one on testnet
A $40 instant spend in the reference Pot, approved by Pot rules alone, as the $84 is here. Its hub is newer than this simulation, so its request event and its last receipt's answer also say the spend's tier and whether it is a pay link.
Real · testnet (opens nearblocks.io in a new tab)AcobWgTJZMyosrP1nEhiE5YB1UCfZtkXcSwuFwEtXzke
Words in this chapter
11 words
- DAO
- A shared account run by rules instead of a person. Here every Pot is one, and its members approve what it does.
- proposal
- A request filed in the DAO, like “pay Maya $84” or “add Leo”. It runs only once enough seats approve it.
- rulebot
- The Pot Hub's seat in every Pot. One vote from it approves transfers and new members, and the Hub only casts it when the Pot's rules allow.
- Pot Hub
- Pot's own contract (pots.potprev.testnet). It creates each Pot, checks the rules, holds the rulebot seat and keeps the ledger of who put in what.
- threshold
- How many approvals a role needs. Members need a majority: 2 of 3 here.
- delegate action
- A request the user signs but doesn't send (NEP-366). Someone else wraps it in a transaction and pays for it; contracts still see the user as the caller.
- relayer
- Pot's server account (users.potprev.testnet) that sends signed requests and pays their gas, so users need 0 NEAR. It refuses anything outside an allowlist.
- receipt
- One step of a transaction's work, run by one account. A transaction fans out into receipts as contracts call each other.
- callback (then)
- A receipt that runs after another finishes and reads its result.
- storage prepay
- NEAR the Hub sends with each proposal, so the Pot can pay for the bytes it stores.
- gas / Tgas
- The work a call may use, bought in NEAR. 1 Tgas is a trillion gas, about 0.0001 NEAR. Unused gas is refunded.
Part 4: Bigger spends
Today Leo's OK goes to the Hub, which approves with its rulebot seat (the first real link below). The steps below show the other path, the one spends over the one-OK limit take: Maya's and Leo's approvals are DAO votes (2 of 3).
Chapter 5 of 6
A $300 boat trip goes to the group
Now you're Leo
Above the one-OK limit the Hub can only file the request. Two of the three friends must approve it in the DAO itself.
Maya asks for $300
Lisbon Trip · now
Done. Your OK sent $300 to Maya.
One OK needed
Maya · asked, approval counted
You · gave the OK
Under the hood
Simulated- 3 signatures by people
- 4 transactions
- 18 receipts
- 0 NEAR paid by people
Where we start
The Pot has $366.00 and three members, so group approvals need 2 of 3.
- In the Pot
- $366.00
- Members (DAO role member)
- Maya, Sam, Leo
- Group approvals needed
- 2 of 3
What happens, step by step
- Step 1 of 10In the app
Maya wants $300 for the boat. That's over the limit, so the app says the group decides.
- Step 2 of 10In the app
She signs the same kind of request as for groceries.
- Step 3 of 10
The Hub files it as proposal #3 and stops. It has no seat that can approve this.
What Maya signed
Simulated valuesdevice key · ed255…qWntAsk the Pot Hub to pay me $300.00 from Lisbon Trip for tickets. Attach no money. Valid for the next 200 blocks (about 2 minutes).
- Signersender_id
- u-7k2m9qxa.
users. potprev. testnetMaya's account - Callsreceiver_id
- pots.
potprev. testnetthe Pot Hub: the only contract this can call - Methodactions[0].FunctionCall
- request_spendone call
- Amountargs.amount = "300000000"
- $300.00$300.00 (USDC has 6 decimals)
- Gasgas
- 150 Tgasthe most it may use; the relayer buys it
- Depositdeposit
- 0Maya attaches no money
Show every field
- Noncenonce
- 275000000000004this key's signature counter; a replay fails
- Valid untilmax_block_height
- 275012247valid for about 2 minutes
- Public keypublic_key
- ed25519:6rFmed3Kxeaqp8Ki7oN8Ad3cCg3S8W5sVqXi9eRYqWntMaya's phone key
- Signaturesignature
- ed25519:4v6FoBvUFNrZqJjcwg2ANoYGaXBXczJj5KTJNAyyfFc9cL8wH8KcrG8zmgtDdVxVvXubW91yxV14mc1paPJbDif9
- Argumentspot
- lisbon-trip-7k2f.
pots. potprev. testnetLisbon Trip - Argumentsamount
- 300000000$300.00 (USDC has 6 decimals)
- Argumentsreceiver_id
- u-7k2m9qxa.
users. potprev. testnetMaya - Argumentscategory
- tickets
- Argumentsrequest_ref
- p8w2m4q6z1x3r5t7her phone's id for this request. The note stays off-chain in Pot's database, because chain data is public.
- How it's signedNEP-366
- 7zBXH4JkKPZEpaz4i4QhkRoPdR1D69KUX7ABS41PsUGfsha256(6e 01 00 40 ‖ borsh(DelegateAction)), then ed25519. The 4-byte NEP-366 prefix makes it unusable as an ordinary transaction.
Raw JSON
{ "delegate_action": { "sender_id": "u-7k2m9qxa.users.potprev.testnet", "receiver_id": "pots.potprev.testnet", "actions": [ { "FunctionCall": { "method_name": "request_spend", "args": { "pot": "lisbon-trip-7k2f.pots.potprev.testnet", "amount": "300000000", "receiver_id": "u-7k2m9qxa.users.potprev.testnet", "category": "tickets", "request_ref": "p8w2m4q6z1x3r5t7" }, "args_base64": "eyJwb3QiOiJsaXNib24tdHJpcC03azJmLnBvdHMucG90cHJldi50ZXN0bmV0IiwiYW1vdW50IjoiMzAwMDAwMDAwIiwicmVjZWl2ZXJfaWQiOiJ1LTdrMm05cXhhLnVzZXJzLnBvdHByZXYudGVzdG5ldCIsImNhdGVnb3J5IjoidGlja2V0cyIsInJlcXVlc3RfcmVmIjoicDh3Mm00cTZ6MXgzcjV0NyJ9", "gas": "150000000000000", "deposit": "0" } } ], "nonce": "275000000000004", "max_block_height": "275012247", "public_key": "ed25519:6rFmed3Kxeaqp8Ki7oN8Ad3cCg3S8W5sVqXi9eRYqWnt" }, "signature": "ed25519:4v6FoBvUFNrZqJjcwg2ANoYGaXBXczJj5KTJNAyyfFc9cL8wH8KcrG8zmgtDdVxVvXubW91yxV14mc1paPJbDif9"}
What the app posts to our relayer
IAAAAHUtN2sybTlxeGEudXNlcnMucG90cHJldi50ZXN0bmV0FAAAAHBvdHMucG90cHJldi50ZXN0bmV0AQAAAAINAAAAcmVxdWVzdF9zcGVuZKsAAAB7InBvdCI6Imxpc2Jvbi10cmlwLTdrMmYucG90cy5wb3RwcmV2LnRlc3RuZXQiLCJhbW91bnQiOiIzMDAwMDAwMDAiLCJyZWNlaXZlcl9pZCI6InUtN2sybTlxeGEudXNlcnMucG90cHJldi50ZXN0bmV0IiwiY2F0ZWdvcnkiOiJ0aWNrZXRzIiwicmVxdWVzdF9yZWYiOiJwOHcybTRxNnoxeDNyNXQ3In0AYLeYbIgAAAAAAAAAAAAAAAAAAAAAAAAEMFBtHPoAAJdaZBAAAAAAAFbomDRAlr+QHkImA2WcWuQQZ75wIeV+mFpapHU0t9spAMPVldwnM6Icp8DpJ10GbCIctPD+mMuBOY82kSVjlZrljnAFDhYPVAJzW0Rl1ZrRrmlVcpLTU07KeID0zK9gOAg=
How the Hub checked the rules
Check Value Result The amount is within the instant limitinstant_capthe rule that decided $300 > $100 → the group Path: the group (over_cap) In progressProposal #3 in Lisbon Tripa Sputnik DAO
Transfer · $300.00 USDC (test) → Maya
Pot request #1: tickets (ref p8w2m4q6z1x3r5t7)
· filed by Pot HubWho can decide this
Members (3)
0 of 2 approvals- Maya not yet
- Sam not yetneeds 2
- Leo not yet
Pot rules seat (1)can't vote outside the rulesHow “2” is computed
Role member, a group of 3, vote_policy default [1, 2], quorum "1": threshold = max(quorum, min(3 × 1 ÷ 2 + 1, 3)) = 2 (whole-number division). A single role reaching its threshold decides. The threshold follows the current group size, so a new member raises it for open requests.
Raw JSON
{ "id": 3, "proposer": "pots.potprev.testnet", "description": "Pot request #1: tickets (ref p8w2m4q6z1x3r5t7)", "kind": { "Transfer": { "token_id": "usdc.potprev.testnet", "receiver_id": "u-7k2m9qxa.users.potprev.testnet", "amount": "300000000", "msg": null } }, "status": "InProgress", "vote_counts": {}, "votes": {}, "submission_time": "1794049231200000000", "last_actions_log": [ { "block_height": "275012052" } ]}
What happened on NEAR, receipt by receipt
Receipts 1–8 of 8sim-6goM…HGFCSimulated
- ·Phone → RelayerThe signed request goes to our relayer (POST /api/relay), off-chain
- 1Relayer → MayaDelegate: runs Maya's signed request
- 2Maya → Pot Hubrequest_spend
(Lisbon Trip, Maya, $300.00)150 Tgas - 3Pot Hub → USDC (test)ft_balance_of
(Lisbon Trip)5 Tgasin parallelreturned "366000000" - 4Pot Hub → USDC (test)storage_balance_of
(Maya)5 Tgasin parallelreturned 0.00125 NEAR - 5Pot Hub → Lisbon Tripget_policy10 Tgasin parallelreturned policy
- 6Callback from USDC (test) to Pot Hubcallbackon_spend_checked
(Lisbon Trip)105 Tgas - 7Pot Hub → Lisbon Tripone receipt, 2 actionsTransfer 0.00885 NEARadd_proposal10 Tgasreturned 3
- 8Callback from Lisbon Trip to Pot Hubcallbackon_spend_added
(Lisbon Trip)80 Tgasreturned status pending
Show 8 gas refunds
- 9Gas refund to the relayer · 0.00576 NEAR
- 10Gas refund to the relayer · 0.00576 NEAR
- 11Gas refund to the relayer · 0.01070 NEAR
- 12Gas refund to the relayer · 0.01078 NEAR
- 13Gas refund to the relayer · 0.08061 NEAR
- 14Gas refund to the relayer · 0.01365 NEAR
- 15Gas refund to the relayer · 0.02193 NEAR
- 16Gas refund to the relayer · 0.00046 NEAR
- Step 4 of 10
Her phone immediately adds her own approval. It sends the exact request she saw, so she can't approve something else by mistake.
What Maya signed
Simulated valuesdevice key · ed255…qWntTell Lisbon Trip I approve proposal #3, exactly as it was filed. Attach no money. Valid for about 2 minutes.
- Signersender_id
- u-7k2m9qxa.
users. potprev. testnetMaya's account - Callsreceiver_id
- lisbon-trip-7k2f.
pots. potprev. testnetthe Pot's own DAO, not the Hub - Methodactions[0].FunctionCall
- act_proposalone call
- Gasgas
- 100 Tgasthe most it may use; the relayer buys it
- Depositdeposit
- 0Maya attaches no money
Show every field
- Noncenonce
- 275000000000005this key's signature counter; a replay fails
- Valid untilmax_block_height
- 275012252valid for about 2 minutes
- Public keypublic_key
- ed25519:6rFmed3Kxeaqp8Ki7oN8Ad3cCg3S8W5sVqXi9eRYqWntMaya's phone key
- Signaturesignature
- ed25519:2kpaSKn16uLuyvSbNXnTFd3XZLDAu1S4zs8Sh8ZyyoQVWHHFpXU3cnWL3PVmrs3ykkzAAWQocEJnZU9XBV8WGUWh
- Argumentsid
- 3
- Argumentsaction
- VoteApprove
- Argumentsproposal
- {"Transfer":{"token_id":"usdc.potprev.testnet","receiver_id":"u-7k2m9qxa.users.potprev.testnet","amount":"300000000","msg":null}}the DAO rejects the vote unless this equals the stored request exactly (ERR_WRONG_KIND).
- How it's signedNEP-366
- 5g4yFf5twLVyH6iqWuGqZCHhb1x5RSwo1Q8gQZf7np22sha256(6e 01 00 40 ‖ borsh(DelegateAction)), then ed25519. The 4-byte NEP-366 prefix makes it unusable as an ordinary transaction.
Raw JSON
{ "delegate_action": { "sender_id": "u-7k2m9qxa.users.potprev.testnet", "receiver_id": "lisbon-trip-7k2f.pots.potprev.testnet", "actions": [ { "FunctionCall": { "method_name": "act_proposal", "args": { "id": 3, "action": "VoteApprove", "proposal": { "Transfer": { "token_id": "usdc.potprev.testnet", "receiver_id": "u-7k2m9qxa.users.potprev.testnet", "amount": "300000000", "msg": null } } }, "args_base64": "eyJpZCI6MywiYWN0aW9uIjoiVm90ZUFwcHJvdmUiLCJwcm9wb3NhbCI6eyJUcmFuc2ZlciI6eyJ0b2tlbl9pZCI6InVzZGMucG90cHJldi50ZXN0bmV0IiwicmVjZWl2ZXJfaWQiOiJ1LTdrMm05cXhhLnVzZXJzLnBvdHByZXYudGVzdG5ldCIsImFtb3VudCI6IjMwMDAwMDAwMCIsIm1zZyI6bnVsbH19fQ==", "gas": "100000000000000", "deposit": "0" } } ], "nonce": "275000000000005", "max_block_height": "275012252", "public_key": "ed25519:6rFmed3Kxeaqp8Ki7oN8Ad3cCg3S8W5sVqXi9eRYqWnt" }, "signature": "ed25519:2kpaSKn16uLuyvSbNXnTFd3XZLDAu1S4zs8Sh8ZyyoQVWHHFpXU3cnWL3PVmrs3ykkzAAWQocEJnZU9XBV8WGUWh"}
What the app posts to our relayer
IAAAAHUtN2sybTlxeGEudXNlcnMucG90cHJldi50ZXN0bmV0JQAAAGxpc2Jvbi10cmlwLTdrMmYucG90cy5wb3RwcmV2LnRlc3RuZXQBAAAAAgwAAABhY3RfcHJvcG9zYWysAAAAeyJpZCI6MywiYWN0aW9uIjoiVm90ZUFwcHJvdmUiLCJwcm9wb3NhbCI6eyJUcmFuc2ZlciI6eyJ0b2tlbl9pZCI6InVzZGMucG90cHJldi50ZXN0bmV0IiwicmVjZWl2ZXJfaWQiOiJ1LTdrMm05cXhhLnVzZXJzLnBvdHByZXYudGVzdG5ldCIsImFtb3VudCI6IjMwMDAwMDAwMCIsIm1zZyI6bnVsbH19fQBAehDzWgAAAAAAAAAAAAAAAAAAAAAAAAUwUG0c+gAAnFpkEAAAAAAAVuiYNECWv5AeQiYDZZxa5BBnvnAh5X6YWlqkdTS32ykAV85Sn+4Z5/Ovgw6G3y3TVuZ7wcS9/rTfUQ4wL9T1pUOQGj95LEFNGaPJCN8931ig2GAt3PzeB3FE0Jg5RscmDg==
In progressProposal #3 in Lisbon Tripa Sputnik DAO
Transfer · $300.00 USDC (test) → Maya
Pot request #1: tickets (ref p8w2m4q6z1x3r5t7)
· filed by Pot HubWho can decide this
Members (3)
1 of 2 approvals- Maya approved
- Sam not yetneeds 2
- Leo not yet
Pot rules seat (1)can't vote outside the rulesHow “2” is computed
Role member, a group of 3, vote_policy default [1, 2], quorum "1": threshold = max(quorum, min(3 × 1 ÷ 2 + 1, 3)) = 2 (whole-number division). A single role reaching its threshold decides. The threshold follows the current group size, so a new member raises it for open requests.
Raw JSON
{ "id": 3, "proposer": "pots.potprev.testnet", "description": "Pot request #1: tickets (ref p8w2m4q6z1x3r5t7)", "kind": { "Transfer": { "token_id": "usdc.potprev.testnet", "receiver_id": "u-7k2m9qxa.users.potprev.testnet", "amount": "300000000", "msg": null } }, "status": "InProgress", "vote_counts": { "member": [ "1", "0", "0" ] }, "votes": { "u-7k2m9qxa.users.potprev.testnet": "Approve" }, "submission_time": "1794049231200000000", "last_actions_log": [ { "block_height": "275012052" }, { "block_height": "275012055" } ]}
Good to know
A vote carries a copy of the exact request. The DAO refuses it if the copy differs, so nobody approves something other than what they saw.
What happened on NEAR, receipt by receipt
Receipts 1–2 of 2sim-Hin9…hNxmSimulated
- ·Phone → RelayerThe signed request goes to our relayer (POST /api/relay), off-chain
- 1Relayer → MayaDelegate: runs Maya's signed request
- 2Maya → Lisbon Tripact_proposal
(#3, VoteApprove)100 Tgas
Show 2 gas refunds
- 3Gas refund to the relayer · 0.10065 NEAR
- 4Gas refund to the relayer · 0.00046 NEAR
- Step 5 of 10In the app
One more approval is needed. Leo and Sam get a notification.
- Step 6 of 10In the app
Now you're Leo. He reads what he'd approve: $300 to Maya, for tickets.
- Step 7 of 10In the app
Leo signs one vote, sent straight to the Pot, not to the Hub.
- Step 8 of 10
That's the second of two. The Pot pays Maya $300 in the same moment.
What Leo signed
Simulated valuesdevice key · ed255…HJirTell Lisbon Trip I approve proposal #3, exactly as it was filed. Attach no money. Valid for about 2 minutes.
- Signersender_id
- u-3p8w2kdn.
users. potprev. testnetLeo's account - Callsreceiver_id
- lisbon-trip-7k2f.
pots. potprev. testnetthe Pot's own DAO, not the Hub - Methodactions[0].FunctionCall
- act_proposalone call
- Gasgas
- 100 Tgasthe most it may use; the relayer buys it
- Depositdeposit
- 0Leo attaches no money
Show every field
- Noncenonce
- 275010024000003this key's signature counter; a replay fails
- Valid untilmax_block_height
- 275013757valid for about 2 minutes
- Public keypublic_key
- ed25519:GM9zgxaPnBHvJ5hvWTJJwbiPoAvRj6z6nhghxJfZHJirLeo's phone key
- Signaturesignature
- ed25519:1cUZ99vhjSnzNP2kNBtGSD9m4SEByEu1yxCV46t4SLUkwrCzpwc7BooQEyp8nWft7qFBdFPQpCQvt7CDnKR7jUE
- Argumentsid
- 3
- Argumentsaction
- VoteApprove
- Argumentsproposal
- {"Transfer":{"token_id":"usdc.potprev.testnet","receiver_id":"u-7k2m9qxa.users.potprev.testnet","amount":"300000000","msg":null}}the DAO rejects the vote unless this equals the stored request exactly (ERR_WRONG_KIND).
- How it's signedNEP-366
- BAdHaCQRjkzf6Ut9iX4srUvmsYzSwMpkZGG2pG5rKAk5sha256(6e 01 00 40 ‖ borsh(DelegateAction)), then ed25519. The 4-byte NEP-366 prefix makes it unusable as an ordinary transaction.
Raw JSON
{ "delegate_action": { "sender_id": "u-3p8w2kdn.users.potprev.testnet", "receiver_id": "lisbon-trip-7k2f.pots.potprev.testnet", "actions": [ { "FunctionCall": { "method_name": "act_proposal", "args": { "id": 3, "action": "VoteApprove", "proposal": { "Transfer": { "token_id": "usdc.potprev.testnet", "receiver_id": "u-7k2m9qxa.users.potprev.testnet", "amount": "300000000", "msg": null } } }, "args_base64": "eyJpZCI6MywiYWN0aW9uIjoiVm90ZUFwcHJvdmUiLCJwcm9wb3NhbCI6eyJUcmFuc2ZlciI6eyJ0b2tlbl9pZCI6InVzZGMucG90cHJldi50ZXN0bmV0IiwicmVjZWl2ZXJfaWQiOiJ1LTdrMm05cXhhLnVzZXJzLnBvdHByZXYudGVzdG5ldCIsImFtb3VudCI6IjMwMDAwMDAwMCIsIm1zZyI6bnVsbH19fQ==", "gas": "100000000000000", "deposit": "0" } } ], "nonce": "275010024000003", "max_block_height": "275013757", "public_key": "ed25519:GM9zgxaPnBHvJ5hvWTJJwbiPoAvRj6z6nhghxJfZHJir" }, "signature": "ed25519:1cUZ99vhjSnzNP2kNBtGSD9m4SEByEu1yxCV46t4SLUkwrCzpwc7BooQEyp8nWft7qFBdFPQpCQvt7CDnKR7jUE"}
What the app posts to our relayer
IAAAAHUtM3A4dzJrZG4udXNlcnMucG90cHJldi50ZXN0bmV0JQAAAGxpc2Jvbi10cmlwLTdrMmYucG90cy5wb3RwcmV2LnRlc3RuZXQBAAAAAgwAAABhY3RfcHJvcG9zYWysAAAAeyJpZCI6MywiYWN0aW9uIjoiVm90ZUFwcHJvdmUiLCJwcm9wb3NhbCI6eyJUcmFuc2ZlciI6eyJ0b2tlbl9pZCI6InVzZGMucG90cHJldi50ZXN0bmV0IiwicmVjZWl2ZXJfaWQiOiJ1LTdrMm05cXhhLnVzZXJzLnBvdHByZXYudGVzdG5ldCIsImFtb3VudCI6IjMwMDAwMDAwMCIsIm1zZyI6bnVsbH19fQBAehDzWgAAAAAAAAAAAAAAAAAAAAAAAANKysIe+gAAfWBkEAAAAAAA5AkXINpInt7wtU6kybf3ulgfJ7bsAq5CUtqne+Lm5N8AAIcHQF1hF4V3bZY6AlfbSFLuQlEPJq/7r1VgPzA3ney7H1JQpxBF83BXQpcoKXzCN2E/Az9MuZaVZNbreakMAw==
ApprovedProposal #3 in Lisbon Tripa Sputnik DAO
Transfer · $300.00 USDC (test) → Maya
Pot request #1: tickets (ref p8w2m4q6z1x3r5t7)
· filed by Pot HubWho can decide this
Members (3)
2 of 2 approvals- Maya approved
- Leo approvedneeds 2
- Sam not yet
Pot rules seat (1)can't vote outside the rulesHow “2” is computed
Role member, a group of 3, vote_policy default [1, 2], quorum "1": threshold = max(quorum, min(3 × 1 ÷ 2 + 1, 3)) = 2 (whole-number division). A single role reaching its threshold decides. The threshold follows the current group size, so a new member raises it for open requests.
Raw JSON
{ "id": 3, "proposer": "pots.potprev.testnet", "description": "Pot request #1: tickets (ref p8w2m4q6z1x3r5t7)", "kind": { "Transfer": { "token_id": "usdc.potprev.testnet", "receiver_id": "u-7k2m9qxa.users.potprev.testnet", "amount": "300000000", "msg": null } }, "status": "Approved", "vote_counts": { "member": [ "2", "0", "0" ] }, "votes": { "u-3p8w2kdn.users.potprev.testnet": "Approve", "u-7k2m9qxa.users.potprev.testnet": "Approve" }, "submission_time": "1794049231200000000", "last_actions_log": [ { "block_height": "275012052" }, { "block_height": "275012055" }, { "block_height": "275013563" } ]}
Good to know
Approving doesn't prove the money arrived: the payout is a separate receipt. Pot checks it, and sync records a failure if there is one.
What happened on NEAR, receipt by receipt
Receipts 1–5 of 5sim-4eUp…RjdgSimulated
- ·Phone → RelayerThe signed request goes to our relayer (POST /api/relay), off-chain
- 1Relayer → LeoDelegate: runs Leo's signed request
- 2Leo → Lisbon Tripact_proposal
(#3, VoteApprove)100 Tgas - 3Lisbon Trip → USDC (test)ft_transfer
(Maya, $300.00)$300.00≈ 48 Tgas1 yocto - 4Callback from USDC (test) to Lisbon Tripcallbackon_proposal_callback
(#3)≈ 48 Tgas - 5Lisbon Trip → Pot HubTransfer 0
Show 5 gas refunds
- 6Gas refund to the relayer · 0.04868 NEAR
- 7Gas refund to the relayer · 0.00020 NEAR
- 8Gas refund to the relayer · 0.04840 NEAR
- 9Gas refund to the relayer · 0.00282 NEAR
- 10Gas refund to the relayer · 0.00046 NEAR
- Step 9 of 10
Our relayer then tells the Hub what happened, so its records match the Pot.
What our relayer sent
Simulated valuesThe relayer's transaction
- Signersigner_id
- users.
potprev. testnetRelayer - Receiverreceiver_id
- pots.
potprev. testnetPot Hub - Noncenonce
- 274990000000008
- Block hashblock_hash
- sim-4B73…5AuW
- Transactionhash
- sim-FGWr…nX1w
A plain transaction the relayer signs itself. sync is open to anyone, so no request rides inside.
sync → Pot Hub · 50 Tgas
Who paid
Relayer · users.
potprev. testnet ≈ 0.0007 NEAR of gas
Copied from a real testnet run of the same call. A simulation can't burn gas.
Raw JSON
{ "hash": "sim-FGWrSBk8FGVfkkJbj5i2gGw5692FnX1w", "signer_id": "users.potprev.testnet", "public_key": "ed25519:3q2rinP5DL1aiB33YTh372bu3K3Tf9kwNyAUjEm4xzF3", "nonce": "274990000000008", "receiver_id": "pots.potprev.testnet", "block_hash": "sim-4B7362pC5vuKdSMpxuwn5AycTkbY5AuW", "actions": [ { "FunctionCall": { "method_name": "sync", "args": { "pot": "lisbon-trip-7k2f.pots.potprev.testnet", "proposal_id": 3 }, "gas": "50000000000000", "deposit": "0" } } ], "checks": []}
Good to know
Anyone may call sync. Our relayer does it after every vote, so the Hub's records always match the Pot.
What happened on NEAR, receipt by receipt
Receipts 1–3 of 3sim-FGWr…nX1wSimulated
- 1Relayer → Pot Hubsync
(Lisbon Trip, #3)50 Tgas - 2Pot Hub → Lisbon Tripget_proposal
(#3)10 Tgasreturned #3 Approved - 3Callback from Lisbon Trip to Pot Hubcallbackon_sync
(Lisbon Trip, #3)10 Tgasreturned proposal_id, subject…
Show 3 gas refunds
- 4Gas refund to the relayer · 0.01071 NEAR
- 5Gas refund to the relayer · 0.01062 NEAR
- 6Gas refund to the relayer · 0.02885 NEAR
- Step 10 of 10In the app
Done: $66 left in the Pot.
Balances
Dollars Account Before After Change Maya $84.00 $384.00 + $300.00in Sam $0.00 $0.00 no change Leo $0.00 $0.00 no change Lisbon Trip $366.00 $66.00 − $300.00out Pot Hub $0.00 $0.00 no change NEAR (fees and storage)
- Maya0 NEAR
- Leo0 NEAR
- Lisbon Trip− 1 yoctoNEAR
- Pot Hub− 0.00885 NEAR
- Relayer− ≈ 0.00368 NEAR
Copied from a real testnet run of the same call. A simulation can't burn gas.
Raw JSON
{ "before": { "u-7k2m9qxa.users.potprev.testnet": { "usdc": "84000000", "near": "0" }, "u-4hn8c2wd.users.potprev.testnet": { "usdc": "0", "near": "0" }, "u-3p8w2kdn.users.potprev.testnet": { "usdc": "0", "near": "0" }, "pots.potprev.testnet": { "usdc": "0" }, "lisbon-trip-7k2f.pots.potprev.testnet": { "usdc": "366000000" } }, "after": { "u-7k2m9qxa.users.potprev.testnet": { "usdc": "384000000", "near": "0" }, "u-4hn8c2wd.users.potprev.testnet": { "usdc": "0", "near": "0" }, "u-3p8w2kdn.users.potprev.testnet": { "usdc": "0", "near": "0" }, "pots.potprev.testnet": { "usdc": "0" }, "lisbon-trip-7k2f.pots.potprev.testnet": { "usdc": "66000000" } }}
After this chapter
What changed
| Field | Before | After |
|---|---|---|
| The Pot's account | a Sputnik DAO, no access keys, 0.05 NEAR float | a Sputnik DAO, no access keys, 0.05 NEAR float |
| Members (DAO role member) | Maya, Sam, Leo | Maya, Sam, Leo |
| Group approvals needed | 2 of 3 | 2 of 3 |
| Proposals (changed) | #0 Add member · Approved #1 Add member · Approved #2 Transfer · Approved | #0 Add member · Approved #1 Add member · Approved #2 Transfer · Approved #3 Transfer · Approved |
| Pot status | Open | Open |
| Hub ledger: put in | Maya $150.00 · Sam $150.00 · Leo $150.00 | Maya $150.00 · Sam $150.00 · Leo $150.00 |
| Hub ledger: spent without asking | Maya $84.00 · Sam $0.00 · Leo $0.00 | Maya $84.00 · Sam $0.00 · Leo $0.00 |
Raw JSON
{ "before": { "pot": { "status": "open", "members": [ { "account": "u-7k2m9qxa.users.potprev.testnet", "joinIndex": 0, "contributed": "150000000", "instantUsed": "84000000" }, { "account": "u-4hn8c2wd.users.potprev.testnet", "joinIndex": 1, "contributed": "150000000", "instantUsed": "0" }, { "account": "u-3p8w2kdn.users.potprev.testnet", "joinIndex": 2, "contributed": "150000000", "instantUsed": "0" } ], "ledger": { "totalContributed": "450000000", "spentInstant": "84000000", "spentVote": "0", "reserved": "0" }, "requests": [ { "id": 0, "path": "instant", "status": "approved", "amount": "84000000", "proposalId": 2, "category": "groceries", "requestRef": "k3d9x1a2b4c6m7n8" } ] }, "dao": { "exists": true, "members": [ "u-7k2m9qxa.users.potprev.testnet", "u-4hn8c2wd.users.potprev.testnet", "u-3p8w2kdn.users.potprev.testnet" ], "rulebot": [ "pots.potprev.testnet" ], "threshold": { "k": 2, "n": 3 }, "lastProposalId": 3, "proposals": [ { "id": 0, "proposer": "pots.potprev.testnet", "description": "Pot join via invite", "kind": { "AddMemberToRole": { "member_id": "u-4hn8c2wd.users.potprev.testnet", "role": "member" } }, "status": "Approved", "vote_counts": { "rulebot": [ "1", "0", "0" ] }, "votes": { "pots.potprev.testnet": "Approve" }, "submission_time": "1794042306600000000", "last_actions_log": [ { "block_height": "275000511" }, { "block_height": "275000513" } ] }, { "id": 1, "proposer": "pots.potprev.testnet", "description": "Pot join via invite", "kind": { "AddMemberToRole": { "member_id": "u-3p8w2kdn.users.potprev.testnet", "role": "member" } }, "status": "Approved", "vote_counts": { "rulebot": [ "1", "0", "0" ] }, "votes": { "pots.potprev.testnet": "Approve" }, "submission_time": "1794048017400000000", "last_actions_log": [ { "block_height": "275010029" }, { "block_height": "275010031" } ] }, { "id": 2, "proposer": "pots.potprev.testnet", "description": "Pot request #0: groceries (ref k3d9x1a2b4c6m7n8)", "kind": { "Transfer": { "token_id": "usdc.potprev.testnet", "receiver_id": "u-7k2m9qxa.users.potprev.testnet", "amount": "84000000", "msg": null } }, "status": "Approved", "vote_counts": { "rulebot": [ "1", "0", "0" ] }, "votes": { "pots.potprev.testnet": "Approve" }, "submission_time": "1794048625200000000", "last_actions_log": [ { "block_height": "275011042" }, { "block_height": "275011046" } ] } ], "floatNear": "0.05" } }, "after": { "pot": { "status": "open", "members": [ { "account": "u-7k2m9qxa.users.potprev.testnet", "joinIndex": 0, "contributed": "150000000", "instantUsed": "84000000" }, { "account": "u-4hn8c2wd.users.potprev.testnet", "joinIndex": 1, "contributed": "150000000", "instantUsed": "0" }, { "account": "u-3p8w2kdn.users.potprev.testnet", "joinIndex": 2, "contributed": "150000000", "instantUsed": "0" } ], "ledger": { "totalContributed": "450000000", "spentInstant": "84000000", "spentVote": "300000000", "reserved": "0" }, "requests": [ { "id": 0, "path": "instant", "status": "approved", "amount": "84000000", "proposalId": 2, "category": "groceries", "requestRef": "k3d9x1a2b4c6m7n8" }, { "id": 1, "path": "vote", "status": "approved", "amount": "300000000", "proposalId": 3, "category": "tickets", "requestRef": "p8w2m4q6z1x3r5t7" } ] }, "dao": { "exists": true, "members": [ "u-7k2m9qxa.users.potprev.testnet", "u-4hn8c2wd.users.potprev.testnet", "u-3p8w2kdn.users.potprev.testnet" ], "rulebot": [ "pots.potprev.testnet" ], "threshold": { "k": 2, "n": 3 }, "lastProposalId": 4, "proposals": [ { "id": 0, "proposer": "pots.potprev.testnet", "description": "Pot join via invite", "kind": { "AddMemberToRole": { "member_id": "u-4hn8c2wd.users.potprev.testnet", "role": "member" } }, "status": "Approved", "vote_counts": { "rulebot": [ "1", "0", "0" ] }, "votes": { "pots.potprev.testnet": "Approve" }, "submission_time": "1794042306600000000", "last_actions_log": [ { "block_height": "275000511" }, { "block_height": "275000513" } ] }, { "id": 1, "proposer": "pots.potprev.testnet", "description": "Pot join via invite", "kind": { "AddMemberToRole": { "member_id": "u-3p8w2kdn.users.potprev.testnet", "role": "member" } }, "status": "Approved", "vote_counts": { "rulebot": [ "1", "0", "0" ] }, "votes": { "pots.potprev.testnet": "Approve" }, "submission_time": "1794048017400000000", "last_actions_log": [ { "block_height": "275010029" }, { "block_height": "275010031" } ] }, { "id": 2, "proposer": "pots.potprev.testnet", "description": "Pot request #0: groceries (ref k3d9x1a2b4c6m7n8)", "kind": { "Transfer": { "token_id": "usdc.potprev.testnet", "receiver_id": "u-7k2m9qxa.users.potprev.testnet", "amount": "84000000", "msg": null } }, "status": "Approved", "vote_counts": { "rulebot": [ "1", "0", "0" ] }, "votes": { "pots.potprev.testnet": "Approve" }, "submission_time": "1794048625200000000", "last_actions_log": [ { "block_height": "275011042" }, { "block_height": "275011046" } ] }, { "id": 3, "proposer": "pots.potprev.testnet", "description": "Pot request #1: tickets (ref p8w2m4q6z1x3r5t7)", "kind": { "Transfer": { "token_id": "usdc.potprev.testnet", "receiver_id": "u-7k2m9qxa.users.potprev.testnet", "amount": "300000000", "msg": null } }, "status": "Approved", "vote_counts": { "member": [ "2", "0", "0" ] }, "votes": { "u-3p8w2kdn.users.potprev.testnet": "Approve", "u-7k2m9qxa.users.potprev.testnet": "Approve" }, "submission_time": "1794049231200000000", "last_actions_log": [ { "block_height": "275012052" }, { "block_height": "275012055" }, { "block_height": "275013563" } ] } ], "floatNear": "0.05", "policy": { "roles": [ { "name": "member", "kind": { "Group": [ "u-7k2m9qxa.users.potprev.testnet", "u-4hn8c2wd.users.potprev.testnet", "u-3p8w2kdn.users.potprev.testnet" ] }, "permissions": [ "*:VoteApprove", "*:VoteReject" ], "vote_policy": {} }, { "name": "rulebot", "kind": { "Group": [ "pots.potprev.testnet" ] }, "permissions": [ "transfer:AddProposal", "transfer:VoteApprove", "transfer:Finalize", "add_member_to_role:AddProposal", "add_member_to_role:VoteApprove", "call:AddProposal", "call:Finalize" ], "vote_policy": { "transfer": { "weight_kind": "RoleWeight", "quorum": "1", "threshold": [ 1, 1 ] }, "add_member_to_role": { "weight_kind": "RoleWeight", "quorum": "1", "threshold": [ 1, 1 ] } } } ], "default_vote_policy": { "weight_kind": "RoleWeight", "quorum": "1", "threshold": [ 1, 2 ] }, "proposal_bond": "0", "proposal_period": "604800000000000", "bounty_bond": "0", "bounty_forgiveness_period": "86400000000000" } } }}
Who paid
Relayer · users.
potprev. testnet ≈ 0.0037 NEAR of gas
Pot Hub · pots.
potprev. testnet 0.00885 NEAR storage prepay for the new proposal
Lisbon Trip · lisbon-trip-7k2f.
pots. potprev. testnet 1 yoctoNEAR on a token transfer
Maya · u-7k2m9qxa.
users. potprev. testnet Maya: 0 NEAR. Their account has 0 NEAR before and after.
Leo · u-3p8w2kdn.
users. potprev. testnet Leo: 0 NEAR. Their account has 0 NEAR before and after.
Copied from a real testnet run of the same call. A simulation can't burn gas.
See a real one on testnet
One OK in the reference Pot, as in this part's story: Sam's OK on Maya's $150 goes to the Hub, which checks it and approves with its seat, and the Pot pays.
Real · testnet (opens nearblocks.io in a new tab)9K4beHZV1BkxKvXgaQ2WY8KAz5MPrHSkHTj5NqGL1HVrThe group path the steps below show: Leo's approval of Maya's $310 in the reference Pot, over its $300 one-OK limit. It is the second of 2 needed out of 3, so the Pot pays in the same transaction.
Real · testnet (opens nearblocks.io in a new tab)9YfZ6DtZ2Ey5trk5CVPqDTKByfEasLYyvsbTZJwTbNAM
Words in this chapter
11 words
- DAO
- A shared account run by rules instead of a person. Here every Pot is one, and its members approve what it does.
- proposal
- A request filed in the DAO, like “pay Maya $84” or “add Leo”. It runs only once enough seats approve it.
- role / seat
- A named group in the DAO with its own powers. A Pot has two: member (the friends) and rulebot (only the Pot Hub).
- threshold
- How many approvals a role needs. Members need a majority: 2 of 3 here.
- quorum
- The minimum number of approvals ever accepted. Pot sets it to 1, so nothing can pass with zero votes.
- rulebot
- The Pot Hub's seat in every Pot. One vote from it approves transfers and new members, and the Hub only casts it when the Pot's rules allow.
- sync
- A Hub method anyone can call to copy a proposal's final status from the Pot into the Hub's records.
- delegate action
- A request the user signs but doesn't send (NEP-366). Someone else wraps it in a transaction and pays for it; contracts still see the user as the caller.
- relayer
- Pot's server account (users.potprev.testnet) that sends signed requests and pays their gas, so users need 0 NEAR. It refuses anything outside an allowlist.
- receipt
- One step of a transaction's work, run by one account. A transaction fans out into receipts as contracts call each other.
- gas / Tgas
- The work a call may use, bought in NEAR. 1 Tgas is a trillion gas, about 0.0001 NEAR. Unused gas is refunded.
Part 5: Close and share back
Closing is one proposal that holds all three refunds, computed from what each person put in. Sam's approval ran them in one step: all of them, or none.
Chapter 6 of 6
Close the Pot: $22 back each
Now you're Sam
Closing is one proposal with everyone's refund inside it. Approve it once and every refund happens in one step, or none do.
Pot closed
$22.00 is in your Pot account.
It stays in your Pot dollars, ready for your next Pot.
| Person | Put in | Gets back |
|---|---|---|
| Sam (you) | $150.00 | $22.00 |
| Maya | $150.00 | $22.00 |
| Leo | $150.00 | $22.00 |
| Total | $450.00 | $66.00 |
Under the hood
Simulated- 3 signatures by people
- 4 transactions
- 16 receipts
- 0 NEAR paid by people
Where we start
The Pot has $66.00, and each member put in $150.00. Proposals #0 to #3 are all approved. The last money movement was 30 minutes ago.
- In the Pot
- $66.00
- Members (DAO role member)
- Maya, Sam, Leo
- Group approvals needed
- 2 of 3
What happens, step by step
- Step 1 of 10In the app
The trip is over. The app shows what each person would get back before anyone asks.
- Step 2 of 10In the app
Maya signs one request to the Hub.
- Step 3 of 10
The Hub reads what's really in the Pot, $66, and splits it by what each person put in: $22 each.
What Maya signed
Simulated valuesdevice key · ed255…qWntAsk the Pot Hub to close Lisbon Trip and give everyone their share back. Attach no money. Valid for about 2 minutes.
- Signersender_id
- u-7k2m9qxa.
users. potprev. testnetMaya's account - Callsreceiver_id
- pots.
potprev. testnetthe Pot Hub: the only contract this can call - Methodactions[0].FunctionCall
- propose_closeone call
- Gasgas
- 150 Tgasthe most it may use; the relayer buys it
- Depositdeposit
- 0Maya attaches no money
Show every field
- Noncenonce
- 275000000000006this key's signature counter; a replay fails
- Valid untilmax_block_height
- 275016765valid for about 2 minutes
- Public keypublic_key
- ed25519:6rFmed3Kxeaqp8Ki7oN8Ad3cCg3S8W5sVqXi9eRYqWntMaya's phone key
- Signaturesignature
- ed25519:2XZBN8RJy6yVH6A8PpsKcPB8jJTknzCN8FkhaCvujm7xeaNYkTvQns1Z2XqZC4qwQdBL2XtAt6aJL8QpmtYoGBde
- Argumentspot
- lisbon-trip-7k2f.
pots. potprev. testnetLisbon Trip - How it's signedNEP-366
- 4CQC3idaWUMEPDkg2YsJecxuubrJTMaSXUfsFHKT46Wrsha256(6e 01 00 40 ‖ borsh(DelegateAction)), then ed25519. The 4-byte NEP-366 prefix makes it unusable as an ordinary transaction.
Raw JSON
{ "delegate_action": { "sender_id": "u-7k2m9qxa.users.potprev.testnet", "receiver_id": "pots.potprev.testnet", "actions": [ { "FunctionCall": { "method_name": "propose_close", "args": { "pot": "lisbon-trip-7k2f.pots.potprev.testnet" }, "args_base64": "eyJwb3QiOiJsaXNib24tdHJpcC03azJmLnBvdHMucG90cHJldi50ZXN0bmV0In0=", "gas": "150000000000000", "deposit": "0" } } ], "nonce": "275000000000006", "max_block_height": "275016765", "public_key": "ed25519:6rFmed3Kxeaqp8Ki7oN8Ad3cCg3S8W5sVqXi9eRYqWnt" }, "signature": "ed25519:2XZBN8RJy6yVH6A8PpsKcPB8jJTknzCN8FkhaCvujm7xeaNYkTvQns1Z2XqZC4qwQdBL2XtAt6aJL8QpmtYoGBde"}
What the app posts to our relayer
IAAAAHUtN2sybTlxeGEudXNlcnMucG90cHJldi50ZXN0bmV0FAAAAHBvdHMucG90cHJldi50ZXN0bmV0AQAAAAINAAAAcHJvcG9zZV9jbG9zZS8AAAB7InBvdCI6Imxpc2Jvbi10cmlwLTdrMmYucG90cy5wb3RwcmV2LnRlc3RuZXQifQBgt5hsiAAAAAAAAAAAAAAAAAAAAAAAAAYwUG0c+gAAPWxkEAAAAAAAVuiYNECWv5AeQiYDZZxa5BBnvnAh5X6YWlqkdTS32ykATF3F/2SnmRS5Q51raP/FAvML+b+k/fNP5YO/J4bHrNghd5Xuk6HKnpGeut3N8jQdxeMoQ6CwmjB9glJAp6ohDQ==
Good to know
A close can't start within a minute of money moving, so a refund is never computed from a balance that's about to change.
What happened on NEAR, receipt by receipt
Receipts 1–4 of 6sim-7orj…7E2zSimulated
- ·Phone → RelayerThe signed request goes to our relayer (POST /api/relay), off-chain
- 1Relayer → MayaDelegate: runs Maya's signed request
- 2Maya → Pot Hubpropose_close
(Lisbon Trip)150 Tgas - 3Pot Hub → USDC (test)ft_balance_of
(Lisbon Trip)5 Tgasreturned "66000000" - 4Callback from USDC (test) to Pot Hubcallbackon_close_balance
(Lisbon Trip)60 Tgas
- Step 4 of 10
It files one request in the Pot that holds all three refunds. From now on, nobody can add or spend.
In progressProposal #4 in Lisbon Tripa Sputnik DAO
One call to USDC (test) with 3 refunds inside
Pot close-out: refund 66000000 to 3 members
· filed by Pot Hub- Maya gets $22.00
- Sam gets $22.00
- Leo gets $22.00
Who can decide this
Members (3)
0 of 2 approvals- Maya not yet
- Sam not yetneeds 2
- Leo not yet
Pot rules seat (1)can't vote on this kindHow “2” is computed
The Hub has only call:AddProposal, so it can file this but never vote on it. Members decide: min(3 × 1 ÷ 2 + 1, 3) = 2.
Raw JSON
{ "id": 4, "proposer": "pots.potprev.testnet", "description": "Pot close-out: refund 66000000 to 3 members", "kind": { "FunctionCall": { "receiver_id": "usdc.potprev.testnet", "actions": [ { "method_name": "ft_transfer", "args": "eyJyZWNlaXZlcl9pZCI6InUtN2sybTlxeGEudXNlcnMucG90cHJldi50ZXN0bmV0IiwiYW1vdW50IjoiMjIwMDAwMDAiLCJtZW1vIjoicG90IHJlZnVuZCJ9", "deposit": "1", "gas": "5000000000000" }, { "method_name": "ft_transfer", "args": "eyJyZWNlaXZlcl9pZCI6InUtNGhuOGMyd2QudXNlcnMucG90cHJldi50ZXN0bmV0IiwiYW1vdW50IjoiMjIwMDAwMDAiLCJtZW1vIjoicG90IHJlZnVuZCJ9", "deposit": "1", "gas": "5000000000000" }, { "method_name": "ft_transfer", "args": "eyJyZWNlaXZlcl9pZCI6InUtM3A4dzJrZG4udXNlcnMucG90cHJldi50ZXN0bmV0IiwiYW1vdW50IjoiMjIwMDAwMDAiLCJtZW1vIjoicG90IHJlZnVuZCJ9", "deposit": "1", "gas": "5000000000000" } ] } }, "status": "InProgress", "vote_counts": {}, "votes": {}, "submission_time": "1794051942000000000", "last_actions_log": [ { "block_height": "275016570" } ]}
What happened on NEAR, receipt by receipt
Receipts 5–6 of 6sim-7orj…7E2zSimulated
- 5Pot Hub → Lisbon Tripone receipt, 2 actionsTransfer 0.01452 NEARadd_proposal20 Tgasreturned 4
- 6Callback from Lisbon Trip to Pot Hubcallbackon_close_added
(Lisbon Trip)10 Tgasreturned ok
Show 6 gas refunds
- 7Gas refund to the relayer · 0.00576 NEAR
- 8Gas refund to the relayer · 0.02077 NEAR
- 9Gas refund to the relayer · 0.01061 NEAR
- 10Gas refund to the relayer · 0.02863 NEAR
- 11Gas refund to the relayer · 0.08374 NEAR
- 12Gas refund to the relayer · 0.00045 NEAR
- Step 5 of 10
Her phone adds her approval. Closing needs extra gas room, so all refunds fit in one step.
What Maya signed
Simulated valuesdevice key · ed255…qWntTell Lisbon Trip I approve proposal #4, exactly as it was filed. Attach no money. Valid for about 2 minutes.
- Signersender_id
- u-7k2m9qxa.
users. potprev. testnetMaya's account - Callsreceiver_id
- lisbon-trip-7k2f.
pots. potprev. testnetthe Pot's own DAO, not the Hub - Methodactions[0].FunctionCall
- act_proposalone call
- Gasgas
- 300 Tgasthe most it may use; the relayer buys it
- Depositdeposit
- 0Maya attaches no money
Show every field
- Noncenonce
- 275000000000007this key's signature counter; a replay fails
- Valid untilmax_block_height
- 275016770valid for about 2 minutes
- Public keypublic_key
- ed25519:6rFmed3Kxeaqp8Ki7oN8Ad3cCg3S8W5sVqXi9eRYqWntMaya's phone key
- Signaturesignature
- ed25519:39kQWoAg3V6j8v9muZaBhtxenS5fbGRiYt6uPrf9817gAPLUyWAorBPTYbRmSyKsQs85DVDmawcGsVfebg24zUu1
- Argumentsid
- 4
- Argumentsaction
- VoteApprove
- Argumentsproposal
- {"FunctionCall":{"receiver_id":"usdc.potprev.testnet","actions":[{"method_name":"ft_transfer","args":"eyJyZWNlaXZlcl9pZCI6InUtN2sybTlxeGEudXNlcnMucG90cHJldi50ZXN0bmV0IiwiYW1vdW50IjoiMjIwMDAwMDAiLCJtZW1vIjoicG90IHJlZnVuZCJ9","deposit":"1","gas":"5000000000000"},{"method_name":"ft_transfer","args":"eyJyZWNlaXZlcl9pZCI6InUtNGhuOGMyd2QudXNlcnMucG90cHJldi50ZXN0bmV0IiwiYW1vdW50IjoiMjIwMDAwMDAiLCJtZW1vIjoicG90IHJlZnVuZCJ9","deposit":"1","gas":"5000000000000"},{"method_name":"ft_transfer","args":"eyJyZWNlaXZlcl9pZCI6InUtM3A4dzJrZG4udXNlcnMucG90cHJldi50ZXN0bmV0IiwiYW1vdW50IjoiMjIwMDAwMDAiLCJtZW1vIjoicG90IHJlZnVuZCJ9","deposit":"1","gas":"5000000000000"}]}}the DAO rejects the vote unless this equals the stored request exactly (ERR_WRONG_KIND).
- How it's signedNEP-366
- BVcT5kVQ3NdvzrZj7cLXs7aU1BiqF3wCYrUK2YMdwkKEsha256(6e 01 00 40 ‖ borsh(DelegateAction)), then ed25519. The 4-byte NEP-366 prefix makes it unusable as an ordinary transaction.
Raw JSON
{ "delegate_action": { "sender_id": "u-7k2m9qxa.users.potprev.testnet", "receiver_id": "lisbon-trip-7k2f.pots.potprev.testnet", "actions": [ { "FunctionCall": { "method_name": "act_proposal", "args": { "id": 4, "action": "VoteApprove", "proposal": { "FunctionCall": { "receiver_id": "usdc.potprev.testnet", "actions": [ { "method_name": "ft_transfer", "args": "eyJyZWNlaXZlcl9pZCI6InUtN2sybTlxeGEudXNlcnMucG90cHJldi50ZXN0bmV0IiwiYW1vdW50IjoiMjIwMDAwMDAiLCJtZW1vIjoicG90IHJlZnVuZCJ9", "deposit": "1", "gas": "5000000000000" }, { "method_name": "ft_transfer", "args": "eyJyZWNlaXZlcl9pZCI6InUtNGhuOGMyd2QudXNlcnMucG90cHJldi50ZXN0bmV0IiwiYW1vdW50IjoiMjIwMDAwMDAiLCJtZW1vIjoicG90IHJlZnVuZCJ9", "deposit": "1", "gas": "5000000000000" }, { "method_name": "ft_transfer", "args": "eyJyZWNlaXZlcl9pZCI6InUtM3A4dzJrZG4udXNlcnMucG90cHJldi50ZXN0bmV0IiwiYW1vdW50IjoiMjIwMDAwMDAiLCJtZW1vIjoicG90IHJlZnVuZCJ9", "deposit": "1", "gas": "5000000000000" } ] } } }, "args_base64": "eyJpZCI6NCwiYWN0aW9uIjoiVm90ZUFwcHJvdmUiLCJwcm9wb3NhbCI6eyJGdW5jdGlvbkNhbGwiOnsicmVjZWl2ZXJfaWQiOiJ1c2RjLnBvdHByZXYudGVzdG5ldCIsImFjdGlvbnMiOlt7Im1ldGhvZF9uYW1lIjoiZnRfdHJhbnNmZXIiLCJhcmdzIjoiZXlKeVpXTmxhWFpsY2w5cFpDSTZJblV0TjJzeWJUbHhlR0V1ZFhObGNuTXVjRzkwY0hKbGRpNTBaWE4wYm1WMElpd2lZVzF2ZFc1MElqb2lNakl3TURBd01EQWlMQ0p0Wlcxdklqb2ljRzkwSUhKbFpuVnVaQ0o5IiwiZGVwb3NpdCI6IjEiLCJnYXMiOiI1MDAwMDAwMDAwMDAwIn0seyJtZXRob2RfbmFtZSI6ImZ0X3RyYW5zZmVyIiwiYXJncyI6ImV5SnlaV05sYVhabGNsOXBaQ0k2SW5VdE5HaHVPR015ZDJRdWRYTmxjbk11Y0c5MGNISmxkaTUwWlhOMGJtVjBJaXdpWVcxdmRXNTBJam9pTWpJd01EQXdNREFpTENKdFpXMXZJam9pY0c5MElISmxablZ1WkNKOSIsImRlcG9zaXQiOiIxIiwiZ2FzIjoiNTAwMDAwMDAwMDAwMCJ9LHsibWV0aG9kX25hbWUiOiJmdF90cmFuc2ZlciIsImFyZ3MiOiJleUp5WldObGFYWmxjbDlwWkNJNkluVXRNM0E0ZHpKclpHNHVkWE5sY25NdWNHOTBjSEpsZGk1MFpYTjBibVYwSWl3aVlXMXZkVzUwSWpvaU1qSXdNREF3TURBaUxDSnRaVzF2SWpvaWNHOTBJSEpsWm5WdVpDSjkiLCJkZXBvc2l0IjoiMSIsImdhcyI6IjUwMDAwMDAwMDAwMDAifV19fX0=", "gas": "300000000000000", "deposit": "0" } } ], "nonce": "275000000000007", "max_block_height": "275016770", "public_key": "ed25519:6rFmed3Kxeaqp8Ki7oN8Ad3cCg3S8W5sVqXi9eRYqWnt" }, "signature": "ed25519:39kQWoAg3V6j8v9muZaBhtxenS5fbGRiYt6uPrf9817gAPLUyWAorBPTYbRmSyKsQs85DVDmawcGsVfebg24zUu1"}
What the app posts to our relayer
IAAAAHUtN2sybTlxeGEudXNlcnMucG90cHJldi50ZXN0bmV0JQAAAGxpc2Jvbi10cmlwLTdrMmYucG90cy5wb3RwcmV2LnRlc3RuZXQBAAAAAgwAAABhY3RfcHJvcG9zYWy6AgAAeyJpZCI6NCwiYWN0aW9uIjoiVm90ZUFwcHJvdmUiLCJwcm9wb3NhbCI6eyJGdW5jdGlvbkNhbGwiOnsicmVjZWl2ZXJfaWQiOiJ1c2RjLnBvdHByZXYudGVzdG5ldCIsImFjdGlvbnMiOlt7Im1ldGhvZF9uYW1lIjoiZnRfdHJhbnNmZXIiLCJhcmdzIjoiZXlKeVpXTmxhWFpsY2w5cFpDSTZJblV0TjJzeWJUbHhlR0V1ZFhObGNuTXVjRzkwY0hKbGRpNTBaWE4wYm1WMElpd2lZVzF2ZFc1MElqb2lNakl3TURBd01EQWlMQ0p0Wlcxdklqb2ljRzkwSUhKbFpuVnVaQ0o5IiwiZGVwb3NpdCI6IjEiLCJnYXMiOiI1MDAwMDAwMDAwMDAwIn0seyJtZXRob2RfbmFtZSI6ImZ0X3RyYW5zZmVyIiwiYXJncyI6ImV5SnlaV05sYVhabGNsOXBaQ0k2SW5VdE5HaHVPR015ZDJRdWRYTmxjbk11Y0c5MGNISmxkaTUwWlhOMGJtVjBJaXdpWVcxdmRXNTBJam9pTWpJd01EQXdNREFpTENKdFpXMXZJam9pY0c5MElISmxablZ1WkNKOSIsImRlcG9zaXQiOiIxIiwiZ2FzIjoiNTAwMDAwMDAwMDAwMCJ9LHsibWV0aG9kX25hbWUiOiJmdF90cmFuc2ZlciIsImFyZ3MiOiJleUp5WldObGFYWmxjbDlwWkNJNkluVXRNM0E0ZHpKclpHNHVkWE5sY25NdWNHOTBjSEpsZGk1MFpYTjBibVYwSWl3aVlXMXZkVzUwSWpvaU1qSXdNREF3TURBaUxDSnRaVzF2SWpvaWNHOTBJSEpsWm5WdVpDSjkiLCJkZXBvc2l0IjoiMSIsImdhcyI6IjUwMDAwMDAwMDAwMDAifV19fX0AwG4x2RABAAAAAAAAAAAAAAAAAAAAAAAHMFBtHPoAAEJsZBAAAAAAAFbomDRAlr+QHkImA2WcWuQQZ75wIeV+mFpapHU0t9spAGuUCLsLZSdH6SPWA/Dxos9U6zzxkLj8yW2PQ8VUzKIAlnhT+PbuHDMBHAu+884Xzlr2gBt5ErVXpuNCBb9AyAw=
In progressProposal #4 in Lisbon Tripa Sputnik DAO
One call to USDC (test) with 3 refunds inside
Pot close-out: refund 66000000 to 3 members
· filed by Pot Hub- Maya gets $22.00
- Sam gets $22.00
- Leo gets $22.00
Who can decide this
Members (3)
1 of 2 approvals- Maya approved
- Sam not yetneeds 2
- Leo not yet
Pot rules seat (1)can't vote on this kindHow “2” is computed
The Hub has only call:AddProposal, so it can file this but never vote on it. Members decide: min(3 × 1 ÷ 2 + 1, 3) = 2.
Raw JSON
{ "id": 4, "proposer": "pots.potprev.testnet", "description": "Pot close-out: refund 66000000 to 3 members", "kind": { "FunctionCall": { "receiver_id": "usdc.potprev.testnet", "actions": [ { "method_name": "ft_transfer", "args": "eyJyZWNlaXZlcl9pZCI6InUtN2sybTlxeGEudXNlcnMucG90cHJldi50ZXN0bmV0IiwiYW1vdW50IjoiMjIwMDAwMDAiLCJtZW1vIjoicG90IHJlZnVuZCJ9", "deposit": "1", "gas": "5000000000000" }, { "method_name": "ft_transfer", "args": "eyJyZWNlaXZlcl9pZCI6InUtNGhuOGMyd2QudXNlcnMucG90cHJldi50ZXN0bmV0IiwiYW1vdW50IjoiMjIwMDAwMDAiLCJtZW1vIjoicG90IHJlZnVuZCJ9", "deposit": "1", "gas": "5000000000000" }, { "method_name": "ft_transfer", "args": "eyJyZWNlaXZlcl9pZCI6InUtM3A4dzJrZG4udXNlcnMucG90cHJldi50ZXN0bmV0IiwiYW1vdW50IjoiMjIwMDAwMDAiLCJtZW1vIjoicG90IHJlZnVuZCJ9", "deposit": "1", "gas": "5000000000000" } ] } }, "status": "InProgress", "vote_counts": { "member": [ "1", "0", "0" ] }, "votes": { "u-7k2m9qxa.users.potprev.testnet": "Approve" }, "submission_time": "1794051942000000000", "last_actions_log": [ { "block_height": "275016570" }, { "block_height": "275016573" } ]}
What happened on NEAR, receipt by receipt
Receipts 1–2 of 2sim-B8kQ…bPF3Simulated
- ·Phone → RelayerThe signed request goes to our relayer (POST /api/relay), off-chain
- 1Relayer → MayaDelegate: runs Maya's signed request
- 2Maya → Lisbon Tripact_proposal
(#4, VoteApprove)300 Tgas
Show 2 gas refunds
- 3Gas refund to the relayer · 0.30064 NEAR
- 4Gas refund to the relayer · 0.00047 NEAR
- Step 6 of 10In the app
Now you're Sam. He sees exactly the three refunds he'd approve.
- Step 7 of 10In the app
Sam's approval is the second of two.
- Step 8 of 10
The Pot sends all three refunds in a single step. If one failed, none would happen.
What Sam signed
Simulated valuesdevice key · ed255…ddzXTell Lisbon Trip I approve proposal #4, exactly as it was filed. Attach no money. Valid for about 2 minutes.
- Signersender_id
- u-4hn8c2wd.
users. potprev. testnetSam's account - Callsreceiver_id
- lisbon-trip-7k2f.
pots. potprev. testnetthe Pot's own DAO, not the Hub - Methodactions[0].FunctionCall
- act_proposalone call
- Gasgas
- 300 Tgasthe most it may use; the relayer buys it
- Depositdeposit
- 0Sam attaches no money
Show every field
- Noncenonce
- 275000506000003this key's signature counter; a replay fails
- Valid untilmax_block_height
- 275017775valid for about 2 minutes
- Public keypublic_key
- ed25519:CLqK4ADFQKLeQgxxPwXwiFUDtDMWvApHsPKzfDzmddzXSam's phone key
- Signaturesignature
- ed25519:475HrRbyczw1XH2eobJwFQSmcyN1kmmACqwCaFKwQjfZE1GXg31q6RVXV9PBPQeGC2umotbPm6X9tuWPHyfzwEVN
- Argumentsid
- 4
- Argumentsaction
- VoteApprove
- Argumentsproposal
- {"FunctionCall":{"receiver_id":"usdc.potprev.testnet","actions":[{"method_name":"ft_transfer","args":"eyJyZWNlaXZlcl9pZCI6InUtN2sybTlxeGEudXNlcnMucG90cHJldi50ZXN0bmV0IiwiYW1vdW50IjoiMjIwMDAwMDAiLCJtZW1vIjoicG90IHJlZnVuZCJ9","deposit":"1","gas":"5000000000000"},{"method_name":"ft_transfer","args":"eyJyZWNlaXZlcl9pZCI6InUtNGhuOGMyd2QudXNlcnMucG90cHJldi50ZXN0bmV0IiwiYW1vdW50IjoiMjIwMDAwMDAiLCJtZW1vIjoicG90IHJlZnVuZCJ9","deposit":"1","gas":"5000000000000"},{"method_name":"ft_transfer","args":"eyJyZWNlaXZlcl9pZCI6InUtM3A4dzJrZG4udXNlcnMucG90cHJldi50ZXN0bmV0IiwiYW1vdW50IjoiMjIwMDAwMDAiLCJtZW1vIjoicG90IHJlZnVuZCJ9","deposit":"1","gas":"5000000000000"}]}}the DAO rejects the vote unless this equals the stored request exactly (ERR_WRONG_KIND).
- How it's signedNEP-366
- EUN8v1VwAnAGZNx5akGN9Kcb3Gu6cBWa8Jo8oZ8v6xkosha256(6e 01 00 40 ‖ borsh(DelegateAction)), then ed25519. The 4-byte NEP-366 prefix makes it unusable as an ordinary transaction.
Raw JSON
{ "delegate_action": { "sender_id": "u-4hn8c2wd.users.potprev.testnet", "receiver_id": "lisbon-trip-7k2f.pots.potprev.testnet", "actions": [ { "FunctionCall": { "method_name": "act_proposal", "args": { "id": 4, "action": "VoteApprove", "proposal": { "FunctionCall": { "receiver_id": "usdc.potprev.testnet", "actions": [ { "method_name": "ft_transfer", "args": "eyJyZWNlaXZlcl9pZCI6InUtN2sybTlxeGEudXNlcnMucG90cHJldi50ZXN0bmV0IiwiYW1vdW50IjoiMjIwMDAwMDAiLCJtZW1vIjoicG90IHJlZnVuZCJ9", "deposit": "1", "gas": "5000000000000" }, { "method_name": "ft_transfer", "args": "eyJyZWNlaXZlcl9pZCI6InUtNGhuOGMyd2QudXNlcnMucG90cHJldi50ZXN0bmV0IiwiYW1vdW50IjoiMjIwMDAwMDAiLCJtZW1vIjoicG90IHJlZnVuZCJ9", "deposit": "1", "gas": "5000000000000" }, { "method_name": "ft_transfer", "args": "eyJyZWNlaXZlcl9pZCI6InUtM3A4dzJrZG4udXNlcnMucG90cHJldi50ZXN0bmV0IiwiYW1vdW50IjoiMjIwMDAwMDAiLCJtZW1vIjoicG90IHJlZnVuZCJ9", "deposit": "1", "gas": "5000000000000" } ] } } }, "args_base64": "eyJpZCI6NCwiYWN0aW9uIjoiVm90ZUFwcHJvdmUiLCJwcm9wb3NhbCI6eyJGdW5jdGlvbkNhbGwiOnsicmVjZWl2ZXJfaWQiOiJ1c2RjLnBvdHByZXYudGVzdG5ldCIsImFjdGlvbnMiOlt7Im1ldGhvZF9uYW1lIjoiZnRfdHJhbnNmZXIiLCJhcmdzIjoiZXlKeVpXTmxhWFpsY2w5cFpDSTZJblV0TjJzeWJUbHhlR0V1ZFhObGNuTXVjRzkwY0hKbGRpNTBaWE4wYm1WMElpd2lZVzF2ZFc1MElqb2lNakl3TURBd01EQWlMQ0p0Wlcxdklqb2ljRzkwSUhKbFpuVnVaQ0o5IiwiZGVwb3NpdCI6IjEiLCJnYXMiOiI1MDAwMDAwMDAwMDAwIn0seyJtZXRob2RfbmFtZSI6ImZ0X3RyYW5zZmVyIiwiYXJncyI6ImV5SnlaV05sYVhabGNsOXBaQ0k2SW5VdE5HaHVPR015ZDJRdWRYTmxjbk11Y0c5MGNISmxkaTUwWlhOMGJtVjBJaXdpWVcxdmRXNTBJam9pTWpJd01EQXdNREFpTENKdFpXMXZJam9pY0c5MElISmxablZ1WkNKOSIsImRlcG9zaXQiOiIxIiwiZ2FzIjoiNTAwMDAwMDAwMDAwMCJ9LHsibWV0aG9kX25hbWUiOiJmdF90cmFuc2ZlciIsImFyZ3MiOiJleUp5WldObGFYWmxjbDlwWkNJNkluVXRNM0E0ZHpKclpHNHVkWE5sY25NdWNHOTBjSEpsZGk1MFpYTjBibVYwSWl3aVlXMXZkVzUwSWpvaU1qSXdNREF3TURBaUxDSnRaVzF2SWpvaWNHOTBJSEpsWm5WdVpDSjkiLCJkZXBvc2l0IjoiMSIsImdhcyI6IjUwMDAwMDAwMDAwMDAifV19fX0=", "gas": "300000000000000", "deposit": "0" } } ], "nonce": "275000506000003", "max_block_height": "275017775", "public_key": "ed25519:CLqK4ADFQKLeQgxxPwXwiFUDtDMWvApHsPKzfDzmddzX" }, "signature": "ed25519:475HrRbyczw1XH2eobJwFQSmcyN1kmmACqwCaFKwQjfZE1GXg31q6RVXV9PBPQeGC2umotbPm6X9tuWPHyfzwEVN"}
What the app posts to our relayer
IAAAAHUtNGhuOGMyd2QudXNlcnMucG90cHJldi50ZXN0bmV0JQAAAGxpc2Jvbi10cmlwLTdrMmYucG90cy5wb3RwcmV2LnRlc3RuZXQBAAAAAgwAAABhY3RfcHJvcG9zYWy6AgAAeyJpZCI6NCwiYWN0aW9uIjoiVm90ZUFwcHJvdmUiLCJwcm9wb3NhbCI6eyJGdW5jdGlvbkNhbGwiOnsicmVjZWl2ZXJfaWQiOiJ1c2RjLnBvdHByZXYudGVzdG5ldCIsImFjdGlvbnMiOlt7Im1ldGhvZF9uYW1lIjoiZnRfdHJhbnNmZXIiLCJhcmdzIjoiZXlKeVpXTmxhWFpsY2w5cFpDSTZJblV0TjJzeWJUbHhlR0V1ZFhObGNuTXVjRzkwY0hKbGRpNTBaWE4wYm1WMElpd2lZVzF2ZFc1MElqb2lNakl3TURBd01EQWlMQ0p0Wlcxdklqb2ljRzkwSUhKbFpuVnVaQ0o5IiwiZGVwb3NpdCI6IjEiLCJnYXMiOiI1MDAwMDAwMDAwMDAwIn0seyJtZXRob2RfbmFtZSI6ImZ0X3RyYW5zZmVyIiwiYXJncyI6ImV5SnlaV05sYVhabGNsOXBaQ0k2SW5VdE5HaHVPR015ZDJRdWRYTmxjbk11Y0c5MGNISmxkaTUwWlhOMGJtVjBJaXdpWVcxdmRXNTBJam9pTWpJd01EQXdNREFpTENKdFpXMXZJam9pY0c5MElISmxablZ1WkNKOSIsImRlcG9zaXQiOiIxIiwiZ2FzIjoiNTAwMDAwMDAwMDAwMCJ9LHsibWV0aG9kX25hbWUiOiJmdF90cmFuc2ZlciIsImFyZ3MiOiJleUp5WldObGFYWmxjbDlwWkNJNkluVXRNM0E0ZHpKclpHNHVkWE5sY25NdWNHOTBjSEpsZGk1MFpYTjBibVYwSWl3aVlXMXZkVzUwSWpvaU1qSXdNREF3TURBaUxDSnRaVzF2SWpvaWNHOTBJSEpsWm5WdVpDSjkiLCJkZXBvc2l0IjoiMSIsImdhcyI6IjUwMDAwMDAwMDAwMDAifV19fX0AwG4x2RABAAAAAAAAAAAAAAAAAAAAAACDInmLHPoAAC9wZBAAAAAAAKiFN6FOSZbEbwdE0SzKDwGMJzOnfguIu7tg+BhUEFIYAJtJ+VPvwyqYEBe1XPRW46cr8T8qK/qhp/XugxyLGWV9MeNCI8CUo6RxEe3r7pi3qHfUtGJ2hxzX6bsn0N2LagE=
ApprovedProposal #4 in Lisbon Tripa Sputnik DAO
One call to USDC (test) with 3 refunds inside
Pot close-out: refund 66000000 to 3 members
· filed by Pot Hub- Maya gets $22.00
- Sam gets $22.00
- Leo gets $22.00
Who can decide this
Members (3)
2 of 2 approvals- Maya approved
- Sam approvedneeds 2
- Leo not yet
Pot rules seat (1)can't vote on this kindHow “2” is computed
The Hub has only call:AddProposal, so it can file this but never vote on it. Members decide: min(3 × 1 ÷ 2 + 1, 3) = 2.
Raw JSON
{ "id": 4, "proposer": "pots.potprev.testnet", "description": "Pot close-out: refund 66000000 to 3 members", "kind": { "FunctionCall": { "receiver_id": "usdc.potprev.testnet", "actions": [ { "method_name": "ft_transfer", "args": "eyJyZWNlaXZlcl9pZCI6InUtN2sybTlxeGEudXNlcnMucG90cHJldi50ZXN0bmV0IiwiYW1vdW50IjoiMjIwMDAwMDAiLCJtZW1vIjoicG90IHJlZnVuZCJ9", "deposit": "1", "gas": "5000000000000" }, { "method_name": "ft_transfer", "args": "eyJyZWNlaXZlcl9pZCI6InUtNGhuOGMyd2QudXNlcnMucG90cHJldi50ZXN0bmV0IiwiYW1vdW50IjoiMjIwMDAwMDAiLCJtZW1vIjoicG90IHJlZnVuZCJ9", "deposit": "1", "gas": "5000000000000" }, { "method_name": "ft_transfer", "args": "eyJyZWNlaXZlcl9pZCI6InUtM3A4dzJrZG4udXNlcnMucG90cHJldi50ZXN0bmV0IiwiYW1vdW50IjoiMjIwMDAwMDAiLCJtZW1vIjoicG90IHJlZnVuZCJ9", "deposit": "1", "gas": "5000000000000" } ] } }, "status": "Approved", "vote_counts": { "member": [ "2", "0", "0" ] }, "votes": { "u-4hn8c2wd.users.potprev.testnet": "Approve", "u-7k2m9qxa.users.potprev.testnet": "Approve" }, "submission_time": "1794051942000000000", "last_actions_log": [ { "block_height": "275016570" }, { "block_height": "275016573" }, { "block_height": "275017581" } ]}
Good to know
All three refunds run inside one receipt. If any one failed, all would be undone and the group could try again.
What happened on NEAR, receipt by receipt
Receipts 1–5 of 5sim-Aqy7…feb5Simulated
- ·Phone → RelayerThe signed request goes to our relayer (POST /api/relay), off-chain
- 1Relayer → SamDelegate: runs Sam's signed request
- 2Sam → Lisbon Tripact_proposal
(#4, VoteApprove)300 Tgas - 3Lisbon Trip → USDC (test)one receipt, 3 actionsft_transfer
(Maya, $22.00)$22.00ft_transfer (Sam, $22.00)ft_transfer (Leo, $22.00)5 Tgas1 yocto1 yocto1 yocto - 4Callback from USDC (test) to Lisbon Tripcallbackon_proposal_callback
(#4)≈ 279 Tgas - 5Lisbon Trip → Pot HubTransfer 0
Show 5 gas refunds
- 6Gas refund to the relayer · 0.01693 NEAR
- 7Gas refund to the relayer · 0.00020 NEAR
- 8Gas refund to the relayer · 0.27924 NEAR
- 9Gas refund to the relayer · 0.00334 NEAR
- 10Gas refund to the relayer · 0.00048 NEAR
- Step 9 of 10
The relayer tells the Hub, and the Hub marks Lisbon Trip closed.
What our relayer sent
Simulated valuesThe relayer's transaction
- Signersigner_id
- users.
potprev. testnetRelayer - Receiverreceiver_id
- pots.
potprev. testnetPot Hub - Noncenonce
- 274990000000012
- Block hashblock_hash
- sim-F6vC…7eru
- Transactionhash
- sim-FsGb…Vz5Y
A plain transaction the relayer signs itself. sync is open to anyone, so no request rides inside.
sync → Pot Hub · 50 Tgas
Who paid
Relayer · users.
potprev. testnet ≈ 0.0007 NEAR of gas
Copied from a real testnet run of the same call. A simulation can't burn gas.
Raw JSON
{ "hash": "sim-FsGbGJZLbD8VD1RRXbH23wfuXLPFVz5Y", "signer_id": "users.potprev.testnet", "public_key": "ed25519:3q2rinP5DL1aiB33YTh372bu3K3Tf9kwNyAUjEm4xzF3", "nonce": "274990000000012", "receiver_id": "pots.potprev.testnet", "block_hash": "sim-F6vCm9e3XdjFhVDrX6ERC5ptHhWF7eru", "actions": [ { "FunctionCall": { "method_name": "sync", "args": { "pot": "lisbon-trip-7k2f.pots.potprev.testnet", "proposal_id": 4 }, "gas": "50000000000000", "deposit": "0" } } ], "checks": []}
What changed
What changed in the Pot and the Hub Field Before After The Pot's account a Sputnik DAO, no access keys, 0.05 NEAR float a Sputnik DAO, no access keys, 0.05 NEAR float Members (DAO role member) Maya, Sam, Leo Maya, Sam, Leo Group approvals needed 2 of 3 2 of 3 Proposals (changed) #0 Add member · Approved #1 Add member · Approved #2 Transfer · Approved #3 Transfer · Approved #0 Add member · Approved #1 Add member · Approved #2 Transfer · Approved #3 Transfer · Approved #4 Refunds (one call) · Approved Pot status (changed) Open Closed Hub ledger: put in Maya $150.00 · Sam $150.00 · Leo $150.00 Maya $150.00 · Sam $150.00 · Leo $150.00 Hub ledger: spent without asking Maya $84.00 · Sam $0.00 · Leo $0.00 Maya $84.00 · Sam $0.00 · Leo $0.00 Raw JSON
{ "before": { "pot": { "status": "open", "members": [ { "account": "u-7k2m9qxa.users.potprev.testnet", "joinIndex": 0, "contributed": "150000000", "instantUsed": "84000000" }, { "account": "u-4hn8c2wd.users.potprev.testnet", "joinIndex": 1, "contributed": "150000000", "instantUsed": "0" }, { "account": "u-3p8w2kdn.users.potprev.testnet", "joinIndex": 2, "contributed": "150000000", "instantUsed": "0" } ], "ledger": { "totalContributed": "450000000", "spentInstant": "84000000", "spentVote": "300000000", "reserved": "0" }, "requests": [ { "id": 0, "path": "instant", "status": "approved", "amount": "84000000", "proposalId": 2, "category": "groceries", "requestRef": "k3d9x1a2b4c6m7n8" }, { "id": 1, "path": "vote", "status": "approved", "amount": "300000000", "proposalId": 3, "category": "tickets", "requestRef": "p8w2m4q6z1x3r5t7" } ] }, "dao": { "exists": true, "members": [ "u-7k2m9qxa.users.potprev.testnet", "u-4hn8c2wd.users.potprev.testnet", "u-3p8w2kdn.users.potprev.testnet" ], "rulebot": [ "pots.potprev.testnet" ], "threshold": { "k": 2, "n": 3 }, "lastProposalId": 4, "proposals": [ { "id": 0, "proposer": "pots.potprev.testnet", "description": "Pot join via invite", "kind": { "AddMemberToRole": { "member_id": "u-4hn8c2wd.users.potprev.testnet", "role": "member" } }, "status": "Approved", "vote_counts": { "rulebot": [ "1", "0", "0" ] }, "votes": { "pots.potprev.testnet": "Approve" }, "submission_time": "1794042306600000000", "last_actions_log": [ { "block_height": "275000511" }, { "block_height": "275000513" } ] }, { "id": 1, "proposer": "pots.potprev.testnet", "description": "Pot join via invite", "kind": { "AddMemberToRole": { "member_id": "u-3p8w2kdn.users.potprev.testnet", "role": "member" } }, "status": "Approved", "vote_counts": { "rulebot": [ "1", "0", "0" ] }, "votes": { "pots.potprev.testnet": "Approve" }, "submission_time": "1794048017400000000", "last_actions_log": [ { "block_height": "275010029" }, { "block_height": "275010031" } ] }, { "id": 2, "proposer": "pots.potprev.testnet", "description": "Pot request #0: groceries (ref k3d9x1a2b4c6m7n8)", "kind": { "Transfer": { "token_id": "usdc.potprev.testnet", "receiver_id": "u-7k2m9qxa.users.potprev.testnet", "amount": "84000000", "msg": null } }, "status": "Approved", "vote_counts": { "rulebot": [ "1", "0", "0" ] }, "votes": { "pots.potprev.testnet": "Approve" }, "submission_time": "1794048625200000000", "last_actions_log": [ { "block_height": "275011042" }, { "block_height": "275011046" } ] }, { "id": 3, "proposer": "pots.potprev.testnet", "description": "Pot request #1: tickets (ref p8w2m4q6z1x3r5t7)", "kind": { "Transfer": { "token_id": "usdc.potprev.testnet", "receiver_id": "u-7k2m9qxa.users.potprev.testnet", "amount": "300000000", "msg": null } }, "status": "Approved", "vote_counts": { "member": [ "2", "0", "0" ] }, "votes": { "u-3p8w2kdn.users.potprev.testnet": "Approve", "u-7k2m9qxa.users.potprev.testnet": "Approve" }, "submission_time": "1794049231200000000", "last_actions_log": [ { "block_height": "275012052" }, { "block_height": "275012055" }, { "block_height": "275013563" } ] } ], "floatNear": "0.05" } }, "after": { "pot": { "status": "closed", "members": [ { "account": "u-7k2m9qxa.users.potprev.testnet", "joinIndex": 0, "contributed": "150000000", "instantUsed": "84000000" }, { "account": "u-4hn8c2wd.users.potprev.testnet", "joinIndex": 1, "contributed": "150000000", "instantUsed": "0" }, { "account": "u-3p8w2kdn.users.potprev.testnet", "joinIndex": 2, "contributed": "150000000", "instantUsed": "0" } ], "ledger": { "totalContributed": "450000000", "spentInstant": "84000000", "spentVote": "300000000", "reserved": "0" }, "requests": [ { "id": 0, "path": "instant", "status": "approved", "amount": "84000000", "proposalId": 2, "category": "groceries", "requestRef": "k3d9x1a2b4c6m7n8" }, { "id": 1, "path": "vote", "status": "approved", "amount": "300000000", "proposalId": 3, "category": "tickets", "requestRef": "p8w2m4q6z1x3r5t7" } ] }, "dao": { "exists": true, "members": [ "u-7k2m9qxa.users.potprev.testnet", "u-4hn8c2wd.users.potprev.testnet", "u-3p8w2kdn.users.potprev.testnet" ], "rulebot": [ "pots.potprev.testnet" ], "threshold": { "k": 2, "n": 3 }, "lastProposalId": 5, "proposals": [ { "id": 0, "proposer": "pots.potprev.testnet", "description": "Pot join via invite", "kind": { "AddMemberToRole": { "member_id": "u-4hn8c2wd.users.potprev.testnet", "role": "member" } }, "status": "Approved", "vote_counts": { "rulebot": [ "1", "0", "0" ] }, "votes": { "pots.potprev.testnet": "Approve" }, "submission_time": "1794042306600000000", "last_actions_log": [ { "block_height": "275000511" }, { "block_height": "275000513" } ] }, { "id": 1, "proposer": "pots.potprev.testnet", "description": "Pot join via invite", "kind": { "AddMemberToRole": { "member_id": "u-3p8w2kdn.users.potprev.testnet", "role": "member" } }, "status": "Approved", "vote_counts": { "rulebot": [ "1", "0", "0" ] }, "votes": { "pots.potprev.testnet": "Approve" }, "submission_time": "1794048017400000000", "last_actions_log": [ { "block_height": "275010029" }, { "block_height": "275010031" } ] }, { "id": 2, "proposer": "pots.potprev.testnet", "description": "Pot request #0: groceries (ref k3d9x1a2b4c6m7n8)", "kind": { "Transfer": { "token_id": "usdc.potprev.testnet", "receiver_id": "u-7k2m9qxa.users.potprev.testnet", "amount": "84000000", "msg": null } }, "status": "Approved", "vote_counts": { "rulebot": [ "1", "0", "0" ] }, "votes": { "pots.potprev.testnet": "Approve" }, "submission_time": "1794048625200000000", "last_actions_log": [ { "block_height": "275011042" }, { "block_height": "275011046" } ] }, { "id": 3, "proposer": "pots.potprev.testnet", "description": "Pot request #1: tickets (ref p8w2m4q6z1x3r5t7)", "kind": { "Transfer": { "token_id": "usdc.potprev.testnet", "receiver_id": "u-7k2m9qxa.users.potprev.testnet", "amount": "300000000", "msg": null } }, "status": "Approved", "vote_counts": { "member": [ "2", "0", "0" ] }, "votes": { "u-3p8w2kdn.users.potprev.testnet": "Approve", "u-7k2m9qxa.users.potprev.testnet": "Approve" }, "submission_time": "1794049231200000000", "last_actions_log": [ { "block_height": "275012052" }, { "block_height": "275012055" }, { "block_height": "275013563" } ] }, { "id": 4, "proposer": "pots.potprev.testnet", "description": "Pot close-out: refund 66000000 to 3 members", "kind": { "FunctionCall": { "receiver_id": "usdc.potprev.testnet", "actions": [ { "method_name": "ft_transfer", "args": "eyJyZWNlaXZlcl9pZCI6InUtN2sybTlxeGEudXNlcnMucG90cHJldi50ZXN0bmV0IiwiYW1vdW50IjoiMjIwMDAwMDAiLCJtZW1vIjoicG90IHJlZnVuZCJ9", "deposit": "1", "gas": "5000000000000" }, { "method_name": "ft_transfer", "args": "eyJyZWNlaXZlcl9pZCI6InUtNGhuOGMyd2QudXNlcnMucG90cHJldi50ZXN0bmV0IiwiYW1vdW50IjoiMjIwMDAwMDAiLCJtZW1vIjoicG90IHJlZnVuZCJ9", "deposit": "1", "gas": "5000000000000" }, { "method_name": "ft_transfer", "args": "eyJyZWNlaXZlcl9pZCI6InUtM3A4dzJrZG4udXNlcnMucG90cHJldi50ZXN0bmV0IiwiYW1vdW50IjoiMjIwMDAwMDAiLCJtZW1vIjoicG90IHJlZnVuZCJ9", "deposit": "1", "gas": "5000000000000" } ] } }, "status": "Approved", "vote_counts": { "member": [ "2", "0", "0" ] }, "votes": { "u-4hn8c2wd.users.potprev.testnet": "Approve", "u-7k2m9qxa.users.potprev.testnet": "Approve" }, "submission_time": "1794051942000000000", "last_actions_log": [ { "block_height": "275016570" }, { "block_height": "275016573" }, { "block_height": "275017581" } ] } ], "floatNear": "0.05", "policy": { "roles": [ { "name": "member", "kind": { "Group": [ "u-7k2m9qxa.users.potprev.testnet", "u-4hn8c2wd.users.potprev.testnet", "u-3p8w2kdn.users.potprev.testnet" ] }, "permissions": [ "*:VoteApprove", "*:VoteReject" ], "vote_policy": {} }, { "name": "rulebot", "kind": { "Group": [ "pots.potprev.testnet" ] }, "permissions": [ "transfer:AddProposal", "transfer:VoteApprove", "transfer:Finalize", "add_member_to_role:AddProposal", "add_member_to_role:VoteApprove", "call:AddProposal", "call:Finalize" ], "vote_policy": { "transfer": { "weight_kind": "RoleWeight", "quorum": "1", "threshold": [ 1, 1 ] }, "add_member_to_role": { "weight_kind": "RoleWeight", "quorum": "1", "threshold": [ 1, 1 ] } } } ], "default_vote_policy": { "weight_kind": "RoleWeight", "quorum": "1", "threshold": [ 1, 2 ] }, "proposal_bond": "0", "proposal_period": "604800000000000", "bounty_bond": "0", "bounty_forgiveness_period": "86400000000000" } } }}
What happened on NEAR, receipt by receipt
Receipts 1–3 of 3sim-FsGb…Vz5YSimulated
- 1Relayer → Pot Hubsync
(Lisbon Trip, #4)50 Tgas - 2Pot Hub → Lisbon Tripget_proposal
(#4)10 Tgasreturned #4 Approved - 3Callback from Lisbon Trip to Pot Hubcallbackon_sync
(Lisbon Trip, #4)10 Tgasreturned proposal_id, subject…
Show 3 gas refunds
- 4Gas refund to the relayer · 0.01071 NEAR
- 5Gas refund to the relayer · 0.01062 NEAR
- 6Gas refund to the relayer · 0.02885 NEAR
- Step 10 of 10In the app
Everyone got their share back: $22.00 each.
Balances
Dollars Account Before After Change Maya $384.00 $406.00 + $22.00in Sam $0.00 $22.00 + $22.00in Leo $0.00 $22.00 + $22.00in Lisbon Trip $66.00 $0.00 − $66.00out Pot Hub $0.00 $0.00 no change NEAR (fees and storage)
- Maya0 NEAR
- Sam0 NEAR
- Lisbon Trip− 3 yoctoNEAR
- Pot Hub− 0.01452 NEAR
- Relayer− ≈ 0.0038 NEAR
Copied from a real testnet run of the same call. A simulation can't burn gas.
Raw JSON
{ "before": { "u-7k2m9qxa.users.potprev.testnet": { "usdc": "384000000", "near": "0" }, "u-4hn8c2wd.users.potprev.testnet": { "usdc": "0", "near": "0" }, "u-3p8w2kdn.users.potprev.testnet": { "usdc": "0", "near": "0" }, "pots.potprev.testnet": { "usdc": "0" }, "lisbon-trip-7k2f.pots.potprev.testnet": { "usdc": "66000000" } }, "after": { "u-7k2m9qxa.users.potprev.testnet": { "usdc": "406000000", "near": "0" }, "u-4hn8c2wd.users.potprev.testnet": { "usdc": "22000000", "near": "0" }, "u-3p8w2kdn.users.potprev.testnet": { "usdc": "22000000", "near": "0" }, "pots.potprev.testnet": { "usdc": "0" }, "lisbon-trip-7k2f.pots.potprev.testnet": { "usdc": "0" } }}
Good to know
The $22 now sits in each person's Pot account. Pot has no cash-out yet; this is test money on testnet.
After this chapter
Who paid
Relayer · users.
potprev. testnet ≈ 0.0038 NEAR of gas
Pot Hub · pots.
potprev. testnet 0.01452 NEAR storage prepay for the new proposal
Lisbon Trip · lisbon-trip-7k2f.
pots. potprev. testnet 3 yoctoNEAR on a token transfer
Maya · u-7k2m9qxa.
users. potprev. testnet Maya: 0 NEAR. Their account has 0 NEAR before and after.
Sam · u-4hn8c2wd.
users. potprev. testnet Sam: 0 NEAR. Their account has 0 NEAR before and after.
Copied from a real testnet run of the same call. A simulation can't burn gas.
See a real one on testnet
Maya asking to close the reference Pot: one request that holds every member's refund, as here.
Real · testnet (opens nearblocks.io in a new tab)EjggW51daS5saiJ3w4cSRJ2WJ2siKfmMnTKLAKJPsSSYSam's approval that decided the reference Pot's close: all three refunds in one receipt, as here.
Real · testnet (opens nearblocks.io in a new tab)6Si9m42frwXBTiKTWbd3Xgmz895uULXSsbcPsH9Vken7
Words in this chapter
11 words
- DAO
- A shared account run by rules instead of a person. Here every Pot is one, and its members approve what it does.
- proposal
- A request filed in the DAO, like “pay Maya $84” or “add Leo”. It runs only once enough seats approve it.
- pro-rata
- Split by what each person put in: everyone put in $150, so everyone gets an equal third.
- threshold
- How many approvals a role needs. Members need a majority: 2 of 3 here.
- sync
- A Hub method anyone can call to copy a proposal's final status from the Pot into the Hub's records.
- delegate action
- A request the user signs but doesn't send (NEP-366). Someone else wraps it in a transaction and pays for it; contracts still see the user as the caller.
- relayer
- Pot's server account (users.potprev.testnet) that sends signed requests and pays their gas, so users need 0 NEAR. It refuses anything outside an allowlist.
- receipt
- One step of a transaction's work, run by one account. A transaction fans out into receipts as contracts call each other.
- callback (then)
- A receipt that runs after another finishes and reads its result.
- storage prepay
- NEAR the Hub sends with each proposal, so the Pot can pay for the bytes it stores.
- gas / Tgas
- The work a call may use, bought in NEAR. 1 Tgas is a trillion gas, about 0.0001 NEAR. Unused gas is refunded.
What you just saw
| Chapter | Signatures by people | Transactions | Receipts | Who decided | NEAR paid by people |
|---|---|---|---|---|---|
| 1 · Start a Pot | 1 | 1 | 6 | No decision needed | 0 |
| 2 · Join | 2 | 1 | 8 | Pot rules (the Hub's seat) | 0 |
| 3 · Add money | 1 | 1 | 6 | No decision needed | 0 |
| 4 · Spend | 1 | 1 | 14 | Pot rules (the Hub's seat) | 0 |
| 5 · Ask the group | 3 | 4 | 18 | The group, by majority | 0 |
| 6 · Close | 3 | 4 | 16 | The group, by majority | 0 |
| Total | 11 | 12 | 68 | 0 |
Check the real thing yourself (read-only, no keys)
These commands read the real reference Pot on testnet with near-cli-rs: the same story ran there, from the first invite to the close. They sign nothing.
near() { npx -y near-cli-rs@0.30.1 "$@"; }
ROOT=potprev.testnet; HUB=pots.$ROOT; POT=reference-trip-2gzw.$HUB
near contract call-function as-read-only $POT get_policy json-args '{}' network-config testnet now
near contract call-function as-read-only $HUB get_pot json-args "{\"pot\":\"$POT\"}" network-config testnet now
near contract call-function as-read-only usdc.$ROOT ft_balance_of json-args "{\"account_id\":\"$POT\"}" network-config testnet now
near account view-account-summary $POT network-config testnet now
near account list-keys $POT network-config testnet nowEvery word, explained
29 words
- DAO
- A shared account run by rules instead of a person. Here every Pot is one, and its members approve what it does.
- Sputnik DAO
- NEAR's standard DAO contract. Pot uses its official code, installed by hash, unchanged.
- Pot Hub
- Pot's own contract (pots.potprev.testnet). It creates each Pot, checks the rules, holds the rulebot seat and keeps the ledger of who put in what.
- rulebot
- The Pot Hub's seat in every Pot. One vote from it approves transfers and new members, and the Hub only casts it when the Pot's rules allow.
- role / seat
- A named group in the DAO with its own powers. A Pot has two: member (the friends) and rulebot (only the Pot Hub).
- policy
- The DAO's rulebook: its roles, their powers, thresholds and the 7-day voting period.
- threshold
- How many approvals a role needs. Members need a majority: 2 of 3 here.
- quorum
- The minimum number of approvals ever accepted. Pot sets it to 1, so nothing can pass with zero votes.
- delegate action
- A request the user signs but doesn't send (NEP-366). Someone else wraps it in a transaction and pays for it; contracts still see the user as the caller.
- relayer
- Pot's server account (users.potprev.testnet) that sends signed requests and pays their gas, so users need 0 NEAR. It refuses anything outside an allowlist.
- transaction
- What gets submitted to NEAR. Here the relayer signs it, and the user's request rides inside.
- receipt
- One step of a transaction's work, run by one account. A transaction fans out into receipts as contracts call each other.
- callback (then)
- A receipt that runs after another finishes and reads its result.
- gas / Tgas
- The work a call may use, bought in NEAR. 1 Tgas is a trillion gas, about 0.0001 NEAR. Unused gas is refunded.
- deposit
- NEAR attached to a call. In Pot it is 0 everywhere except that 1 yoctoNEAR.
- nonce
- A counter on each key. Every signature uses a higher one, so an old signature can't be replayed.
- max_block_height
- The block after which a signed request expires: about 2 minutes here.
- device key
- The key Pot keeps on your phone (non-extractable). It signs your requests without a popup.
- ed25519 signature
- The kind of digital signature NEAR uses: 64 bytes that prove a key signed exactly this message.
- invite key
- A key made for one invite link. Its secret lives after the # in the link; the Hub stores only its public half.
- account (named)
- A NEAR account like u-3p8w2kdn.users.potprev.testnet. Pot creates it for you with 0 NEAR; your name lives off-chain.
- global contract
- Contract code stored once on NEAR and used by many accounts by its hash. Every Pot uses Sputnik's this way, for 0.012 NEAR instead of 5.4.
- USDC (test)
- Pot's test dollar token on testnet (NEP-141, 6 decimals). It has no value.
- proposal
- A request filed in the DAO, like “pay Maya $84” or “add Leo”. It runs only once enough seats approve it.
- storage prepay
- NEAR the Hub sends with each proposal, so the Pot can pay for the bytes it stores.
- ft_transfer_call
- Send tokens to a contract and call it in the same step. If the contract refuses, the tokens come back.
- yoctoNEAR
- The smallest unit of NEAR: 10⁻²⁴ NEAR. Token transfers must attach exactly 1, to prove a full-access key signed them.
- sync
- A Hub method anyone can call to copy a proposal's final status from the Pot into the Hub's records.
- pro-rata
- Split by what each person put in: everyone put in $150, so everyone gets an equal third.