routr

Security

What routr can and cannot do.

The trust model in plain words, and how to check it yourself.

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' feesyes, up to 30% of the pool fee, never above
Change the protocol's fee recipientyes, for new pools
Change an existing pool's fee or splitno: 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 balancesno: no owner or guardian method in the current code can withdraw reserves or reassign balances; they move only through swaps and payouts
Pause tradingyes, at once (the guardian can too); unpausing waits the delay
Block a trader or a tokenno: no owner or guardian method in the current code does this
Change the codeyes, 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 platformno: 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.

MethodWhy it existsWhat it cannot do
pauseStop 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_upgradeFix 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, unpauseReopen 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_guardianReplace 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_ownerReplace 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_protocolSet 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.