Start
Transactions
Every action in the app runs the same steps, and its button tells you which step it is on. Nothing is sent until the program would accept it.
- A signed price stays valid
- 10 s
- Checked before your wallet opens
- every action
- Rounding
- down, to the token's last unit
The steps
- Preparing. The app builds the transaction. On an SPYx or QQQx path it also fetches a price signed by Pyth. Then it runs the transaction against the program: if the program would refuse it, the reason is written under the button in words and your wallet never opens. Editing the amount clears it.
- Approve in your wallet. Your wallet is open. On an xStock path, ten cells on the button go dark one a second while the signed price stays valid.
- Sending. The wallet has signed. A note at the bottom left reads Sent, with an explorer link.
- On confirmation the note says what happened, such as Minted 9,990 USDq, and every balance the transaction touched is read again at once.
If the program refuses the transaction after it is sent, the note reads Didn't go through, with the reason in a sentence, and the form keeps your values so you can try again. If you decline in your wallet, the note reads Not signed and nothing is sent.
The signed price
The xStock paths value your tokens at the ETF's price from Pyth Pro, and the program accepts a price only while it is at most 10 seconds old. So the price travels inside your transaction, signed by Pyth, and the program checks the signature and the age before it pays anything.
That is what the ten cells count down. If you approve after the last one goes dark, the app does not send the transaction. The button reads Price refreshed · approve again, the app fetches a new signed price, and your wallet opens once more with the new figures.
The USDC paths, staking and claims use no price, so they have no countdown.
Messages after sending
Between pressing and confirming, the state can move. If it does, the transaction is refused and the note says why:
| The note says | Error |
|---|---|
| This hour's capacity on this path was used in the meantime. | CapacityExceeded |
| That would take USDq past its supply ceiling. | SupplyCapExceeded |
| That would take this collateral past its spot cap. | SpotCapExceeded |
| The buffer holds less than that right now. | BufferInsufficient |
| The vault holds less of this collateral than that. | VaultInsufficient |
| Your balance is lower than that. | InsufficientFunds |
| The session closed before this was sent. | CollateralNotLive |
| The hedge market paused before this was sent. | HedgeNotLive |
| This collateral is inside a corporate-action window. | CorporateActionWindow |
| The signed price expired before the wallet approved. | StalePrice |
| The cooldown is still running. | CooldownActive |
| There is nothing to claim. | NothingToClaim |
| There is no stake to unstake. | NothingStaked |
| Your position is cooling down. | PositionCooling |
| The withdraw window isn't open. | WindowNotOpen |
| Add SOL to this wallet for the network fee. | NeedsSol |
| Enter an amount. | ZeroAmount |
| The protocol state is still loading. | NotReady |
Anything else reads The transaction didn't go through, with the explorer link so you can see the program's log.
What goes into a transaction
Each action is one versioned transaction, built in this order:
- a compute budget;
- the creation of any token account you don't have yet, which does nothing if it already exists;
- on an xStock path, the signature check that carries Pyth's signed price;
- the program instruction.
It uses the program's address lookup table, which keeps it within one transaction even with every vault and price account in it.
Rounding
The program works in each token's smallest unit and rounds every amount it pays you down to the last whole unit. The app repeats the program's arithmetic exactly. On the USDC paths and when staking, You receive is the amount that arrives. On an xStock path it is quoted on the latest relay price, and the transaction uses the signed price fetched when you press.