How it works
Arvo lets an agent trade from a vault you own without ever holding your funds. Every trade is executed and independently risk-assessed in parallel, and the contract only insures the result if both halves agree on terms you set up front. This is the whole path, from landing on the site to getting your balance back.
Connect your wallet
Create a vault
createVault on the vault factory for the chain you picked, and you become its owner. You can deploy as many as you like — the factory keeps the list under getUserVaults, which is what the dashboard reads.Deposit tokens
depositETH or depositToken. Only the owner can deposit or withdraw, and withdrawals are limited to the vault's available balance — the total minus anything locked behind an open position. The dashboard shows both numbers per token.Connect an agent to the vault
The agent posts a trade intent
post-trade-intent with the pair, the amount, a deadline, and the terms it will accept: a maximum premium, a minimum coverage percentage, and a minimum coverage duration. The agent's wallet signs the payload as EIP-712 typed data, so the request cannot be forged on its behalf. Arvo records the intent and submits it on-chain with submitTradeIntent, where it sits in PENDING. If the chain submission fails the intent is rolled back and nothing is queued.The intent fans out to two engines
trade_execution_queue list for the trade engine and an arvo:trade-intents stream for the risk engine. The two run independently and neither waits on the other. That is the point: the party executing the trade is not the party pricing its risk.The trade engine executes the swap
minAmountOut floor in the intent. When it settles, the engine posts a signed trade confirmation back to Arvo — the transaction hash, the tokens, and the exact amounts in and out — which Arvo forwards on-chain via submitTradeConfirmation.The risk engine prepares an assessment
submitRiskAssessment. Each assessment carries an expiry, so a stale quote cannot be redeemed later.The contract decides whether to insure
evaluateTradeIntent automatically. The contract then checks the assessment against the terms the agent committed to in the intent, and rejects the trade if any of these hold:- Risk assessment has expired
- the assessment's expiresAt is in the past
- Coverage duration is zero
- the assessment offered no coverage window
- Premium exceeds maximum
- the quoted premium is above the maxPremium in the intent
- Coverage is below minimum
- the offered coverage is under the intent's minCoverage
- Coverage duration is below minimum
- the window is shorter than minCoverageDuration
- Risk score is too high
- the score is above 70
A rejected intent is marked REJECTED and stops there — the swap may already have happened, but it carries no coverage. Nothing is locked and no premium is taken.
Insurance is issued and a position opens
lockAsset, and the premium is pulled with deductPremium. The intent flips to APPROVED.Locked balance, and how to get it back
To free them, invalidate the insurance. Only you or the protocol owner can call invalidateInsurance. It marks the insurance invalid, deactivates the position, and calls unlockAsset on your vault, returning the locked amount to your available balance. From that point the trade is uninsured and the funds are withdrawable again.
The shape of the whole thing: you hold the keys, the agent holds a scoped binding, the two engines never trust each other, and the contract is the only thing that can move funds between them.