Trust model
routr holds two kinds of value: each pool's reserves, and what it owes to accounts (swap output in flight, fee shares not yet pushed). In the current code, reserves move only through swaps against the pool, and amounts owed move only to the account they are owed to. There is no method that transfers a pool's reserves elsewhere, no trader allowlist and no emergency withdrawal. Output is recorded as pending before its transfer leaves; if the transfer fails and its callback completes, the amount is recorded as owed for a later push.
The contract is upgradeable, and we say so plainly rather than promising code that can never change. Three rules make that control visible: a guardian can freeze the venue at once and can do nothing else; unfreezing and every code change must be announced on chain (an upgrade_staged event with the new code's hash) and wait a fixed delay before they can land; and the account holds no access keys on mainnet, so the announced path is the only way the code can change. The delay is set when the contract is deployed and has no setter: shortening it is itself an upgrade that waits the old delay.
What this does and does not protect against. A stolen guardian key can only freeze trading. One stolen key of the owner multisig can do nothing alone. Whoever controls the owner quorum can, after the delay, deploy code that does anything, including moving balances. The delay guarantees notice, not an exit: the owner can pause at once, and a pause also stops payouts, so a hostile quorum could freeze the venue for the whole delay and then deploy. What the delay gives everyone is time to see the staged hash, check it against the published source, and raise the alarm. Our rule: the wasm and its source commit are published the moment an upgrade is staged; treat a stage whose bytes are not public as hostile.
The trading path does not scan all pools, and each trade updates only its own pool's reserves. Pools share the contract account, and pools of one platform share its storage credit.
What the owner can and cannot do
| Owner | |
|---|---|
| Change the protocol's share of new pools' fees | yes, up to 30% of the pool fee, never above |
| Change the protocol's fee recipient | yes, for new pools |
| Change an existing pool's fee or split | no: the fee rate and percentage terms are frozen at creation (the creator can hand its share to another account, and the platform's approved-builder list applies to all its pools) |
| Move pool reserves or user balances | no: no owner or guardian method in the current code can withdraw reserves or reassign balances; they move only through swaps and payouts |
| Pause trading | yes, at once (the guardian can too); unpausing waits the delay |
| Block a trader or a token | no: no owner or guardian method in the current code does this |
| Change the code | yes, after announcing the new code's hash on chain and waiting the delay; our policy is to publish the wasm and its source commit at that moment |
| Register, change or delete someone's platform | no: only the platform's own owner changes it, and a platform cannot be deleted |
Why these powers exist
Registering a platform, creating pools where the platform allows it, trading, quoting and pushing payouts are open to anyone. Some actions are limited to the account they concern: a platform's owner changes the platform, the named seeder seeds a pool, a platform owner or the seeder can remove a pool that was never seeded, and a pool's creator hands on its share. Beyond those, a few methods belong only to the venue's owner multisig or its guardian. The table says why each one exists. We use them to respond to an exploit or a lost key, to ship announced code releases, and to set the protocol's fee for new pools, and for nothing else. That is our policy, not something the contract can check; what the contract enforces is that each is limited as the last column says.
| Method | Why it exists | What it cannot do |
|---|---|---|
pause | Stop an exploit in progress. If a bug in the swap or payout code is being used against the pools, freezing at once stops new swaps and seeds before more is taken. The guardian can do this on its own, without waiting for two of three owner signers; the owner can too. | Move funds, change terms, or single out a trader or token: it stops everyone equally, and lifting it waits the delay. |
stage_upgrade, apply_upgrade, cancel_upgrade | Fix the bug, and ship new features such as resting orders. Depending on the bug, recovery may need only existing methods, or corrected code, which reaches the contract only through an announced upgrade; some bugs could need one to give people access to their funds again. cancel_upgrade withdraws a mistaken announcement. | Land without announcing the new code's hash on chain and waiting the 2-day delay. |
stage_unpause, unpause | Reopen trading after a pause. | Reopen at once: the unpause is announced and waits the delay, so anyone can see it coming and check what changed. The contract does not itself require a fix first. |
set_guardian | Replace a lost or stolen guardian key, or remove the guardian. | Grant anything beyond pausing: a guardian's only power is to freeze. |
propose_owner, accept_owner | Replace the owner if signers are lost or compromised, or move ownership to a new multisig. Nothing moves until the proposed account accepts, and the current owner can withdraw the proposal until then. There is no delay on the handoff itself. | Widen the owner's powers: the new owner has exactly these methods. |
set_protocol | Set the protocol's share of the pool fee and the account it goes to. The only one of these methods that concerns fees rather than safety. | Exceed 30% of the pool fee, or change anything about a pool that already exists. |
Why not give up control entirely? A contract that can never change cannot be fixed either: a bug found after launch would stay live in every pool. Why no emergency withdrawal? A method that moves pool money is the first thing an attacker would steal a key for, and it would let one compromise empty every pool at once. The current code has no such method: its answer to an exploit is to freeze at once, then recover with the existing methods or with an upgrade announced in the open and delayed. The honest limit is the one stated in the trust model: an upgrade can deploy any code, including code that moves balances, so the protection is the 2-of-3 quorum and the public 2-day notice, not the absence of power.
Keys and upgrades
routr.near holds no access keys. Its owner is routr.sputnik-dao.near, a Sputnik DAO whose council of three signers must reach two approvals for any owner action; the guardian is guardian.routr.near; the delay is 2 days. The code changes only through stage_upgrade and, after the delay, apply_upgrade with bytes that hash to the staged value. To watch for changes, subscribe to the upgrade_staged event or poll get_protocol: its staged_upgrade field is null when nothing is pending. To check that the account holds no keys:
curl -s https://rpc.mainnet.near.org -H 'content-type: application/json' -d '{"jsonrpc":"2.0","id":1,"method":"query","params":{"request_type":"view_access_key_list","finality":"final","account_id":"routr.near"}}' | jq .result.keys # expected: []
Reproducible builds
The contracts are built in CI inside a pinned container (cargo-near reproducible build). Current routr build: code hash H4G6bhZXHKnhecs6gwgL3fQ4F3pt4RPHw34L2uaVhWdN, 331,716 bytes, commit 76c5992. routr.near runs exactly this build. To check it:
curl -s https://rpc.mainnet.near.org -H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"query","params":{"request_type":"view_account","finality":"final","account_id":"routr.near"}}' \
| jq -r .result.code_hash
The testnet account runs development builds and will not match a release hash.
Reviews
Each change to the contract went through internal adversarial review rounds, run with an AI code reviewer, focused on liabilities, storage griefing, pool-key squatting, arithmetic bounds and callback failure; findings were fixed and rechecked. routr has not been audited. It is live on mainnet without an external security review; one will be commissioned when revenue pays for it, and its report will be linked here. Until then we do not describe routr as audited, and you should weigh that before routing funds through it. The pause and the 2-day public delay on every code change are the safety net meanwhile.
Report a problem
Email [email protected] with the contract, the call and what you observed. Please do not post exploitable details publicly before we reply.