How a Vectr wallet guards its key
Signing in with X gives you a custodial wallet whose key Vectr's backend holds, so that you, your agent and your scheduled orders can trade without a browser extension. This guide covers how that key is stored, the only code paths that decrypt it, and every rule a transaction must pass first. It is read from the backend source and the live security block of GET /config.
Updated
How the key is stored
- Created
- at first X sign-inA fresh random private key, encrypted before the account row is written
- Cipher
- AES-256-GCMA random 12-byte IV for every encryption and a 16-byte authentication tag: a ciphertext that was altered fails to decrypt
- Key that encrypts it
- server master keyA 256-bit key held in the server environment, not in the database
- In the database
- ciphertext, IV, tagNo plaintext key, and nothing that decrypts it on its own
| KMS envelope | Master key | |
|---|---|---|
| Wallet key encrypted with | A 256-bit data key generated by AWS KMS for that wallet | One 256-bit key from the server environment |
| Stored beside the ciphertext | The data key, encrypted by KMS | Nothing |
| To decrypt | A KMS Decrypt call for the data key, then AES-256-GCM | AES-256-GCM with the master key |
| A database dump alone yields | Ciphertext only | Ciphertext only |
| Key-encryption key | Stays inside AWS KMS; decrypting a wallet takes a KMS Decrypt call for its data key | Lives in the server environment; whoever holds it can decrypt every wallet encrypted under it |
| On this deployment | Not enabled | In use here |
- Wallet key encrypted with
- KMS envelope
- A 256-bit data key generated by AWS KMS for that wallet
- Master key
- One 256-bit key from the server environment
- Stored beside the ciphertext
- KMS envelope
- The data key, encrypted by KMS
- Master key
- Nothing
- To decrypt
- KMS envelope
- A KMS Decrypt call for the data key, then AES-256-GCM
- Master key
- AES-256-GCM with the master key
- A database dump alone yields
- KMS envelope
- Ciphertext only
- Master key
- Ciphertext only
- Key-encryption key
- KMS envelope
- Stays inside AWS KMS; decrypting a wallet takes a KMS Decrypt call for its data key
- Master key
- Lives in the server environment; whoever holds it can decrypt every wallet encrypted under it
- On this deployment
- KMS envelope
- Not enabled
- Master key
- In use here
The mode comes from platform.security.custody.custodialWallet.kmsEnvelope, which the backend sets from its own environment at boot.
The four places a key is decrypted
Nothing else in the backend calls the decryption function. In every case the plaintext key lives in memory for that one call.
POST /wallet/sign-and-submitAfter the calldata whitelist, the API key's own limits and every wallet rule have passed.
limit, stop, DCA, TWAPWhen an order you created comes due, after the same wallet rules. The calldata is built by the backend itself.
FeeCollector.depositPlatformFee()Only if you turned auto top-up on, after the wallet rules, within the daily cap you set.
POST /wallet/export-keyOnly after a fresh X login, and then only sealed to a key your browser generated. See the export section.
What can ask the wallet to sign
With a Vectr wallet, after you confirm, Vectr's wallet service signs the validated transaction from your encrypted custodial wallet, subject to your spending limits and allowlists. That confirmation is per transaction in the Terminal; the other paths below either never sign or were authorised once, up front.
| Path | How it signs | A click per transaction |
|---|---|---|
| Terminal: swap, launch, transfer | The agent turns your request into a typed intent and builds an unsigned transaction. You review and confirm it before your wallet signs and broadcasts it. | Yes |
| Agent job over API, MCP or CLI | Through the API, MCP server or CLI, an agent job returns the unsigned transaction and stops there: nothing is signed or broadcast until you submit it yourself. | Nothing signs |
| Limit, stop, DCA and TWAP orders | Limit, stop, DCA and TWAP orders are authorised once, when you create them in the order form or give the agent their parameters. After that, eligible fills execute automatically from your wallet under the spending, recipient and contract controls you set. | No: authorised once |
| Auto top-up of AI credits | Off unless you turn it on. When the credit balance cannot cover the next agent turn, or drops under your threshold after one, the backend buys credits with ETH from the wallet, within the daily cap you set. | No: authorised once |
| Read-write API key | A read-write API key is the automation path: it can submit transactions from your Vectr wallet without a person in the loop, inside the key's limits. A read-only key can never sign, submit or create orders. | No |
- Terminal: swap, launch, transfer
- How it signs
- The agent turns your request into a typed intent and builds an unsigned transaction. You review and confirm it before your wallet signs and broadcasts it.
- A click per transaction
- Yes
- Agent job over API, MCP or CLI
- How it signs
- Through the API, MCP server or CLI, an agent job returns the unsigned transaction and stops there: nothing is signed or broadcast until you submit it yourself.
- A click per transaction
- Nothing signs
- Limit, stop, DCA and TWAP orders
- How it signs
- Limit, stop, DCA and TWAP orders are authorised once, when you create them in the order form or give the agent their parameters. After that, eligible fills execute automatically from your wallet under the spending, recipient and contract controls you set.
- A click per transaction
- No: authorised once
- Auto top-up of AI credits
- How it signs
- Off unless you turn it on. When the credit balance cannot cover the next agent turn, or drops under your threshold after one, the backend buys credits with ETH from the wallet, within the daily cap you set.
- A click per transaction
- No: authorised once
- Read-write API key
- How it signs
- A read-write API key is the automation path: it can submit transactions from your Vectr wallet without a person in the loop, inside the key's limits. A read-only key can never sign, submit or create orders.
- A click per transaction
- No
What a signing request passes, in order
POST /wallet/sign-and-submit, the route the Terminal and read-write keys use. Scheduled fills and auto top-ups never reach this route: the backend builds their calldata itself, and they start at the wallet rules.
- 1Route guardthrottle and auth30 requests a minute per client. The caller needs a session or an active API key, used from an address on the key's IP list if it has one.
- 2Key scopewallet-mgr.controllerA request made with a read-only API key is refused with 403 here.
- 3Signer rate limitper account20 signing calls a minute for the account, however many keys or sessions they come from, so a runaway script cannot fire faster than you can react.
- 4Calldata whitelistvalidateCalldataThe transaction must declare its type, its 4-byte selector must be on that type's list, and trade, launch and credit-purchase targets must be known contracts. Malformed calldata fails here, before the key is touched.
- 5The key's recipient listper API keyIf the API key was created with recipient addresses, the transaction's to address must be one of them.
- 6Wallet rulesSecurityService.enforcePause, contract-call guard, recipient allowlist and cooldown, pricing, per-transaction limit, 24-hour limit, in that order. The first failure ends the request with its reason.
- 7Decrypt and signCryptoServiceOnly now is the key decrypted, for this call alone. If the transaction needs an ERC-20 approval, that is sent and confirmed first.
- 8Recordaudit logThe signature is logged with its type, target, USD value and hash, and the USD value is added to the wallet's 24-hour total.
What each transaction type may call
Checked against the calldata before the key is decrypted. A selector that is not on its type's list is refused, whatever produced it.
| Type | Allowed functions | Allowed target |
|---|---|---|
| buy | buy(uint256,uint256), exactInputSingle | A curve Vectr launched, or Uniswap's SwapRouter02 for a graduated Vectr token against its own pairing asset, at the graduation fee tier, paid out to this wallet |
| sell | sell(uint256,uint256), exactInputSingle, multicall | The same; a sell for ETH may unwrap WETH, still to this wallet |
| launch | launchToken, launchTokenAndBuy | The factory only, and not on a pairing asset that is about to stop minting |
| claim | claimCreatorFees(), claimCreatorFeesToken(address) | Selector checked; the target is not pinned |
| buy_credits | depositPlatformFee() | The FeeCollector only |
| transfer | transfer(address,uint256) | Selector checked; the target is not pinned |
| subscribe | No calldata: a plain value transfer | Not pinned |
- buy
- Allowed functions
buy(uint256,uint256),exactInputSingle- Allowed target
- A curve Vectr launched, or Uniswap's SwapRouter02 for a graduated Vectr token against its own pairing asset, at the graduation fee tier, paid out to this wallet
- sell
- Allowed functions
sell(uint256,uint256),exactInputSingle,multicall- Allowed target
- The same; a sell for ETH may unwrap WETH, still to this wallet
- launch
- Allowed functions
launchToken,launchTokenAndBuy- Allowed target
- The factory only, and not on a pairing asset that is about to stop minting
- claim
- Allowed functions
claimCreatorFees(),claimCreatorFeesToken(address)- Allowed target
- Selector checked; the target is not pinned
- buy_credits
- Allowed functions
depositPlatformFee()- Allowed target
- The FeeCollector only
- transfer
- Allowed functions
transfer(address,uint256)- Allowed target
- Selector checked; the target is not pinned
- subscribe
- Allowed functions
- No calldata: a plain value transfer
- Allowed target
- Not pinned
An approval that travels with a trade may only be an ERC-20 approve, granted to the trade contract itself (or the factory, for a launch), over the token being sold or the pairing asset being spent. Where a target is not pinned, your allowlists and USD limits are what bound the transaction, which is one reason to set them.
The controls you set
In the Security tab of your profile. Only a signed-in session can read or change them; an API key cannot. The values on the right are what a wallet that has never saved its settings is checked against.
- Pause switch
- offStops every signature: Terminal, API keys, scheduled fills, auto top-ups. Keys stay valid
- Per-transaction limit
- $500USD, from 1 to 1,000,000, or none. A wallet that has never saved its settings is held to $500
- 24-hour limit
- noneUSD over a sliding 24 hours, not a calendar day: no midnight reset to wait for
- No price, no signature
- fail closedWith either limit on, a transaction whose value cannot be priced is refused
- Recipient allowlist
- emptyOnce it has entries, every transaction must be sent to one of them, or to the wallet itself
- Recipient cooldown
- 24 h0 to 168 hours. A new entry cannot receive until it has aged; removing and re-adding restarts it
- Contract-call guard
- offWhen on, refuses buys, sells, launches, fee claims and credit purchases; transfers still pass the other rules
- Anomaly flag
- log onlyA transfer of $50 or more to a new recipient, above 3x the wallet's recent average, is logged for review. It does not block
USD value is what the transaction spends: ETH sent as value, priced at the ETH rate, or on a buy or launch paid in an ERC-20, the approved amount at that asset's USD price. For a Stock Token that price is Robinhood's quote times the multiplier, as Stock Token pairing explains.
The recipient allowlist checks the address a transaction is sent to, which for a trade is the curve or router it calls, so a wallet that trades with the list switched on needs those contracts on it too. Separately, the agent will only build a transfer to an address already on the list, whether or not the list is otherwise in use. The cooldown exists for the case where someone else has your session: an address they add cannot receive until you have had time to see it and remove it.
Taking the key out
The backend's export route is built behind these gates; GET /config does not list export as available yet.
- Status on this deployment
- not listed as availableGET /config reports exportable: false
- Gate
- fresh X loginA new X sign-in mints a single-use grant, valid for 5 minutes, bound to the session, IP address and browser that earned it
- Attempts
- 3 per hourEvery attempt from a signed-in session, allowed or refused, is written to the audit log
- Delivery
- sealed to your browserEncrypted to a one-time key your tab generates (ECDH P-256, HKDF-SHA256, AES-256-GCM), so the plaintext never crosses the wire
An exported key is outside every rule on this page. Whoever holds a copy can sign anything, and the pause switch, limits and allowlists no longer apply to them.
What custody still means
A custodial wallet is as safe as the servers that hold its ciphertext and the key that decrypts it; the controls above narrow what a stolen session, a leaked API key or a misled agent can do, not what a compromise of those servers could. If you want no one else to hold a key, connect your own wallet: Vectr builds the transaction and your wallet signs it, and none of the custodial paths above exist for you. API keys, which are the widest door into a custodial wallet, have their own guide: read-only and read-write API keys. The signing pipeline from the model's side is in how an AI agent trades, the summary of every control on the security page, and the risks in the risk disclosure.
Wallet security questions
Does Vectr store my wallet's private key?
It stores it encrypted. The key is generated when you first sign in with X and encrypted with AES-256-GCM before it is written to the database, under a 256-bit key-encryption key held in the server environment, not in the database. It is decrypted only inside four code paths, each after its own checks.
Can an AI agent drain my Vectr wallet?
The model never sees a key or writes raw calldata; it produces typed intents, and the backend builds the transaction. Anything that reaches the signer still has to pass the calldata whitelist and your wallet rules. The exposure is what you have authorised to run without a click: scheduled orders, auto top-up if it is on, and any read-write API key you gave out, each capped by your USD limits and allowlists.
What does the pause switch stop?
Every signature for the wallet: Terminal confirmations, read-write API keys, scheduled order fills and auto top-ups. It does not revoke anything, so turning it off resumes where you left off.
Is the 24-hour limit a calendar day?
No. It is a sliding 24-hour window, so there is no midnight reset for anyone to wait for. The running total is the USD value of what the signing service has signed for the wallet in the last 24 hours.
What happens if the price feed is down?
If a per-transaction or 24-hour limit is on and the transaction's value cannot be priced, it is refused. With no USD limit set, pricing is not needed and the other rules still apply.
Can an API key change my security settings?
No. Reading and changing the pause switch, limits, allowlist and cooldown takes a signed-in session. So do creating and revoking keys and exporting the wallet key. An API key can only use the wallet inside the rules the session set.