Skip to main content

Overview

Sardis wallets are non-custodial, MPC-secured wallets designed specifically for AI agents. Unlike traditional custodial wallets, Sardis never holds your funds or private keys—all assets remain on-chain, and transactions are signed using Multi-Party Computation (MPC) through providers like Turnkey or Fireblocks.
Non-custodial means:
  • Funds are held on-chain, not in our database
  • Balances are read directly from the blockchain
  • Private keys never leave secure enclaves
  • You maintain full custody of your agent’s assets

Architecture

Every AI agent has its own programmable wallet that enforces spending policies before executing transactions:

Supported Chains & Tokens

Sardis wallets support multi-chain stablecoin payments:
ChainSupported Tokens
Arc (Circle L1)USDC, EURC
BaseUSDC, EURC
PolygonUSDC, USDT, EURC
EthereumUSDC, USDT, PYUSD, EURC
ArbitrumUSDC, USDT
OptimismUSDC, USDT
Base is recommended for lowest transaction costs and fastest confirmation times.

Creating a Wallet

Simple SDK

from sardis import Agent

# Create an agent with a wallet
agent = Agent(name="Shopping Assistant")
wallet = agent.create_wallet(
    initial_balance=100,
    currency="USDC",
    limit_per_tx=50,
    limit_total=1000
)

print(f"Wallet ID: {wallet.wallet_id}")
print(f"Balance: ${wallet.balance} {wallet.currency}")

Core API

from sardis_v2_core import Wallet, TokenType

# Create a new non-custodial wallet
wallet = Wallet.new(
    agent_id="agent_xxx",
    mpc_provider="turnkey",  # or "fireblocks", "local"
    account_type="mpc_v1",
    currency="USDC"
)

# Set address for a specific chain
wallet.set_address("base", "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb")

Reading On-Chain Balances

Balances are always read directly from the blockchain, never stored in our database:
from sardis_v2_core import Wallet, TokenType
from sardis_chain import ChainRPCClient

# Get wallet balance from chain
rpc_client = ChainRPCClient(chain="base")
balance = await wallet.get_balance(
    chain="base",
    token=TokenType.USDC,
    rpc_client=rpc_client
)

print(f"Balance: {balance} USDC")
Balances are queried using the ERC20 balanceOf function. Sardis never caches balances—you always get real-time data.

Wallet Types

Sardis supports multiple wallet architectures:
Smart contract wallets with gasless transactions:
  • Sponsored gas via paymasters
  • Batched transactions
  • Session keys for recurring payments
  • Social recovery
wallet = Wallet.new(
    agent_id="agent_123",
    account_type="erc4337_v2",
    paymaster_enabled=True
)
Gnosis Safe multi-signature wallets:
  • Multiple owners (humans + agents)
  • Configurable threshold (e.g., 2-of-3)
  • On-chain governance
  • Time-locked transactions
wallet = Wallet.new(
    agent_id="agent_123",
    account_type="safe_v1"
)

Funding & Withdrawals

Funding a Wallet

Since wallets are non-custodial, you fund them by sending tokens directly to the wallet’s on-chain address:
# Get the wallet address for a specific chain
address = wallet.get_address("base")
print(f"Send USDC to: {address} on Base")

# After sending, check the balance
balance = await wallet.get_balance(
    chain="base",
    token=TokenType.USDC,
    rpc_client=rpc_client
)
Always verify the chain and token address before sending funds. Sending tokens to the wrong chain or contract address will result in permanent loss.

Withdrawing Funds

Withdraw funds to an external address:
from sardis import Agent

agent = Agent(name="My Agent")
result = agent.pay(
    to="0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
    amount=50,
    purpose="Withdrawal to external wallet"
)

if result.success:
    print(f"Withdrawal successful: {result.tx_hash}")

Wallet Limits

Wallets have configurable spending limits that work alongside policies:
from decimal import Decimal

# Set per-transaction limit
wallet.limit_per_tx = Decimal("100.00")

# Set total lifetime limit
wallet.limit_total = Decimal("5000.00")

# Token-specific limits
from sardis_v2_core import TokenLimit, TokenType

wallet.token_limits["USDC"] = TokenLimit(
    token=TokenType.USDC,
    limit_per_tx=Decimal("200.00"),
    limit_total=Decimal("10000.00")
)

Wallet Security

Freezing a Wallet

Wallets can be frozen to block all transactions (e.g., for compliance violations or suspicious activity):
# Freeze the wallet
wallet.freeze(
    by="[email protected]",
    reason="Suspicious activity detected"
)

# Check if frozen
if wallet.is_frozen:
    print(f"Frozen by: {wallet.frozen_by}")
    print(f"Reason: {wallet.freeze_reason}")

# Unfreeze when resolved
wallet.unfreeze()

Virtual Cards

Wallets can be linked to virtual debit cards for fiat rails (via Lithic):
from sardis_v2_core import VirtualCard

# Attach a virtual card to the wallet
wallet.virtual_card = VirtualCard(
    card_id="card_123",
    last_four="4242",
    exp_month=12,
    exp_year=2025,
    network="VISA"
)

# Use for fiat payments
result = await wallet.pay_with_card(
    merchant="stripe.com",
    amount=Decimal("25.00")
)

Code Example: Full Wallet Lifecycle

wallet_lifecycle.py
from sardis import Agent, Policy
from decimal import Decimal

# Step 1: Create an agent with a wallet
agent = Agent(
    name="E-commerce Bot",
    description="Autonomous shopping agent",
    policy=Policy(
        max_per_tx=100,
        max_total=1000,
        allowed_destinations={"amazon.*", "shopify.*"}
    )
)

# Step 2: Create wallet
wallet = agent.create_wallet(
    initial_balance=500,
    currency="USDC",
    limit_per_tx=100,
    limit_total=1000
)

print(f"Created wallet: {wallet.wallet_id}")
print(f"Initial balance: ${wallet.balance}")

# Step 3: Make a payment
result = agent.pay(
    to="merchant:shopify",
    amount=25,
    purpose="Product purchase"
)

if result.success:
    print(f"Payment successful: {result.tx_hash}")
    print(f"New balance: ${agent.primary_wallet.balance}")
    print(f"Spent total: ${agent.primary_wallet.spent_total}")
    print(f"Remaining limit: ${agent.primary_wallet.remaining_limit()}")
else:
    print(f"Payment failed: {result.message}")

API Reference

Wallet Model

class Wallet(BaseModel):
    wallet_id: str
    agent_id: str
    mpc_provider: str  # "turnkey" | "fireblocks" | "local"
    account_type: Literal["mpc_v1", "erc4337_v2", "safe_v1"]
    addresses: dict[str, str]  # chain -> address mapping
    currency: str  # Default display currency
    limit_per_tx: Decimal
    limit_total: Decimal
    is_active: bool
    is_frozen: bool
    created_at: datetime
    updated_at: datetime

Key Methods

balance = await wallet.get_balance(
    chain="base",
    token=TokenType.USDC,
    rpc_client=rpc_client
)
# Returns: Decimal (e.g., Decimal("100.00"))

Next Steps

Policies

Learn how to enforce spending rules with natural language policies

Agents

Create AI agents with payment capabilities

Payments

Understand the payment execution flow

Compliance

KYC/AML and Know Your Agent verification

Build docs developers (and LLMs) love