# How to Set Spending Limits for LangChain Agents on Ethereum

> Learn how to wrap LangChain Tools with PolicyLayer to enforce hard spending limits on AI agents making blockchain transactions.

Published: Sun Nov 30

Canonical: https://policylayer.com/blog/securing-langchain-agents-ethereum

**LangChain** has become the standard for building reasoning loops, but out of the box, it lacks a secure wallet primitive. Most tutorials suggest passing a private key directly to a `Tool`. This is dangerous for production.

This guide shows you how to wrap a LangChain Tool with **PolicyLayer** to enforce hard spending limits.

<!--truncate-->

## The Risk of Raw Keys in Tools

When you define a Custom Tool in LangChain for blockchain interactions, it usually looks like this:

```typescript
class SendEthTool extends Tool {
  name = "send_eth";
  description = "Sends ETH to an address";
  async _call(input: string) {
    // DANGER: No limits here!
    const wallet = new Wallet(process.env.PRIVATE_KEY);
    return wallet.sendTransaction(...);
  }
}
```

The problems with this approach:

1. **No spending limits** — The LLM can send any amount
2. **No recipient validation** — Any address is valid
3. **No velocity controls** — Infinite transactions possible
4. **No audit trail** — No record of what was attempted vs executed

If the LLM hallucinates the input amount or the recipient, the transaction executes immediately. A single prompt injection could drain the entire wallet.

## The Solution: Policy-Wrapped Tools

Instead of giving tools direct wallet access, we wrap them with PolicyLayer. The LLM interacts with the tool normally, but every transaction passes through policy enforcement.

```
┌─────────────┐     ┌─────────────┐     ┌─────────────┐     ┌──────────┐
│  LangChain  │────▶│  Secure     │────▶│ PolicyLayer │────▶│ Blockchain│
│    Agent    │     │    Tool     │     │  (2-Gate)   │     │          │
└─────────────┘     └─────────────┘     └─────────────┘     └──────────┘
```

## Step 1: Install Dependencies

```bash
npm install @policylayer/sdk langchain @langchain/openai ethers
```

## Step 2: Create the Policy-Aware Tool

We modify the tool to use PolicyLayer's **Two-Gate Enforcement**:

```typescript
import { PolicyWallet, createEthersAdapter } from '@policylayer/sdk';
import { Tool } from 'langchain/tools';

class SecureSendTool extends Tool {
  name = "secure_send_eth";
  description = "Safely sends ETH with spending limits. Input: JSON with 'to' (address) and 'amount' (in wei)";

  private wallet: PolicyWallet;

  constructor(wallet: PolicyWallet) {
    super();
    this.wallet = wallet;
  }

  async _call(input: string): Promise<string> {
    let parsed;
    try {
      parsed = JSON.parse(input);
    } catch {
      return "ERROR: Invalid JSON input. Expected {to: address, amount: string}";
    }

    const { to, amount } = parsed;

    if (!to || !amount) {
      return "ERROR: Missing required fields 'to' and 'amount'";
    }

    try {
      // PolicyWallet.send() handles both gates automatically:
      // 1. Validates intent against spending limits
      // 2. Verifies fingerprint to prevent tampering
      // 3. Signs and broadcasts only if approved
      const result = await this.wallet.send({
        chain: 'ethereum',
        asset: 'eth',
        to,
        amount
      });
      return `SUCCESS: Transaction sent. Hash: ${result.hash}`;

    } catch (error: any) {
      // Policy denials have code POLICY_DECISION_DENY
      // The specific reason is in error.message
      if (error.code === 'POLICY_DECISION_DENY') {
        if (error.message.includes('DAILY_LIMIT')) {
          return `BLOCKED: Daily spending limit reached. Try again tomorrow.`;
        }
        if (error.message.includes('PER_TX_LIMIT')) {
          return `BLOCKED: Amount exceeds per-transaction limit.`;
        }
        if (error.message.includes('RECIPIENT_NOT_WHITELISTED')) {
          return `BLOCKED: Recipient address not approved.`;
        }
      }
      return `BLOCKED: ${error.message}`;
    }
  }
}
```

## Step 3: Set Up the Agent

Create the complete LangChain agent with policy-protected tools:

```typescript
import { ChatOpenAI } from '@langchain/openai';
import { AgentExecutor, createOpenAIToolsAgent } from 'langchain/agents';
import { PolicyWallet, createEthersAdapter } from '@policylayer/sdk';

async function createSecureAgent() {
  // 1. Create policy-wrapped wallet
  const adapter = await createEthersAdapter(
    process.env.PRIVATE_KEY!,
    process.env.RPC_URL!
  );

  const policyWallet = new PolicyWallet(adapter, {
    apiUrl: 'https://api.policylayer.com',
    apiKey: process.env.POLICYLAYER_API_KEY!
  });

  // 2. Create secure tools
  const tools = [
    new SecureSendTool(policyWallet),
    // Add other policy-wrapped tools...
  ];

  // 3. Create agent
  const llm = new ChatOpenAI({ modelName: 'gpt-4' });
  const agent = await createOpenAIToolsAgent({
    llm,
    tools,
    prompt: yourPromptTemplate
  });

  // 4. Create executor
  return new AgentExecutor({
    agent,
    tools,
    verbose: true
  });
}

// Usage
const agent = await createSecureAgent();
const result = await agent.invoke({
  input: "Send 0.1 ETH to 0xBob..."
});
```

## Step 4: Configure Policies

Set up your spending limits in the PolicyLayer dashboard or via API:

```typescript
// Example policy configuration
const policy = {
  perTransactionLimit: '100000000000000000',  // 0.1 ETH max per tx
  dailyLimit: '1000000000000000000',          // 1 ETH max per day
  hourlyLimit: '500000000000000000',          // 0.5 ETH max per hour
  allowedRecipients: [                         // Optional whitelist
    '0xAlice...',
    '0xBob...',
    '0xContract...'
  ]
};
```

## Multi-Tool Scenarios

Most agents need multiple financial tools. Each should be policy-wrapped:

```typescript
const tools = [
  new SecureSendEthTool(policyWallet),
  new SecureSendUsdcTool(policyWallet),
  new SecureSwapTool(policyWallet),
  new CheckBalanceTool(policyWallet),  // Read-only, no policy needed
];
```

PolicyLayer tracks spending across all tools. If the agent uses `SendEth` and `SendUsdc` in the same day, both count towards the daily limit.

## Error Handling Best Practices

Return structured error messages so the LLM can reason about failures:

```typescript
async _call(input: string): Promise<string> {
  try {
    const result = await this.wallet.send(/* ... */);
    return JSON.stringify({
      status: 'success',
      hash: result.hash,
      amount: result.amount
    });
  } catch (error: any) {
    return JSON.stringify({
      status: 'blocked',
      reason: error.code,
      message: error.message,
      suggestion: getSuggestion(error.code)
    });
  }
}

function getSuggestion(code: string): string {
  switch (code) {
    case 'DAILY_LIMIT_EXCEEDED':
      return 'Wait until tomorrow or request limit increase';
    case 'PER_TX_LIMIT_EXCEEDED':
      return 'Split into smaller transactions';
    case 'RECIPIENT_NOT_WHITELISTED':
      return 'Request recipient approval from admin';
    default:
      return 'Contact support';
  }
}
```

## Works with Other Frameworks

The same pattern applies to other agent frameworks:

- **CrewAI** — Wrap tools the same way
- **AutoGPT** — Integrate via plugin system
- **Custom agents** — Any code calling wallet functions

The key insight: **policy enforcement happens at the wallet layer**, not the agent layer. This means you can swap agent frameworks without changing your security model.

## Why This Matters

By wrapping the execution logic:

1. **The Agent is oblivious** — The LLM just tries to use the tool
2. **The Policy is sovereign** — If the LLM tries to send 100 ETH, PolicyLayer blocks it before signing
3. **Failures are graceful** — The agent receives an error message it can reason about
4. **Audit trail is complete** — Every attempt is logged, successful or not

This is the only way to safely deploy **LangChain agents** that handle real value on mainnet.

---

**Related reading:**
- [Why Prompts Aren't Security](/blog/why-prompt-guardrails-fail-agent-safety)
- [How Two-Gate Enforcement Works](/blog/two-gate-enforcement-explained)

**Ready to secure your AI agents?**
- [Quick Start Guide](/docs/quick-start) - Get running in 5 minutes
- [GitHub](https://github.com/PolicyLayer/PolicyLayer) - Open source SDK
