Architecture Overview
Copy
Ask AI
+------------------------------------------------------------------+
| YOUR APPLICATION |
+------------------------------------------------------------------+
| Frontend (User Prepares And Enable Permission) |
| - SCA deployment if not already |
| - Smart session validator installation if not already |
| - Funds the SCA if required |
| - Enable session permissions |
+------------------------------------------------------------------+
| Backend (Agent Executes) |
| - Store agent private key securely |
| - Store sessionDetails per user |
| - Executes on behalf of user with enabled permissions |
+------------------------------------------------------------------+
| BICONOMY MEE NETWORK |
| - Executes transactions, enforces policies onchain |
+------------------------------------------------------------------+
Step 1: Setup
Install Dependencies
Copy
Ask AI
npm install @biconomy/abstractjs viem
Initialize SDK
Copy
Ask AI
import {
createMeeClient,
getMEEVersion,
MEEVersion,
SessionDetail,
toMultichainNexusAccount
} from "@biconomy/abstractjs";
import { Abi, Address, http, parseUnits, toFunctionSelector, encodeFunctionData } from "viem";
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";
import { arbitrum, base, optimism } from "viem/chains";
Step 2: Generate Agent Key
Your agent needs its own keypair. Store this securely in your backend.Copy
Ask AI
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";
// Generate once, store securely (e.g., AWS Secrets Manager, Vault)
const agentPrivateKey = generatePrivateKey();
const agentSigner = privateKeyToAccount(agentPrivateKey);
console.log("Agent address:", agentSigner.address);
// This address is what users will enable permissions to
Never expose the agent private key. Treat it like a production database password.
Step 3: User Onboarding (Frontend)
When a user wants to activate your agent, they enable permissions.Create User’s Account & Mee Client
Copy
Ask AI
// User's signer (from their wallet - Privy, MetaMask, etc.)
const userSigner = privateKeyToAccount(generatePrivateKey());
console.log("User address:", userSigner.address);
// Create the user's multichain account
const userAccount = await toMultichainNexusAccount({
signer: userSigner,
chainConfigurations: [
{ chain: base, transport: http(), version: getMEEVersion(MEEVersion.V2_1_0) },
{ chain: optimism, transport: http(), version: getMEEVersion(MEEVersion.V2_1_0) },
{ chain: arbitrum, transport: http(), version: getMEEVersion(MEEVersion.V2_1_0) }
]
});
// Create MEE client with session actions
const meeClient = await createMeeClient({ account: userAccount });
Prepare And Enable Permissions
While user wants to enable permissions, they do the following operations- Deploying the Smart Contract Account (SCA) if it doesn’t exist yet.
- Installing the Smart Session Validator module if not already present.
- Funding the SCA if additional funds are required.
- Granting (enabling) session permissions for the agent to act on the user’s behalf.
Copy
Ask AI
const MORPHO_VAULT: Address = "0x...";
const USDC: Address = "0x...";
// Define what your agent can do
const [depositAction] = userAccount.buildSessionAction({
type: "custom",
data: {
chainIds: [base.id],
contractAddress: MORPHO_VAULT,
functionSignature: toFunctionSelector("deposit(uint256,address)"),
// By default sudo policy will be applied
}
});
const [withdrawAction] = userAccount.buildSessionAction({
type: "custom",
data: {
chainIds: [base.id],
contractAddress: MORPHO_VAULT,
functionSignature: toFunctionSelector("withdraw(uint256,address,address)"),
// By default sudo policy will be applied
}
});
const [transferAction] = userAccount.buildSessionAction({
type: "transfer",
data: {
chainIds: [base.id],
contractAddress: USDC,
recipientAddress: "0x...",
amountLimitPerAction: parseUnits("5", 6),
maxAmountLimit: parseUnits("100", 6),
usageLimit: 100n,
validAfter: Math.floor(Date.now() / 1000),
validUntil: Math.floor(Date.now() / 1000) + 60 * 60 * 24
}
});
const prepareAndEnableSessionQuote = await meeClient.getSessionQuote({
mode: "PREPARE",
enableSession: {
redeemer: agentSigner.address,
// Allowed actions
actions: [depositAction, withdrawAction, transferAction],
// Max gas spend
maxPaymentAmount: parseUnits("20", 6), // 20 USDC for gas
},
simulation: { simulate: true },
feeToken: { address: USDC, chainId: base.id },
trigger: {
tokenAddress: USDC,
chainId: base.id,
amount: parseUnits("10", 6), // $10 USDC funding
},
});
// Store the sessions details for later use.
let sessionDetails: SessionDetail[] = [];
if (prepareAndEnableSessionQuote) {
const { hash } = await meeClient.executeSessionQuote(prepareAndEnableSessionQuote);
await meeClient.waitForSupertransactionReceipt({ hash });
if (prepareAndEnableSessionQuote.sessionDetails) sessionDetails = prepareAndEnableSessionQuote.sessionDetails;
}
// IMPORTANT: Send sessionDetails to your backend
await sendToBackend(sessionDetails, userAccount.addressOn(base.id));
Step 4: Store Session Data (Backend)
Your backend needs to store session details for each user:Copy
Ask AI
// Example database schema
interface UserSession {
userId: string;
accountAddress: string; // User's smart account address
sessionDetails: SessionDetail[]; // Can be JSON stringified while storing
createdAt: Date;
expiresAt: Date;
}
// Store session
async function storeSession(userId: string, accountAddress: string, sessionDetails: SessionDetail[]) {
await db.userSessions.insert({
userId,
accountAddress,
sessionDetails: JSON.stringify(sessionDetails),
createdAt: new Date(),
expiresAt: new Date(sessionDetails.sessionValidUntil * 1000)
});
}
Step 5: Agent Execution (Backend)
Your agent can now act on behalf of users:Copy
Ask AI
async function executeAgentAction(userId: string, action: AgentAction) {
// 1. Load user's session
const session = await db.userSessions.findOne({ userId });
const sessionDetails: SessionDetail[] = JSON.parse(session.sessionDetails);
// 2. Create agent's MEE client
const agentAccount = await toMultichainNexusAccount({
signer: agentSigner,
chainConfigurations: [
{
chain: base,
transport: http(),
version: getMEEVersion(MEEVersion.V2_1_0),
accountAddress: session.accountAddress // User's account address
}
],
});
const agentMeeClient = await createMeeClient({ account: agentAccount });
// 3. Build the instruction
const depositData = encodeFunctionData({
abi: MORPHO_ABI,
functionName: "deposit",
args: [parseUnits("5", 6), session.accountAddress],
});
const instructions = await agentAccount.build({
type: "default",
data: [{
calls: [{
to: MORPHO_VAULT,
data: depositData
}],
chainId: base.id,
}]
});
// 4. Get session quote
const quote = await agentMeeClient.getSessionQuote({
mode: "USE",
sessionDetails,
simulation: { simulate: true },
feeToken: { address: USDC, chainId: base.id },
instructions
});
// 5. Execute with permission
const { hash } = await agentMeeClient.executeSessionQuote(quote);
// 6. Wait for confirmation
const { receipts } = await agentMeeClient.waitForSupertransactionReceipt({ hash });
return receipts;
}
Step 6: Agent Logic
Implement your agent’s decision-making:Yield Optimization Agent
Copy
Ask AI
async function runYieldOptimizer() {
// Get all users with active sessions
const users = await db.userSessions.find({
expiresAt: { $gt: new Date() }
});
for (const user of users) {
// Check current positions
const positions = await getPositions(user.accountAddress);
// Find best yield opportunity
const bestOpportunity = await findBestYield();
// Rebalance if significantly better
if (bestOpportunity.apy > positions.currentApy * 1.1) {
// Withdraw from current
await executeAgentAction(user.userId, {
type: "withdraw",
protocol: positions.currentProtocol,
amount: positions.amount
});
// Deposit to better opportunity
await executeAgentAction(user.userId, {
type: "deposit",
protocol: bestOpportunity.protocol,
amount: positions.amount
});
}
}
}
// Run every hour
setInterval(runYieldOptimizer, 60 * 60 * 1000);
Trading Agent
Copy
Ask AI
async function runTradingAgent() {
const users = await getActiveUsers();
for (const user of users) {
const signal = await analyzeMarket(user.preferences);
if (signal.shouldTrade) {
await executeAgentAction(user.userId, {
type: "swap",
tokenIn: signal.fromToken,
tokenOut: signal.toToken,
amount: signal.amount
});
}
}
}
Sponsored Agent Execution
If you want to pay gas for your users:Copy
Ask AI
await agentMeeClient.getSessionQuote({
mode: "USE",
sessionDetails,
simulation: { simulate: true },
sponsorship: true, // You pay gas
instructions
});
When using
sponsorship: true, don’t set feeToken when enabling permissions.Monitoring & Observability
Copy
Ask AI
// Log all agent actions
async function executeWithLogging(userId: string, action: AgentAction) {
const startTime = Date.now();
try {
const receipts = await executeAgentAction(userId, action);
await logAgentAction({
userId,
action,
status: "success",
transactionHashes: receipts.map((receipt) => receipt.transactionHash),
duration: Date.now() - startTime
});
return result;
} catch (error) {
await logAgentAction({
userId,
action,
status: "failed",
error: error.message,
duration: Date.now() - startTime
});
throw error;
}
}
Security Checklist
Agent private key stored in secure secret management
Session details stored encrypted in database
Rate limiting on agent actions
Monitoring for unusual activity
Automatic session cleanup on expiry
User notification system for important events