Skip to content

Running on testnet

Pot is running on NEAR testnet, so the money here is test money and nothing real is at risk. Pot is built for NEAR mainnet, where it will use real dollars. On testnet you sign in with a NEAR wallet; Google sign-in comes with mainnet.

Simulation

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

  1. Step 1 of 8In the app

    Maya names the Pot and picks a cover.

  2. 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.

  3. Step 3 of 8In the app

    Her phone signs one request: “Pot Hub, create Lisbon Trip for me.” No popup, no fee.

  4. Step 4 of 8

    This is all she signed. The Hub is the only contract it can reach.

    What Maya signed

    Simulated values
    device key · ed255…qWnt

    Ask 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==

  5. 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 values

    The 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

    Maya's signed request rides inside

    create_pot → Pot Hub

    What Maya signed

    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

    1. ·Phone → RelayerThe signed request goes to our relayer (POST /api/relay), off-chain
    2. 1Relayer → MayaDelegate: runs Maya's signed request
    3. 2Maya → Pot Hubcreate_pot300 Tgas
  6. 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

    1. 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.

  7. 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

    1. 4Callback from Lisbon Trip to Pot Hubcallbackon_pot_created40 Tgas
    2. 5Pot Hub → USDC (test)storage_deposit(Lisbon Trip)10 Tgas0.00125 NEARreturned 0.00125 NEAR
    3. 6Callback from USDC (test) to Pot Hubcallbackon_dao_registered10 Tgasreturned ok
    Show 6 gas refunds
    1. 7Gas refund to the relayer · 0.02114 NEAR
    2. 8Gas refund to the relayer · 0.01073 NEAR
    3. 9Gas refund to the relayer · 0.01071 NEAR
    4. 10Gas refund to the relayer · 0.01873 NEAR
    5. 11Gas refund to the relayer · 0.23118 NEAR
    6. 12Gas refund to the relayer · 0.00046 NEAR
  8. 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

What changed in the Pot and the Hub
FieldBeforeAfter
The Pot's account (changed)doesn't exista 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)noneOpen
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

Dollars
AccountBeforeAfterChange
Maya$0.00$0.00no change
Lisbon Trip$0.00$0.00no change
Pot Hub$0.00$0.00no 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

  1. Step 1 of 8In the app

    Leo sees who invited him, who's in, and the rules, before he commits to anything.

  2. Step 2 of 8In the app

    Joining takes two signatures, both on Leo's phone.

  3. 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=="}
  4. Step 4 of 8

    Then his own key signs the request to the Hub, with that proof inside.

    What Leo signed

    Simulated values
    device key · ed255…HJir

    Ask 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

  5. 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 values

    The 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

    Leo's signed request rides inside

    join → Pot Hub

    What Leo signed

    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

    1. ·Phone → RelayerThe signed request goes to our relayer (POST /api/relay), off-chain
    2. 1Relayer → LeoDelegate: runs Leo's signed request
    3. 2Leo → Pot Hubjoin(Lisbon Trip)100 Tgas
    4. 3Pot Hub → Lisbon Tripone receipt, 2 actionsTransfer 0.00513 NEARadd_proposal10 Tgasreturned 1
  6. 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.

    Proposal #1 in Lisbon Tripa Sputnik DAO

    Approved

    Add Leo as a member

    Pot join via invite · filed by Pot Hub

    Who can decide this

    Members (3)

    weren't asked

    Pot rules seat (1)
    1. Pot Hub approved
    2. needs 1
    decides alone (1 of 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

    1. 4Callback from Lisbon Trip to Pot Hubcallbackon_join_added(Lisbon Trip)60 Tgas
    2. 5Pot Hub → Lisbon Tripact_proposal(#1, VoteApprove)20 Tgas
    3. 6Lisbon Trip → Pot HubTransfer 0
  7. 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
    FieldBeforeAfter
    The Pot's accounta Sputnik DAO, no access keys, 0.05 NEAR floata Sputnik DAO, no access keys, 0.05 NEAR float
    Members (DAO role member) (changed)Maya, SamMaya, Sam, Leo
    Group approvals needed (changed)2 of 22 of 3
    Proposals (changed)#0 Add member · Approved#0 Add member · Approved #1 Add member · Approved
    Pot statusOpenOpen
    Hub ledger: put in (changed)Maya $150.00 · Sam $150.00Maya $150.00 · Sam $150.00 · Leo $0.00
    Hub ledger: spent without asking (changed)Maya $0.00 · Sam $0.00Maya $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

    1. 7Pot Hub → Lisbon Tripget_proposal(#1)10 Tgasreturned #1 Approved
    2. 8Callback from Lisbon Trip to Pot Hubcallbackon_join_status(Lisbon Trip)10 Tgasreturned status active
    Show 8 gas refunds
    1. 9Gas refund to the relayer · 0.01079 NEAR
    2. 10Gas refund to the relayer · 0.00020 NEAR
    3. 11Gas refund to the relayer · 0.02040 NEAR
    4. 12Gas refund to the relayer · 0.01072 NEAR
    5. 13Gas refund to the relayer · 0.01061 NEAR
    6. 14Gas refund to the relayer · 0.01785 NEAR
    7. 15Gas refund to the relayer · 0.02860 NEAR
    8. 16Gas refund to the relayer · 0.00046 NEAR
  8. Step 8 of 8In the app

    Leo is in, without ever paying a fee.

After this chapter

Balances

Dollars
AccountBeforeAfterChange
Maya$0.00$0.00no change
Sam$0.00$0.00no change
Leo$0.00$0.00no change
Lisbon Trip$300.00$300.00no change
Pot Hub$0.00$0.00no 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

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.00
  • Maya added $150

    + $150.00
  • Sam 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

  1. Step 1 of 7In the app

    Leo adds his $150 share. The sheet shows the result before he taps.

  2. Step 2 of 7In the app

    His phone signs one request, this time to the dollar token itself.

  3. 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 values
    device key · ed255…HJir

    Ask 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==

  4. 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 values

    The 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

    Leo's signed request rides inside

    ft_transfer_call → USDC (test)

    What Leo signed

    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    }  ]}
  5. 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

    1. ·Phone → RelayerThe signed request goes to our relayer (POST /api/relay), off-chain
    2. 1Relayer → LeoDelegate: runs Leo's signed request
    3. 2Leo → USDC (test)ft_transfer_call(Pot Hub, $150.00)$150.00100 Tgas1 yoctopaid by the relayer
    4. 3USDC (test) → Pot Hubft_on_transfer(Leo, $150.00)≈ 91 Tgas
  6. 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

    1. 4Pot Hub → USDC (test)ft_transfer(Lisbon Trip, $150.00)$150.0010 Tgas1 yocto
    2. 5Callback from USDC (test) to Pot Hubcallbackon_contribution_forwarded(Lisbon Trip, Leo, $150.00)10 Tgasreturned "0"
    3. 6Callback from Pot Hub to USDC (test)callbackft_resolve_transfer(Pot Hub, Leo, $150.00)5 Tgasreturned "150000000"
    Show 6 gas refunds
    1. 7Gas refund to the relayer · 0.01070 NEAR
    2. 8Gas refund to the relayer · 0.01063 NEAR
    3. 9Gas refund to the relayer · 0.07030 NEAR
    4. 10Gas refund to the relayer · 0.00575 NEAR
    5. 11Gas refund to the relayer · 0.00235 NEAR
    6. 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.

  7. Step 7 of 7In the app

    The Pot is full. Everyone's phone shows it.

After this chapter

What changed

What changed in the Pot and the Hub
FieldBeforeAfter
The Pot's accounta Sputnik DAO, no access keys, 0.05 NEAR floata Sputnik DAO, no access keys, 0.05 NEAR float
Members (DAO role member)Maya, Sam, LeoMaya, Sam, Leo
Group approvals needed2 of 32 of 3
Proposals#0 Add member · Approved #1 Add member · Approved#0 Add member · Approved #1 Add member · Approved
Pot statusOpenOpen
Hub ledger: put in (changed)Maya $150.00 · Sam $150.00 · Leo $0.00Maya $150.00 · Sam $150.00 · Leo $150.00
Hub ledger: spent without askingMaya $0.00 · Sam $0.00 · Leo $0.00Maya $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

Dollars
AccountBeforeAfterChange
Maya$0.00$0.00no change
Sam$0.00$0.00no change
Leo$150.00$0.00− $150.00out
Lisbon Trip$300.00$450.00+ $150.00in
Pot Hub$0.00$0.00no 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

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

$84.00Groceries 🛒

Approved by Pot rules

Under the $100 instant limit

$84 sent to you

Sent
In the Pot$366.00

Under 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

  1. Step 1 of 9In the app

    Maya paid $84 for groceries and wants it back. The sheet already says the rules allow it.

  2. Step 2 of 9In the app

    Her phone signs one request to the Hub.

  3. 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 values
    device key · ed255…qWnt

    Ask 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

  4. Step 4 of 9

    The relayer sends it and pays. Maya: still 0 NEAR.

    What our relayer sent

    Simulated values

    The 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

    Maya's signed request rides inside

    request_spend → Pot Hub

    What Maya signed

    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

    1. ·Phone → RelayerThe signed request goes to our relayer (POST /api/relay), off-chain
    2. 1Relayer → MayaDelegate: runs Maya's signed request
    3. 2Maya → Pot Hubrequest_spend(Lisbon Trip, Maya, $84.00)150 Tgas
  5. 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

    CheckValueResult
    The amount is within the instant limitinstant_cap$84 ≤ $100Yes
    It fits the $200 per person without askingmember_budget$0 + $84 ≤ $200Yes
    The money goes to a memberinstant_payeesmemberYes
    The requester has put money incontributed$150Yes
    Path: instant (within_rules)

    What happened on NEAR, receipt by receipt

    Receipts 3–6 of 14sim-DabH…zpCoSimulated

    1. 3Pot Hub → USDC (test)ft_balance_of(Lisbon Trip)5 Tgasin parallelreturned "450000000"
    2. 4Pot Hub → USDC (test)storage_balance_of(Maya)5 Tgasin parallelreturned 0.00125 NEAR
    3. 5Pot Hub → Lisbon Tripget_policy10 Tgasin parallelreturned policy
    4. 6Callback from USDC (test) to Pot Hubcallbackon_spend_checked(Lisbon Trip)105 Tgas
  6. Step 6 of 9

    Inside the rules, the Hub files a payout request in the Pot: $84 to Maya.

    Proposal #2 in Lisbon Tripa Sputnik DAO

    In progress

    Transfer · $84.00 USDC (test) → Maya

    Pot request #0: groceries (ref k3d9x1a2b4c6m7n8) · filed by Pot Hub

    Who can decide this

    Members (3)

    weren't asked

    Pot rules seat (1)
    1. Pot Hub not yet
    2. needs 1
    decides alone (1 of 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

    1. 7Pot Hub → Lisbon Tripone receipt, 2 actionsTransfer 0.00586 NEARadd_proposal10 Tgasreturned 2
    2. 8Callback from Lisbon Trip to Pot Hubcallbackon_spend_added(Lisbon Trip)80 Tgas
  7. Step 7 of 9In the app

    The Hub approves with its seat. That one vote is enough, because the rules said yes.

    Proposal #2 in Lisbon Tripa Sputnik DAO

    Approved

    Transfer · $84.00 USDC (test) → Maya

    Pot request #0: groceries (ref k3d9x1a2b4c6m7n8) · filed by Pot Hub

    Who can decide this

    Members (3)

    weren't asked

    Pot rules seat (1)
    1. Pot Hub approved
    2. needs 1
    decides alone (1 of 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

    1. 9Pot Hub → Lisbon Tripact_proposal(#2, VoteApprove)40 Tgas
  8. 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

    1. 10Lisbon Trip → USDC (test)ft_transfer(Maya, $84.00)$84.00≈ 18 Tgas1 yocto
    2. 11Callback from USDC (test) to Lisbon Tripcallbackon_proposal_callback(#2)≈ 18 Tgas
    3. 12Lisbon Trip → Pot HubTransfer 0
    4. 13Pot Hub → Lisbon Tripget_proposal(#2)10 Tgasreturned #2 Approved
    5. 14Callback from Lisbon Trip to Pot Hubcallbackon_spend_status(Lisbon Trip)10 Tgasreturned status approved
    Show 14 gas refunds
    1. 15Gas refund to the relayer · 0.00576 NEAR
    2. 16Gas refund to the relayer · 0.00576 NEAR
    3. 17Gas refund to the relayer · 0.01071 NEAR
    4. 18Gas refund to the relayer · 0.01078 NEAR
    5. 19Gas refund to the relayer · 0.01872 NEAR
    6. 20Gas refund to the relayer · 0.00020 NEAR
    7. 21Gas refund to the relayer · 0.01845 NEAR
    8. 22Gas refund to the relayer · 0.00275 NEAR
    9. 23Gas refund to the relayer · 0.01072 NEAR
    10. 24Gas refund to the relayer · 0.01063 NEAR
    11. 25Gas refund to the relayer · 0.01783 NEAR
    12. 26Gas refund to the relayer · 0.01365 NEAR
    13. 27Gas refund to the relayer · 0.02193 NEAR
    14. 28Gas refund to the relayer · 0.00046 NEAR
  9. Step 9 of 9In the app

    About five seconds after her tap, the money is hers.

    Balances

    Dollars
    AccountBeforeAfterChange
    Maya$0.00$84.00+ $84.00in
    Sam$0.00$0.00no change
    Leo$0.00$0.00no change
    Lisbon Trip$450.00$366.00− $84.00out
    Pot Hub$0.00$0.00no 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

What changed in the Pot and the Hub
FieldBeforeAfter
The Pot's accounta Sputnik DAO, no access keys, 0.05 NEAR floata Sputnik DAO, no access keys, 0.05 NEAR float
Members (DAO role member)Maya, Sam, LeoMaya, Sam, Leo
Group approvals needed2 of 32 of 3
Proposals (changed)#0 Add member · Approved #1 Add member · Approved#0 Add member · Approved #1 Add member · Approved #2 Transfer · Approved
Pot statusOpenOpen
Hub ledger: put inMaya $150.00 · Sam $150.00 · Leo $150.00Maya $150.00 · Sam $150.00 · Leo $150.00
Hub ledger: spent without asking (changed)Maya $0.00 · Sam $0.00 · Leo $0.00Maya $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

$300.00For tickets

Done. Your OK sent $300 to Maya.

One OK needed

Maya · asked, approval counted

You · gave the OK

In the Pot$66.00

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

  1. Step 1 of 10In the app

    Maya wants $300 for the boat. That's over the limit, so the app says the group decides.

  2. Step 2 of 10In the app

    She signs the same kind of request as for groceries.

  3. 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 values
    device key · ed255…qWnt

    Ask 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

    CheckValueResult
    The amount is within the instant limitinstant_capthe rule that decided$300 > $100→ the group
    Path: the group (over_cap)

    Proposal #3 in Lisbon Tripa Sputnik DAO

    In progress

    Transfer · $300.00 USDC (test) → Maya

    Pot request #1: tickets (ref p8w2m4q6z1x3r5t7) · filed by Pot Hub

    Who can decide this

    Members (3)

    1. Maya not yet
    2. Sam not yetneeds 2
    3. Leo not yet
    0 of 2 approvals
    Pot rules seat (1)can't vote outside the rules
    How “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

    1. ·Phone → RelayerThe signed request goes to our relayer (POST /api/relay), off-chain
    2. 1Relayer → MayaDelegate: runs Maya's signed request
    3. 2Maya → Pot Hubrequest_spend(Lisbon Trip, Maya, $300.00)150 Tgas
    4. 3Pot Hub → USDC (test)ft_balance_of(Lisbon Trip)5 Tgasin parallelreturned "366000000"
    5. 4Pot Hub → USDC (test)storage_balance_of(Maya)5 Tgasin parallelreturned 0.00125 NEAR
    6. 5Pot Hub → Lisbon Tripget_policy10 Tgasin parallelreturned policy
    7. 6Callback from USDC (test) to Pot Hubcallbackon_spend_checked(Lisbon Trip)105 Tgas
    8. 7Pot Hub → Lisbon Tripone receipt, 2 actionsTransfer 0.00885 NEARadd_proposal10 Tgasreturned 3
    9. 8Callback from Lisbon Trip to Pot Hubcallbackon_spend_added(Lisbon Trip)80 Tgasreturned status pending
    Show 8 gas refunds
    1. 9Gas refund to the relayer · 0.00576 NEAR
    2. 10Gas refund to the relayer · 0.00576 NEAR
    3. 11Gas refund to the relayer · 0.01070 NEAR
    4. 12Gas refund to the relayer · 0.01078 NEAR
    5. 13Gas refund to the relayer · 0.08061 NEAR
    6. 14Gas refund to the relayer · 0.01365 NEAR
    7. 15Gas refund to the relayer · 0.02193 NEAR
    8. 16Gas refund to the relayer · 0.00046 NEAR
  4. 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 values
    device key · ed255…qWnt

    Tell 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==

    Proposal #3 in Lisbon Tripa Sputnik DAO

    In progress

    Transfer · $300.00 USDC (test) → Maya

    Pot request #1: tickets (ref p8w2m4q6z1x3r5t7) · filed by Pot Hub

    Who can decide this

    Members (3)

    1. Maya approved
    2. Sam not yetneeds 2
    3. Leo not yet
    1 of 2 approvals
    Pot rules seat (1)can't vote outside the rules
    How “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

    1. ·Phone → RelayerThe signed request goes to our relayer (POST /api/relay), off-chain
    2. 1Relayer → MayaDelegate: runs Maya's signed request
    3. 2Maya → Lisbon Tripact_proposal(#3, VoteApprove)100 Tgas
    Show 2 gas refunds
    1. 3Gas refund to the relayer · 0.10065 NEAR
    2. 4Gas refund to the relayer · 0.00046 NEAR
  5. Step 5 of 10In the app

    One more approval is needed. Leo and Sam get a notification.

  6. Step 6 of 10In the app

    Now you're Leo. He reads what he'd approve: $300 to Maya, for tickets.

  7. Step 7 of 10In the app

    Leo signs one vote, sent straight to the Pot, not to the Hub.

  8. Step 8 of 10

    That's the second of two. The Pot pays Maya $300 in the same moment.

    What Leo signed

    Simulated values
    device key · ed255…HJir

    Tell 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==

    Proposal #3 in Lisbon Tripa Sputnik DAO

    Approved

    Transfer · $300.00 USDC (test) → Maya

    Pot request #1: tickets (ref p8w2m4q6z1x3r5t7) · filed by Pot Hub

    Who can decide this

    Members (3)

    1. Maya approved
    2. Leo approvedneeds 2
    3. Sam not yet
    2 of 2 approvals
    Pot rules seat (1)can't vote outside the rules
    How “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

    1. ·Phone → RelayerThe signed request goes to our relayer (POST /api/relay), off-chain
    2. 1Relayer → LeoDelegate: runs Leo's signed request
    3. 2Leo → Lisbon Tripact_proposal(#3, VoteApprove)100 Tgas
    4. 3Lisbon Trip → USDC (test)ft_transfer(Maya, $300.00)$300.00≈ 48 Tgas1 yocto
    5. 4Callback from USDC (test) to Lisbon Tripcallbackon_proposal_callback(#3)≈ 48 Tgas
    6. 5Lisbon Trip → Pot HubTransfer 0
    Show 5 gas refunds
    1. 6Gas refund to the relayer · 0.04868 NEAR
    2. 7Gas refund to the relayer · 0.00020 NEAR
    3. 8Gas refund to the relayer · 0.04840 NEAR
    4. 9Gas refund to the relayer · 0.00282 NEAR
    5. 10Gas refund to the relayer · 0.00046 NEAR
  9. Step 9 of 10

    Our relayer then tells the Hub what happened, so its records match the Pot.

    What our relayer sent

    Simulated values

    The 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

    1. 1Relayer → Pot Hubsync(Lisbon Trip, #3)50 Tgas
    2. 2Pot Hub → Lisbon Tripget_proposal(#3)10 Tgasreturned #3 Approved
    3. 3Callback from Lisbon Trip to Pot Hubcallbackon_sync(Lisbon Trip, #3)10 Tgasreturned proposal_id, subject…
    Show 3 gas refunds
    1. 4Gas refund to the relayer · 0.01071 NEAR
    2. 5Gas refund to the relayer · 0.01062 NEAR
    3. 6Gas refund to the relayer · 0.02885 NEAR
  10. Step 10 of 10In the app

    Done: $66 left in the Pot.

    Balances

    Dollars
    AccountBeforeAfterChange
    Maya$84.00$384.00+ $300.00in
    Sam$0.00$0.00no change
    Leo$0.00$0.00no change
    Lisbon Trip$366.00$66.00− $300.00out
    Pot Hub$0.00$0.00no 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

What changed in the Pot and the Hub
FieldBeforeAfter
The Pot's accounta Sputnik DAO, no access keys, 0.05 NEAR floata Sputnik DAO, no access keys, 0.05 NEAR float
Members (DAO role member)Maya, Sam, LeoMaya, Sam, Leo
Group approvals needed2 of 32 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 statusOpenOpen
Hub ledger: put inMaya $150.00 · Sam $150.00 · Leo $150.00Maya $150.00 · Sam $150.00 · Leo $150.00
Hub ledger: spent without askingMaya $84.00 · Sam $0.00 · Leo $0.00Maya $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)9K4beHZV1BkxKvXgaQ2WY8KAz5MPrHSkHTj5NqGL1HVr
  • The 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.

Who got what back
PersonPut inGets 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

  1. Step 1 of 10In the app

    The trip is over. The app shows what each person would get back before anyone asks.

  2. Step 2 of 10In the app

    Maya signs one request to the Hub.

  3. 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 values
    device key · ed255…qWnt

    Ask 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

    1. ·Phone → RelayerThe signed request goes to our relayer (POST /api/relay), off-chain
    2. 1Relayer → MayaDelegate: runs Maya's signed request
    3. 2Maya → Pot Hubpropose_close(Lisbon Trip)150 Tgas
    4. 3Pot Hub → USDC (test)ft_balance_of(Lisbon Trip)5 Tgasreturned "66000000"
    5. 4Callback from USDC (test) to Pot Hubcallbackon_close_balance(Lisbon Trip)60 Tgas
  4. Step 4 of 10

    It files one request in the Pot that holds all three refunds. From now on, nobody can add or spend.

    Proposal #4 in Lisbon Tripa Sputnik DAO

    In progress

    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. Maya not yet
    2. Sam not yetneeds 2
    3. Leo not yet
    0 of 2 approvals
    Pot rules seat (1)can't vote on this kind
    How “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

    1. 5Pot Hub → Lisbon Tripone receipt, 2 actionsTransfer 0.01452 NEARadd_proposal20 Tgasreturned 4
    2. 6Callback from Lisbon Trip to Pot Hubcallbackon_close_added(Lisbon Trip)10 Tgasreturned ok
    Show 6 gas refunds
    1. 7Gas refund to the relayer · 0.00576 NEAR
    2. 8Gas refund to the relayer · 0.02077 NEAR
    3. 9Gas refund to the relayer · 0.01061 NEAR
    4. 10Gas refund to the relayer · 0.02863 NEAR
    5. 11Gas refund to the relayer · 0.08374 NEAR
    6. 12Gas refund to the relayer · 0.00045 NEAR
  5. 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 values
    device key · ed255…qWnt

    Tell 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=

    Proposal #4 in Lisbon Tripa Sputnik DAO

    In progress

    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. Maya approved
    2. Sam not yetneeds 2
    3. Leo not yet
    1 of 2 approvals
    Pot rules seat (1)can't vote on this kind
    How “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

    1. ·Phone → RelayerThe signed request goes to our relayer (POST /api/relay), off-chain
    2. 1Relayer → MayaDelegate: runs Maya's signed request
    3. 2Maya → Lisbon Tripact_proposal(#4, VoteApprove)300 Tgas
    Show 2 gas refunds
    1. 3Gas refund to the relayer · 0.30064 NEAR
    2. 4Gas refund to the relayer · 0.00047 NEAR
  6. Step 6 of 10In the app

    Now you're Sam. He sees exactly the three refunds he'd approve.

  7. Step 7 of 10In the app

    Sam's approval is the second of two.

  8. Step 8 of 10

    The Pot sends all three refunds in a single step. If one failed, none would happen.

    What Sam signed

    Simulated values
    device key · ed255…ddzX

    Tell 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=

    Proposal #4 in Lisbon Tripa Sputnik DAO

    Approved

    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. Maya approved
    2. Sam approvedneeds 2
    3. Leo not yet
    2 of 2 approvals
    Pot rules seat (1)can't vote on this kind
    How “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

    1. ·Phone → RelayerThe signed request goes to our relayer (POST /api/relay), off-chain
    2. 1Relayer → SamDelegate: runs Sam's signed request
    3. 2Sam → Lisbon Tripact_proposal(#4, VoteApprove)300 Tgas
    4. 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
    5. 4Callback from USDC (test) to Lisbon Tripcallbackon_proposal_callback(#4)≈ 279 Tgas
    6. 5Lisbon Trip → Pot HubTransfer 0
    Show 5 gas refunds
    1. 6Gas refund to the relayer · 0.01693 NEAR
    2. 7Gas refund to the relayer · 0.00020 NEAR
    3. 8Gas refund to the relayer · 0.27924 NEAR
    4. 9Gas refund to the relayer · 0.00334 NEAR
    5. 10Gas refund to the relayer · 0.00048 NEAR
  9. Step 9 of 10

    The relayer tells the Hub, and the Hub marks Lisbon Trip closed.

    What our relayer sent

    Simulated values

    The 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
    FieldBeforeAfter
    The Pot's accounta Sputnik DAO, no access keys, 0.05 NEAR floata Sputnik DAO, no access keys, 0.05 NEAR float
    Members (DAO role member)Maya, Sam, LeoMaya, Sam, Leo
    Group approvals needed2 of 32 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)OpenClosed
    Hub ledger: put inMaya $150.00 · Sam $150.00 · Leo $150.00Maya $150.00 · Sam $150.00 · Leo $150.00
    Hub ledger: spent without askingMaya $84.00 · Sam $0.00 · Leo $0.00Maya $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

    1. 1Relayer → Pot Hubsync(Lisbon Trip, #4)50 Tgas
    2. 2Pot Hub → Lisbon Tripget_proposal(#4)10 Tgasreturned #4 Approved
    3. 3Callback from Lisbon Trip to Pot Hubcallbackon_sync(Lisbon Trip, #4)10 Tgasreturned proposal_id, subject…
    Show 3 gas refunds
    1. 4Gas refund to the relayer · 0.01071 NEAR
    2. 5Gas refund to the relayer · 0.01062 NEAR
    3. 6Gas refund to the relayer · 0.02885 NEAR
  10. Step 10 of 10In the app

    Everyone got their share back: $22.00 each.

    Balances

    Dollars
    AccountBeforeAfterChange
    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.00no 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

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

Every chapter, counted from the simulation
ChapterSignatures by peopleTransactionsReceiptsWho decidedNEAR paid by people
1 · Start a Pot116No decision needed0
2 · Join218Pot rules (the Hub's seat)0
3 · Add money116No decision needed0
4 · Spend1114Pot rules (the Hub's seat)0
5 · Ask the group3418The group, by majority0
6 · Close3416The group, by majority0
Total1112680

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 now

Every 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.