Skip to main content

Automate DeFi Trades with Agentic Signers

An agentic signer is an autonomous program that holds a session key and can execute blockchain transactions on behalf of a user. Unlike traditional bots that require the user’s private key, agentic signers operate with:
  • Limited scope: Can only perform specific, pre-approved actions
  • Time bounds: Authorization expires automatically
  • Spending limits: Cannot exceed defined value thresholds
  • Revocability: User can cancel at any time
Traditional Bot:          Agentic Signer:
Full private key access   Limited session key
Can do anything           Scoped permissions only
Trust the bot operator    Trustless constraints
Hard to revoke            Instant revocation
StrategyDescriptionExample
DCA (Dollar-Cost Averaging)Buy fixed amounts at intervalsBuy $100 ETH every week
Stop-LossSell when price drops below thresholdSell if ETH < $2000
Take-ProfitSell when price exceeds targetSell 50% if ETH > $5000
RebalancingMaintain target portfolio ratiosKeep 60% ETH, 40% stables
Yield HarvestingAuto-compound farming rewardsClaim & restake daily
ArbitrageExploit price differencesCross-DEX arbitrage
Grid TradingBuy/sell at price intervalsBuy every 50drop,sellevery50 drop, sell every 50 rise
Step 1: Create a smart account with MEE
import { createMeeClient, toMultichainNexusAccount } from "@biconomy/abstractjs";

const account = await toMultichainNexusAccount({
  signer: ownerSigner,
  chains: [base, arbitrum]
});

const meeClient = await createMeeClient({ account });
Biconomy’s MEE (Modular Execution Environment) provides all the functionality of traditional ERC-4337 bundlers and paymasters, with additional cross-chain orchestration capabilities.
Step 2: Define trading permissions
const tradingPolicy = {
  permissions: [
    {
      type: "contract-call",
      target: UNISWAP_ROUTER,
      functions: ["exactInputSingle", "exactOutputSingle"],
      constraints: {
        // Max $1000 per trade
        maxValuePerTx: parseEther("0.5"),
        // Max $5000 per day
        dailyLimit: parseEther("2.5"),
        // Only approved token pairs
        allowedTokens: [WETH, USDC, WBTC]
      }
    }
  ],
  validUntil: Date.now() + 30 * 24 * 60 * 60 * 1000 // 30 days
};
Step 3: Create session for the bot
const session = await smartAccount.createSession({
  policy: tradingPolicy,
  sessionSigner: botSignerAddress
});

console.log("Session ID:", session.id);
// Share session with your trading bot
Step 4: Run the trading bot
// In your bot service
const bot = new TradingBot({
  sessionKey: session.key,
  accountAddress: smartAccount.address,
  strategies: [
    { type: "dca", token: "ETH", amount: 100, interval: "weekly" },
    { type: "stop-loss", token: "ETH", trigger: 2000 }
  ]
});

bot.start();
import { createMeeClient, toMultichainNexusAccount, createSession } from "@biconomy/abstractjs";
import { encodeFunctionData, parseUnits } from "viem";

class DCABot {
  constructor(config) {
    this.sessionKey = config.sessionKey;
    this.account = config.account;
    this.token = config.token;
    this.amount = config.amount; // in USD
    this.interval = config.interval; // in ms
  }

  async executeBuy() {
    const swapData = encodeFunctionData({
      abi: uniswapRouterAbi,
      functionName: "exactInputSingle",
      args: [{
        tokenIn: USDC_ADDRESS,
        tokenOut: this.token,
        fee: 3000,
        recipient: this.account,
        amountIn: parseUnits(this.amount.toString(), 6),
        amountOutMinimum: 0n,
        sqrtPriceLimitX96: 0n
      }]
    });

    const { hash } = await this.executeWithSession({
      to: UNISWAP_ROUTER,
      data: swapData
    });

    console.log(`DCA executed: ${hash}`);
    return hash;
  }

  start() {
    // Execute immediately, then on interval
    this.executeBuy();
    setInterval(() => this.executeBuy(), this.interval);
  }

  async executeWithSession(tx) {
    // Use session key to sign and submit
    return await sendTransactionWithSession({
      sessionKey: this.sessionKey,
      accountAddress: this.account,
      ...tx
    });
  }
}

// Usage
const dcaBot = new DCABot({
  sessionKey: session.key,
  account: smartAccount.address,
  token: WETH_ADDRESS,
  amount: 100, // $100 USDC
  interval: 7 * 24 * 60 * 60 * 1000 // Weekly
});

dcaBot.start();
class PriceMonitorBot {
  constructor(config) {
    this.sessionKey = config.sessionKey;
    this.account = config.account;
    this.positions = config.positions;
    this.priceFeeds = config.priceFeeds;
  }

  async checkPrices() {
    for (const position of this.positions) {
      const currentPrice = await this.getPrice(position.token);
      
      // Check stop-loss
      if (position.stopLoss && currentPrice <= position.stopLoss) {
        console.log(`Stop-loss triggered for ${position.token}`);
        await this.executeSell(position, "stop-loss");
      }
      
      // Check take-profit
      if (position.takeProfit && currentPrice >= position.takeProfit) {
        console.log(`Take-profit triggered for ${position.token}`);
        await this.executeSell(position, "take-profit");
      }
    }
  }

  async executeSell(position, reason) {
    const balance = await this.getBalance(position.token);
    const sellAmount = position.sellPercentage 
      ? (balance * BigInt(position.sellPercentage)) / 100n
      : balance;

    const swapData = encodeFunctionData({
      abi: uniswapRouterAbi,
      functionName: "exactInputSingle",
      args: [{
        tokenIn: position.token,
        tokenOut: USDC_ADDRESS,
        fee: 3000,
        recipient: this.account,
        amountIn: sellAmount,
        amountOutMinimum: 0n,
        sqrtPriceLimitX96: 0n
      }]
    });

    const { hash } = await this.executeWithSession({
      to: UNISWAP_ROUTER,
      data: swapData
    });

    console.log(`${reason} executed: ${hash}`);
    
    // Remove or update position
    this.updatePosition(position, reason);
  }

  start() {
    // Check prices every minute
    setInterval(() => this.checkPrices(), 60 * 1000);
  }
}

// Usage
const monitorBot = new PriceMonitorBot({
  sessionKey: session.key,
  account: smartAccount.address,
  positions: [
    {
      token: WETH_ADDRESS,
      stopLoss: 2000,      // Sell if ETH drops to $2000
      takeProfit: 5000,    // Sell if ETH rises to $5000
      sellPercentage: 50   // Sell 50% of position
    }
  ],
  priceFeeds: CHAINLINK_PRICE_FEEDS
});

monitorBot.start();
import { createSession } from "@biconomy/abstractjs";
import { OpenAI } from "openai";

class AITradingAgent {
  constructor(config) {
    this.sessionKey = config.sessionKey;
    this.account = config.account;
    this.ai = new OpenAI({ apiKey: config.openaiKey });
    this.portfolio = config.portfolio;
  }

  async analyzeMarket() {
    // Gather market data
    const prices = await this.getPrices(this.portfolio.tokens);
    const trends = await this.getTechnicalIndicators(this.portfolio.tokens);
    const sentiment = await this.getSentiment(this.portfolio.tokens);

    // Ask AI for trading decision
    const response = await this.ai.chat.completions.create({
      model: "gpt-4",
      messages: [
        {
          role: "system",
          content: `You are a DeFi trading agent. Analyze market data and suggest trades.
            Portfolio: ${JSON.stringify(this.portfolio)}
            Risk tolerance: ${this.portfolio.riskLevel}
            Max trade size: $${this.portfolio.maxTradeSize}`
        },
        {
          role: "user",
          content: `Current market data:
            Prices: ${JSON.stringify(prices)}
            Technical indicators: ${JSON.stringify(trends)}
            Sentiment: ${JSON.stringify(sentiment)}
            
            Should we make any trades? Respond with JSON:
            { "action": "buy"|"sell"|"hold", "token": "...", "amount": ..., "reason": "..." }`
        }
      ]
    });

    return JSON.parse(response.choices[0].message.content);
  }

  async executeTrade(decision) {
    if (decision.action === "hold") {
      console.log("AI decision: Hold positions");
      return;
    }

    const tradeData = this.buildTradeCalldata(decision);
    
    // Validate against session constraints
    if (!await this.validateTrade(decision)) {
      console.log("Trade exceeds session limits, skipping");
      return;
    }

    const { hash } = await this.executeWithSession(tradeData);
    console.log(`AI trade executed: ${decision.action} ${decision.token}, tx: ${hash}`);
    
    // Log decision for review
    await this.logDecision(decision, hash);
  }

  async run() {
    const decision = await this.analyzeMarket();
    await this.executeTrade(decision);
  }

  start(intervalMs = 60 * 60 * 1000) { // Hourly by default
    this.run(); // Run immediately
    setInterval(() => this.run(), intervalMs);
  }
}

// Usage
const aiAgent = new AITradingAgent({
  sessionKey: session.key,
  account: smartAccount.address,
  openaiKey: process.env.OPENAI_API_KEY,
  portfolio: {
    tokens: [WETH, WBTC, USDC],
    riskLevel: "moderate",
    maxTradeSize: 500
  }
});

aiAgent.start();
class YieldHarvester {
  constructor(config) {
    this.sessionKey = config.sessionKey;
    this.account = config.account;
    this.farms = config.farms;
  }

  async harvestAndCompound() {
    for (const farm of this.farms) {
      try {
        // Check pending rewards
        const pending = await this.getPendingRewards(farm);
        if (pending < farm.minHarvestAmount) continue;

        // Batch: Claim → Swap → Redeposit
        const transactions = [
          // 1. Claim rewards
          {
            to: farm.address,
            data: encodeFunctionData({
              abi: farm.abi,
              functionName: "claim"
            })
          },
          // 2. Swap rewards to deposit token
          {
            to: UNISWAP_ROUTER,
            data: encodeSwap(farm.rewardToken, farm.depositToken, pending)
          },
          // 3. Redeposit
          {
            to: farm.address,
            data: encodeFunctionData({
              abi: farm.abi,
              functionName: "deposit",
              args: [runtime.outputOf(1)] // Use swap output
            })
          }
        ];

        const { hash } = await this.executeWithSession({ transactions });
        console.log(`Harvested & compounded ${farm.name}: ${hash}`);
      } catch (error) {
        console.error(`Failed to harvest ${farm.name}:`, error);
      }
    }
  }

  start() {
    // Harvest every 12 hours
    setInterval(() => this.harvestAndCompound(), 12 * 60 * 60 * 1000);
  }
}

// Usage
const harvester = new YieldHarvester({
  sessionKey: session.key,
  account: smartAccount.address,
  farms: [
    {
      name: "Aave USDC",
      address: AAVE_POOL,
      rewardToken: AAVE_TOKEN,
      depositToken: USDC,
      minHarvestAmount: parseUnits("10", 18) // 10 AAVE
    }
  ]
});

harvester.start();
1. Use restrictive session policies:
const securePolicy = {
  permissions: [
    {
      type: "contract-call",
      target: UNISWAP_ROUTER,
      // Only specific functions
      functions: ["exactInputSingle"],
      constraints: {
        // Strict value limits
        maxValuePerTx: parseEther("0.1"),
        dailyLimit: parseEther("1.0"),
        totalLimit: parseEther("10.0"),
        // Only approved tokens
        allowedTokens: [WETH, USDC],
        // Rate limiting
        minTimeBetweenTxs: 60 * 1000 // 1 minute
      }
    }
  ],
  // Short validity period
  validUntil: Date.now() + 7 * 24 * 60 * 60 * 1000 // 7 days
};
2. Monitor bot activity:
// Set up alerts
const monitor = new BotMonitor({
  sessionId: session.id,
  alerts: [
    { type: "daily-spend-exceeded", threshold: 0.5 }, // ETH
    { type: "unusual-activity", sensitivity: "high" },
    { type: "consecutive-failures", count: 3 }
  ],
  notificationChannels: ["email", "telegram"]
});
3. Implement kill switches:
// Automatic session revocation on anomaly
if (await detectAnomalousActivity(botActivity)) {
  await smartAccount.revokeSession(session.id);
  await notifyOwner("Session revoked due to unusual activity");
}
4. Use separate hot wallets:
// Only keep trading capital in smart account
// Keep long-term holdings in separate cold storage
const tradingAccount = await createSmartAccount({...});
await fundTradingAccount(tradingAccount, TRADING_CAPITAL);
// Never give bot access to main holdings
PracticeDescription
Start smallTest with minimal capital before scaling
Set strict limitsUse daily/total spending caps
Monitor continuouslySet up alerts and dashboards
Use testnets firstValidate strategies on testnets
Log everythingKeep detailed records for debugging
Have kill switchesAbility to stop bot immediately
Diversify strategiesDon’t put all funds in one strategy
Regular reviewsCheck bot performance weekly
Update session keysRotate session keys regularly
Audit your codeHave trading logic reviewed

Get started with automation