Concepts
Rotation and veto
Changing the wallet that gets paid, and what stops a hijacked account from redirecting the money.
Why rotation exists
Wallets get lost, retired or replaced. The owner needs a way to move to a new one. But "prove you own the account" is exactly what an attacker who has taken over the account can also do. So a change of wallet is deliberately slow and can be stopped.
The steps
- The owner picks a new wallet and asks for the code for that wallet.
- They paste it on the platform. Attesters sign, as for a first claim. The signatures carry the current epoch.
rotateaccepts them and schedules the change two days later (configurable per vault).- During those days fees still go to the old wallet.
- After the delay, the new wallet takes over and the epoch goes up by one.
The veto
During the delay the currently bound wallet can cancel the change by signing:
upstream:v1:cancel|<mint>|<new wallet>|<effectiveAt>
Anyone can relay that signature; only a signature from the current wallet is accepted. If the real owner still holds their old wallet and the account was hijacked, they cancel, and the attacker waited for nothing.
A veto also raises the epoch, so the attestations that scheduled the cancelled rotation can never be replayed to schedule it again (tested). On chain, the veto is the current wallet signing the cancel transaction.
Why the epoch
Every attestation includes the vault's epoch (how many times a wallet has been bound). Without it, an old attestation for wallet A could be replayed after a rotation to flip the vault back to A. With it, an attestation is only valid for the binding it was made for. The test suite replays an old bundle after a rotation and checks it is refused.
What an attacker needs
| To redirect fees an attacker must | and is stopped by |
|---|---|
| Control the account (bio, description or repo) | the platform's own security |
| Get two of three attesters to read the code | they only sign what the page shows |
| Wait two days with the old wallet watching | the veto, if the old wallet is still held |
If the owner lost the old wallet and the account is compromised, nobody can veto. That is the cost of a design with no admin key. A longer delay is the only lever.
Only once per rotation
One rotation can wait at a time. A second request while one is pending is refused (rotation_pending), and rotating to the wallet that is already bound is refused (same_wallet).