SweeFi
A payment SDK for AI agents on Sui, where the spending limit is a Move object the agent cannot edit and its human can revoke at any time.
Retired September 2026 in favor of x402, the open standard.
The problem
An agent’s spending limit usually lives inside the agent. It is a config value or a line in a prompt, sitting in the same process the model can be talked into. A prompt injection that reaches the payment call rewrites the limit and the payee together.
Two objects and one transaction
Mandate is a Sui object the agent owns, carrying max_per_tx, max_total and a running total_spent. RevocationRegistry is a shared object the human owns. mandatedPay puts the check and the transfer into a single programmable transaction block, so both commit or neither does.
Why it holds
Before reading the revocation flag, the contract asserts registry.owner == mandate.delegator. Without that one line a rogue delegate hands in an empty registry it created itself and the kill switch never fires — audit finding C-2. A test runs that exact attack on every build.
What it does not do
There is no third-party audit — every review so far is in-house — and the contracts are on Sui testnet only. A stolen delegate key still spends: the mandate bounds the size of a payment, never the intent behind it. It is a policy gate rather than custody, so it says nothing about coins the agent holds outside this call.
Retired · Sui testnet · 205 Move + 733 SDK tests green · unaudited · never on mainnet
PTB — the agent pays
const client = suiClient.$extend(sweefi()) tx.add(client.sweefi.mandate.mandatedPay({ coinType: '0x2::sui::SUI', recipient: seller, amount: 250_000_000n, mandateId, registryId, }))
Abort — the human revoked
MoveAbort(mandate, 405) { code: 'MANDATE_REVOKED', retryable: false, requiresHumanAction: true, suggestedAction: 'Wait for the user to respond.' }
The refusal carries its own recovery rule — retry, or stop and go find the human — so the agent never has to guess which one this was.
The vocabulary
- delegator
- the human. The only address that can revoke
- delegate
- the agent. Transferring the object away changes nothing
- max_per_tx
- ceiling on one spend. Zero means none, not unlimited
- max_total
- lifetime ceiling, summed in u128 so overflow aborts cleanly
- the registry
- the kill switch. Passing it is mandatory — no unchecked path
- abort 404
- the registry was not the delegator’s. The bypass, caught
A fork, with an interrupt that lands mid-session