Sign in

Action as data

Suggest an edit

Some actions need a signer who will be offline when the time comes, or a condition that is not true yet. A limit order waits for a price, and a multisig transaction waits for the other signers. On Solana a signed transaction cannot wait, because its signature dies within a minute. Something else has to hold the user's intent until its time comes.

A signature lasts a minute

Every Solana transaction includes a recent blockhash, and the network accepts the transaction only while that blockhash is still recent, which is about a minute. An older transaction is rejected, so its signature is useless after that.

Because of this, the usual EVM flow is not possible on Solana. On EVM a user can sign an order once and let a server submit it when the price is right. On Solana a signed order cannot wait for a taker, and approvals for a multisig cannot be collected over a week.

Solana has durable nonces for longer-lived transactions. Such a transaction uses a nonce account instead of a blockhash and stays valid until the nonce is used. But it is still a single fixed transaction, with the accounts and the data set at signing, and the signature can be used once.

The naive solution is a server that keeps the user's request and signs a transaction with its own key when the time comes. This is a bad solution, because the server now controls the user's money: it can crash, it can censor the user, and it can be taken over, and in every one of these cases the user loses control of their own funds.

Store the action, not the signature

Instead of signing a transaction, the user stores the action itself in an account. The account holds four things: the target program, the list of accounts, the instruction data, and the condition under which the action may run.

This account is a PDA of the protocol's program. At the moment of storing, the user hands the PDA what the action needs. For example, if the action will spend tokens, the user moves those tokens into an account the PDA owns.

When the condition is met, anyone sends an execute transaction. The program checks the condition, reads the stored instruction, and calls the target program with invoke_signed, the call that lets a program sign for its own PDA. This means the PDA signs in place of the user. The executor only brings the accounts and pays the fee. The user signed once, when the action was stored, and does not have to be online again.

With action as data, the action is public from the moment it is stored, so anyone can read it, and anyone can execute it once the condition is met.

The action is stored in an accountIn the middle, a PDA account card holds a pending action: program swap, the accounts, the data sell 10 SOL, and the condition price at or above 150. On the left, the user at 10:00 stores the action and locks the tokens, signing once. On the right, a keeper at 14:00 sends execute, and the PDA signs the call to the swap program through invoke signed. A crossed-out note shows that a signed transaction would have expired after about a minute. The action is stored in an account PDA: pending action program: swap accounts: user tokens, pool data: sell 10 SOL condition: price >= 150 user 10:00 store locks the tokens signs once keeper, anyone 14:00, price is 150 execute swap program sells 10 SOL PDA signs invoke_signed a signed transaction instead would have expired after a minute The user signs once, at 10:00. At 14:00 the PDA signs for them.

Squads: a proposal is an account with instructions

Squads is a multisig, and its members are rarely online at the same time.

A multisig transaction in Squads is an account that stores the full list of instructions it will execute. Members approve it one by one, over hours or days, and each approval is a small transaction of its own. When enough members have approved, a member sends the execute transaction. The program runs the stored instructions with the vault PDA as the signer, through invoke_signed. The vault owns the funds, so the stored action can move them, and no member's key is needed at execution time.

Realms: anyone executes a passed proposal

Realms is the governance program behind most Solana DAOs, and after a vote that took days there is nobody in particular to execute the result.

A proposal stores its instructions in accounts next to it, and voters vote over days. When the vote passes, there is a hold-up time, a delay during which the proposal can still be stopped. After it the proposal enters the executing state, and from there the stored instructions can be run by anyone, at any time. The governance PDA is the authority over the treasury, so it signs for the instructions. The person who sends the transaction only pays the fee.

Jupiter: an order is an account with a price

A Jupiter limit order is an account with a price and a size. When the order is placed, the tokens being sold are moved into an account the program controls. A DCA order is the same idea with a schedule instead of a price. When the price or the time comes, a keeper sends the transaction that executes the stored swap. The user has not signed anything since the order was placed.

What the stored action costs

Every pending action is an account, so someone pays rent for it, and the funds it will spend are locked until it runs. If the action never runs, the funds stay locked. So every stored action needs a way out: the user can cancel it, or it expires after a deadline and the funds return.

If it has to wait, store it

A signature is good for a minute, so it cannot wait for anything. Every time a design says that the user signs now and something happens later, the later part cannot be a signed transaction. It has to be an account: the action written down on-chain, waiting for its condition, with a PDA that signs for the user when the condition is met.