Agent Integration Guide
How an external AI agent (or any automated system) executes transactions through a BVCC Agent Wallet (BVCCAgentWalletV2). All permissions and spending limits are enforced on-chain — the agent cannot exceed them no matter what its code does.
How it works
- ●The wallet owner authorizes an agent address from the app (
/wallet/agents), signing with biometrics / WebAuthn (passkey). This callsauthorizeAgent()on the wallet with the agent’s limits. - ●The agent calls
executeAsAgent()on the wallet directly, as a normal EVM transaction — no ERC-4337 UserOp, no bundler, no WebAuthn signature needed. - ●The agent pays gas from its own EOA. The transferred funds come from the wallet.
- ●Every execution is validated against the agent’s on-chain permission set and charged a 0.15% protocol fee.
Two hard requirements for the agent account:
- ●Must be an EOA.
authorizeAgentrejects addresses with deployed code (AgentMustBeEOA). Smart-contract agents are not supported. - ●Must be authorized and not expired at execution time, and agents must not be paused (
pauseAgents()is the owner’s emergency stop).
The entrypoint: executeAsAgent
mode — ERC-7821/7579 execution mode. Use the batch mode constant:
executionData — ABI-encoded array of Execution structs:
A single call can batch multiple executions; limits are checked over the whole batch (cumulative ETH and cumulative per-token amounts).
The four call types
Each Execution item is classified on-chain and checked against the matching whitelist:
| Case | Shape | Checks applied |
|---|---|---|
| 1. ETH send | value > 0, empty callData | recipient whitelist |
| 2. ERC-20 transfer | callData starts with 0xa9059cbb (transfer(address,uint256)) | token whitelist + per-token amount cap + recipient whitelist |
| 2b. ERC-20 approve | callData starts with 0x095ea7b3 (approve(address,uint256)) | token whitelist + per-token amount cap + spender checked against recipient whitelist |
| 3. DeFi / anything else | any other callData | target must be in the protocol whitelist |
- ●The agent can never call the wallet itself (
AgentCannotCallWallet) — owner functions stay owner-only. - ●
approveamounts count toward per-token daily/total budgets, same as transfers. This is intentionally conservative: it prevents draining via an externaltransferFromafter a large approve.
Spending limits
All set per-agent in authorizeAgent. 0 always means unlimited / disabled. Checked in this order:
| Limit | Scope | Error on violation |
|---|---|---|
maxPerTxWei | ETH per single Execution item | ExceedsPerTxLimit |
tokenMaxAmounts[i] | token amount per batch (per token) | TokenBatchLimitExceeded / ExceedsTokenMaxAmount |
tokenDailyLimits[i] | token amount per UTC day | TokenDailyLimitExceeded |
tokenTotalBudgets[i] | token amount lifetime | TokenTotalBudgetExceeded |
periodBudgetWei + periodDuration | ETH per rolling period (auto-rollover when the period elapses) | PeriodBudgetExceeded |
dailyLimitWei | ETH per UTC day (resets at 00:00 UTC) | DailyLimitExceeded |
totalBudgetWei | ETH lifetime | AgentBudgetExceeded |
expiry | unix timestamp after which the agent is disabled | AgentPermissionsExpired |
totalSpentWei, periodSpentWei, per-token spent) is preserved across re-authorizations — the owner can change limits without resetting what the agent already spent.Whitelists
Max 20 entries each. Semantics differ:
| Whitelist | Empty means |
|---|---|
allowedTokens | deny all ERC-20 transfers/approves |
allowedProtocols | deny all DeFi calls (case 3) |
allowedRecipients | any destination allowed (applies to ETH recipients, token recipients, and approve spenders when set) |
Fees
Every agent execution pays a 0.15% protocol fee to the BVCC fee wallet (0x3e3eb089169a7315a994947465ce5f5FC3A307D4), versus 0.05% for the personal smart wallet. Fee logic per call type: deducted from ETH sends, charged on top of token transfers (the wallet must hold amount + fee), and computed from balance deltas on DeFi calls.
Reading agent state
A well-behaved agent should read these before submitting and size its transaction to fit the remaining budget.
Error reference
All reverts use custom errors (4-byte selectors). The most relevant for an agent:
| Error | Cause |
|---|---|
NotAuthorizedAgent() | caller is not an active agent (never authorized, or revoked) |
AgentPermissionsExpired() | block.timestamp >= expiry |
EnforcedPause() | owner called pauseAgents() (OpenZeppelin Pausable) |
AgentCannotCallWallet() | an Execution.target is the wallet itself |
ExceedsPerTxLimit() | one item’s value > maxPerTxWei |
DailyLimitExceeded() / PeriodBudgetExceeded() / AgentBudgetExceeded() | ETH limits (day / period / lifetime) |
NoTokensWhitelisted() / TokenNotAllowed() | token transfer with empty whitelist / token not listed |
ExceedsTokenMaxAmount() / TokenBatchLimitExceeded() | per-tx/batch token cap |
TokenDailyLimitExceeded() / TokenTotalBudgetExceeded() | per-token day / lifetime budgets |
NoProtocolsWhitelisted() / ProtocolNotAllowed() | DeFi call with empty whitelist / target not listed |
RecipientNotAllowed() | destination/spender not in allowedRecipients |
Owner-side errors (OnlyWallet, InvalidAgent, AgentMustBeEOA, ArrayLengthMismatch, TooManyTokens/Protocols/Recipients, UnknownAgent, AgentNotActive, ZeroAmount) only appear when authorizing/managing agents.
Events
AgentExecution is the one to index for monitoring agent activity off-chain.
Example A — Foundry script (Solidity)
Send ETH from the agent wallet, signed by the agent EOA:
Example B — TypeScript / viem
The same call from Node.js — an ETH send plus a USDC transfer in one batch:
If the call reverts, simulate it first (client.simulateContract with the same args) — viem decodes the custom error name, which maps directly to the error reference above.