Wallets & sessions
Your wallet owns your trading account. Two optional signers let you trade without a wallet popup on every order: 1-click trading (a key held in your browser) and trading passkeys. Both can only trade, and Flint pays the network fee for the trades they sign.
| Signer | Can do | Expires | Who pays fees | Set up by |
|---|---|---|---|---|
| Connected wallet (owner) | Everything | Never | You | Connecting |
| 1-click trading | Place orders and close positions | 4h, 12h, or 7d | Flint | Owner wallet, once |
| Trading passkey | Place orders and close positions | 90 days | Flint | Owner wallet, once |
Pick which one signs your trades under Configuration → Trading → Sign trades with: Connected wallet, 1-click trading, or Passkey {address}.
Wallet
Connect any Wallet Standard wallet from the header (Connect → Select wallet). The wallet that creates a trading account is its owner, and only the owner can:
- deposit into a new asset;
- withdraw, lend, or borrow, unless it has granted a delegate those rights (see below);
- add or remove 1-click trading and passkeys.
Owner-signed transactions are never sponsored; you pay fees and rent in SOL. There's no email or passkey login. The app always starts from a connected wallet.
Delegates
1-click trading and passkeys are both built on on-chain delegates. A delegate is an extra key the owner authorises on one trading account, with a set of permissions and an optional expiry. The program checks both on every instruction the delegate signs.
| Scope | Allows |
|---|---|
TRADE | placePerpOrder, modifyPerpOrder, cancelAllPerpOrders, setAccountLeverageCap (and opening, releasing and updating a maker quoting slot, for makers) |
WITHDRAW | withdraw |
BORROW_LEND | lend, redeemLend, borrow, repayBorrow |
QUOTE | Maker fast-quoting instructions |
The owner bypasses scope and expiry. Flash loans and the liquidator and backstopper roles can't be delegated at all. Delegates carry no size or notional limits; scope and expiry are the whole policy.
Revoking (revokeDelegateTraderAccount, owner-signed) empties the
delegate's scope. The account stays on-chain so its replay nonce can't
be reset.
From code, an owner grants an Ed25519 delegate with
createDelegateTraderAccount (ops: a list of DelegateOp, and
expiryUnixSeconds, where 0 means never). An empty list is
rejected. AccountService.ListDelegates lists an account's delegates
with their credential kind (ED25519 or PASSKEY), scope bits,
expiry, and whether they're active. ListAccounts({ authority })
returns accounts where a key is owner or delegate.
1-click trading
A signing key generated in your browser and authorised to trade for one account.
Enable it:
- Configuration → Trading → 1-click trading.
- Pick a Session duration: 4h, 12h, or 7d.
- Press Enable 1-click trading and approve the one transaction in your wallet.
The panel then shows Ready for 1-click trades and Expires {date}. Choose it under Sign trades with, and orders and closes go out without a wallet popup. The ticket shows "1-click signing · Flint sponsors trade fees". Order confirmations are skipped when signing this way.
What it is: an Ed25519 key created as non-extractable in your
browser's storage (IndexedDB) and tied to this browser, wallet,
trading account, and network. "The local signing key cannot be
exported." On-chain it's a delegate with TRADE scope only, and the
expiry you picked is enforced by the program.
What it can't do: withdraw, deposit, lend or borrow, settle PnL, create accounts, or manage delegates. Those still go through your wallet.
Remove it: Remove delegate, confirmed by your wallet. Only the owner can enable or remove 1-click trading. A wallet that's only a delegate of an account can't set it up.
Trading passkeys
A passkey (Face ID, Touch ID, Windows Hello, a security key, or a synced passkey) registered as a trade-only delegate. Each order is approved with a biometric or PIN prompt instead of a wallet popup.
Add one:
- Configuration → Trading → Trading passkeys.
- Press Add passkey and complete your device's passkey prompt.
- Approve the registration transaction in your wallet.
"New passkeys can trade for 90 days. Deposits, withdrawals, and passkey management require your wallet." Remove access revokes a passkey, signed by your wallet. A passkey is bound to the site that created it. One registered elsewhere shows "Registered for another app or site" and can't be used here.
How a passkey order is checked on-chain:
- The program only ever gives a passkey
TRADEscope and refuses to widen it, so a passkey can never withdraw or borrow. - The device signs a challenge built from the order itself: the program, every account in the instruction (including the fee payer and each maker), and the order's arguments. Nothing about the order can change after you approve it.
- Each approval carries the delegate's next nonce and a
valid-until slot at most 150 slots ahead, so it can be used once
and only for about a minute. Reusing one fails with
PasskeyNonceMismatch: "This passkey was used for another trade. Review and retry your order." - User verification is required (
PasskeyUserNotVerifiedwithout it), and the origin and relying-party id must match the ones registered. - The signature is checked by Solana's secp256r1 precompile, which must be the instruction immediately before the order.
Passkey errors are codes 30–45; see Errors.
Passkey orders from code
@superis-labs/flint-margin-user-client builds both instructions:
import {
buildRegisterPasskeyDelegateInstruction,
passkeyRegistrationFromWebAuthn,
preparePasskeyPerpOrder,
} from "@superis-labs/flint-margin-user-client";
// Once, owner-signed: register the credential as a TRADE delegate.
const registerIx = buildRegisterPasskeyDelegateInstruction({
owner, traderId, registration, programAddress,
expiryUnixSeconds, // absolute unix seconds; 0n never expires
});
// Per order: get the challenge, run navigator.credentials.get with it,
// then complete into [secp256r1 verify, placePerpOrder].
const prepared = preparePasskeyPerpOrder({ /* market accounts, order, nonce, validUntilSlot, … */ });
const [verifyIx, orderIx] = prepared.complete({ authenticatorData, signature, clientDataJson });preparePasskeyPerpOrder builds immediate-or-cancel orders only and
accepts at most two maker accounts. The two instructions must stay
adjacent and top-level in the transaction. On-chain, a passkey can
also authorise modifyPerpOrder, cancelAllPerpOrders, and
setAccountLeverageCap, but the SDK has no helper for those yet.
Transaction sponsorship
Trades signed by 1-click trading or a passkey are sponsored: Flint's sponsor key pays the network fee and any rent the order needs. Your wallet needs no SOL for them. Everything signed by your wallet (account creation, deposits, withdrawals, settlement, adding or removing delegates) is wallet-paid.
If sponsorship is unavailable, the app doesn't fall back to wallet payment; it shows "Transaction sponsorship is unavailable on the selected endpoint." Switch Sign trades with to Connected wallet to keep trading and pay your own fees.
Sponsorship rules
SponsorshipService co-signs only as fee payer. The server
checks every transaction before signing:
| Rule | Detail |
|---|---|
| Programs | Only the margin program, Compute Budget, and secp256r1. Each secp256r1 instruction must sit right before a margin instruction. |
| Instructions | placePerpOrder or revokePasskeyDelegate, on an existing trading account. The transaction may also include syncLinearPerpCache. Modifying or cancelling orders and setting a leverage cap are not sponsored. |
| Signatures | At most 3, counting the sponsor, any delegate wallet, and passkey verifications. All but the sponsor's must already be present and valid. |
| Priority fee | At most 100,000 lamports. |
| Simulation | Must succeed before the sponsor signs. |
| Rate | Currently one sponsored transaction per 10 seconds per trading-account owner (RESOURCE_EXHAUSTED past that). |
Using it from code
GetSponsorshipPubkeyreturns the sponsor's address (UNAVAILABLEif the endpoint has no sponsor).- Build the transaction with that address as fee payer (account 0), and leave its signature slot empty.
- Sign with your delegate (or attach the passkey instructions).
- Send the serialized transaction to
SponsorTransaction. It returns the signature after confirmation.
Policy failures return INVALID_ARGUMENT with the reason.
Security summary
| Threat | Owner wallet | 1-click key | Passkey |
|---|---|---|---|
| Withdraw funds | Yes | No | No |
| Trade | Yes | Until expiry or removal | Until expiry or removal; each order needs user verification |
| Grant itself more rights | — | No | No (program refuses) |
| Replay a signed order | Normal Solana rules | Normal Solana rules | No: one nonce, ~150-slot window |
| Stolen with the device | Depends on the wallet | Usable from that browser profile | Needs the device's biometric or PIN |
Account data isn't private: anyone can read any trading account's balances, positions, and fills by trader id.
