Concepts
The few ideas everything else builds on. Read this once and the rest of the docs click.
#Adaptor
A Move package that wraps an external protocol so a Sup vault can use it through the
SupWallet::intent flow. It moves funds only via intent — never by holding a raw
Coin — so the owner's allowance always bounds it. A minimal adaptor looks like:
module my_adaptor::adaptor {
use SupWallet::intent;
public struct MyAdaptor has drop {} // the witness (see below)
public fun swap<CoinIn, CoinOut>(
wallet: &mut SupWallet::wallet::Wallet,
pool: &mut some_dex::Pool<CoinIn, CoinOut>, // protocol object
amount_in: u64, min_out: u64, clock: &Clock, ctx: &mut TxContext,
) {
// pulls CoinIn from the vault via intent (allowance-checked),
// calls the DEX, deposits CoinOut back into the vault.
}
}
Examples on Sui today: a Cetus swap adaptor, a Scallop lending adaptor, an OS-account trading adaptor.
#Witness / service-type
Every adaptor declares a witness type: a struct with has drop and nothing else, e.g.
public struct MyAdaptor has drop {}. Its fully-qualified name —
0x<pkg>::<module>::MyAdaptor — is the service-type: the unique key for the listing and
the exact thing a wallet owner authorizes.
Because the type name is namespaced by the publisher's package id
(type_name::with_defining_ids), nobody can register your witness type and have it resolve
to their package. Service-types are globally unique and unforgeable by construction.
#Allowance — the safety model
Before an adaptor can touch a coin, the owner must grant it. There are three on-chain gates,
all enforced by SupWallet:
- Service allowlist —
wallet::grant_service_coin<Witness, Coin>(wallet)adds the (service-type, coin) pair to the vault's allowlist. This is the gate every op checks. - Per-service allowance — an optional spend cap for a delegate (e.g. an AI agent) on that service.
- Per-coin allowance — an optional spend cap on that coin across services.
Using a listed adaptor therefore only ever moves funds the owner granted, capped by these
allowances. Even a malicious listing is sandboxed to exactly what you authorized. Revoke
any time with revoke_service_coin.
#Intent
SupWallet::intent is the hot-potato engine adaptors call to pull funds out of the vault and
credit results back, all in one transaction. It has four modes (A = self-spend, B =
designated payer, C = unmetered/cap-gated, D = internal swap) — you rarely touch them directly;
the manifest's intentMode is just informational.
#Manifest
The listing's rich metadata (name, summary, category, ops, fees, call specs). It lives
off-chain on Walrus; the registry stores its manifest_uri + a sha256 content hash,
so the body is tamper-evident. Full shape: Manifest & call spec.
#Call spec
The on-chain ABI tells you a parameter's type, not its meaning: it says "param 4 is a
u64" but not "param 4 is the input amount", and "param 2 is a Pool" but not which pool.
A call spec fills that gap — the publisher declares each argument's role
(wallet | amount | minOut | object | clock | pure), so an agent can turn "swap 1 SUI to USDC"
into a concrete transaction. Ops with a call spec are agent-executable; without one
they are discover/authorize-only.
#Mining
Where do the fixed object ids (pools, configs) and scalars in a call spec come from? They are recovered by reading the package's own past on-chain transactions — those calls already passed the real objects. The tooling tallies them per argument position: an argument that's always the same id is a singleton (e.g. a global config) and auto-fills; one that varies is pool-like and comes back as candidates. No hand-typing required.
#Keyless
Every helper — HTTP API, SDK, CLI, MCP server — returns data and unsigned transactions.
Signing is always done by the user with their own wallet (in a browser) or local sui
keypair (in a shell). We never hold, request, or see your keys. The phrase "you sign" appears
throughout these docs for exactly this reason.
Next: Quickstart.