> ## Content Index
> Fetch the complete content index at: https://stablecoininsider.org/llms.txt
> Use this file to discover other available public pages before exploring further.

# ElizaOS EVM Plugin: How to Send USDC Safely (2026)
- URL: https://stablecoininsider.org/elizaos-evm-plugin-send-usdc/
- Published: 2026-10-08T06:01:00.000Z
- Updated: 2026-10-08T06:25:17.000Z
- Description: Ask the ElizaOS EVM plugin for 25 USDC and it signs a 25 ETH transfer. Here's the test, the docs gap, and a guarded SEND_USDC action with caps.
- Author: Alexandra
- Tags: AI, Stablecoins

Ask an ElizaOS agent to send 25 USDC on Base, and the EVM plugin on npm will try to send 25 ETH. Stablecoin Insider signed that transaction in a sandbox and decoded it: right recipient, wrong asset, no token contract anywhere in it.

This guide covers what the **ElizaOS EVM plugin** actually does with a stablecoin request, why the docs and the code disagree, and how to send USDC safely anyway. You'll get the test results, a drop-in guarded SEND\_USDC action with an allowlist and caps, a Base Sepolia test plan, and a quick check of the Solana plugin's token path.

### Key Takeaways

- The npm EVM\_TRANSFER\_TOKENS action ignores the token field and sends native ETH.
- A 25 USDC request signed as a 25 ETH transfer with empty calldata.
- The docs promise ERC-20 transfers; the published 1.0.13 and 2.0.0-alpha.8 code don't.
- Swap and bridge read token decimals on-chain, so they handle USDC amounts correctly.
- Ship a guarded SEND\_USDC action: allowlist, caps, parseUnits with 6 decimals.

🧭

What this guide is: an operator test of the stablecoin transfer path in @elizaos/plugin-evm 1.0.13 (npm latest) and 2.0.0-alpha.8 (npm alpha), plus @elizaos/plugin-solana 1.2.6, checked against the published bundles and live Base and Ethereum reads on 8 October 2026\. No real funds moved; every signed transaction went to a local mock RPC.

## What the ElizaOS EVM plugin is, and which version npm installs

ElizaOS is an open-source TypeScript framework for building autonomous agents, maintained in the [elizaOS/eliza repository](https://github.com/elizaOS/eliza). It takes its name from [ELIZA](https://en.wikipedia.org/wiki/ELIZA), Joseph Weizenbaum's 1960s MIT chatbot. Agents get on-chain abilities through plugins, and the EVM plugin is the one that holds a private key and talks to Ethereum, Base, Arbitrum and other EVM chains.

The plugin ships three money-moving actions: transfer, swap, and bridge. Each one asks a small model to pull parameters out of the chat as XML, then executes. If your character configures no chains, the wallet provider falls back to Ethereum mainnet and Base, per the [published 1.0.13 source map](https://unpkg.com/@elizaos/plugin-evm@1.0.13/dist/index.js.map).

| Package                                                                        | Version tested    | npm tag | Published (UTC) | 30-day downloads                                                                 |
| ------------------------------------------------------------------------------ | ----------------- | ------- | --------------- | -------------------------------------------------------------------------------- |
| [@elizaos/plugin-evm](https://www.npmjs.com/package/@elizaos/plugin-evm)       | 1.0.13            | latest  | 8 Sep 2025      | [769](https://api.npmjs.org/downloads/point/last-month/@elizaos/plugin-evm)      |
| [@elizaos/plugin-evm](https://www.npmjs.com/package/@elizaos/plugin-evm)       | 2.0.0-alpha.8     | alpha   | 6 Apr 2026      | included above                                                                   |
| [@elizaos/plugin-solana](https://www.npmjs.com/package/@elizaos/plugin-solana) | 1.2.6             | latest  | 26 Oct 2025     | [1,045](https://api.npmjs.org/downloads/point/last-month/@elizaos/plugin-solana) |
| [@elizaos/core](https://www.npmjs.com/package/@elizaos/core)                   | framework runtime | latest  | n/a             | [41,356](https://api.npmjs.org/downloads/point/last-month/@elizaos/core)         |

Source: npm registry metadata and the [npm downloads API](https://api.npmjs.org/downloads/point/last-month/@elizaos/plugin-evm) for 5 September to 4 October 2026.

Here's why the numbers matter. The core runtime pulled 41,356 downloads in that window, but the EVM plugin pulled 769, per the [npm downloads API](https://api.npmjs.org/downloads/point/last-month/@elizaos/core). Most ElizaOS agents never touch an EVM wallet. The ones that do are running code that hasn't had a stable release since September 2025.

## The problem: EVM\_TRANSFER\_TOKENS sends ETH when you ask for USDC

The transfer prompt asks the model for five fields: chain, amount, recipient, token, and calldata. The token field is described as "token symbol or address (if not a native token transfer)". So far, so good.

Then the executor ignores it. In the published [1.0.13 transfer action](https://unpkg.com/@elizaos/plugin-evm@1.0.13/dist/index.js.map), the only call that moves value is this one:

```
// @elizaos/plugin-evm 1.0.13, src/actions/transfer.ts (from the npm source map)
const hash = await walletClient.sendTransaction({
  account: walletClient.account,
  to: params.toAddress,
  value: parseEther(params.amount),
  data: params.data as Hex,
  chain: walletClient.chain,
});
```

There's no branch on params.token, no ERC-20 ABI, and no lookup of the USDC contract. viem's [parseEther](https://viem.sh/docs/utilities/parseEther) converts the amount to wei with 18 decimals, and the transaction goes straight to the recipient as a native transfer. Stablecoin Insider searched the full 1.0.13 bundle for the ERC-20 transfer selector a9059cbb and the Base USDC address: zero hits for either.

The alpha line doesn't fix it. The [2.0.0-alpha.8 bundle](https://unpkg.com/@elizaos/plugin-evm@2.0.0-alpha.8/dist/index.js) renames the action to TRANSFER and adds typed errors and a parameter parser, but its transfer method still calls sendTransaction with value set to parseEther(params.amount).

## Stablecoin Insider's test: what the plugin actually signs

Stablecoin Insider installed @elizaos/plugin-evm 1.0.13 from npm, built the plugin's own WalletProvider with a throwaway key on Base (chain ID 8453), and pointed it at a local JSON-RPC mock. The plugin's TransferAction then signed real EIP-1559 transactions, which were captured at eth\_sendRawTransaction and decoded with viem. Nothing reached a live network.

| Input the model extracted                            | Signed tx: to               | Signed tx: value                        | Calldata                      | Gas limit                                  |
| ---------------------------------------------------- | --------------------------- | --------------------------------------- | ----------------------------- | ------------------------------------------ |
| amount 25, token USDC                                | Recipient wallet            | 25 ETH (25,000,000,000,000,000,000 wei) | empty                         | 21000                                      |
| amount 0.5, token = Base USDC contract address       | Recipient wallet            | 0.5 ETH                                 | empty                         | 21000                                      |
| amount 100, recipient vitalik.eth (the docs example) | n/a                         | n/a                                     | n/a                           | Rejected: Address "vitalik.eth" is invalid |
| Correct USDC transfer for comparison                 | USDC contract 0x833589fC... | 0                                       | transfer(recipient, 25000000) | set by estimate                            |

Source: Stablecoin Insider test of [@elizaos/plugin-evm 1.0.13](https://unpkg.com/browse/@elizaos/plugin-evm@1.0.13/) on 8 October 2026; USDC contract and 6 decimals per [Circle USDC contract addresses](https://developers.circle.com/stablecoins/usdc-contract-addresses).

A gas limit of 21000 is the giveaway. That's the fixed cost of a plain ETH transfer, and a USDC transfer always costs more because it runs the token contract's code, as defined in the [ERC-20 standard](https://eips.ethereum.org/EIPS/eip-20).

⚠️

Warning: if the agent wallet holds enough ETH, a USDC request becomes a real ETH payment. A wallet with 1 ETH asked to "send 0.5 USDC" sends 0.5 ETH and reports success. With less ETH than the amount, the RPC rejects it for insufficient funds and nothing moves. Neither outcome is a USDC transfer.

The success message makes it worse. The callback text reads "Successfully transferred 25 tokens", never naming the asset, so a chat log won't tell you ETH left instead of USDC. The only reliable check is the transaction itself on [Basescan](https://basescan.org/token/0x833589fcd6edb6e08f4c7c32d4f71b54bda02913), where a real USDC payment shows up as a Transfer event on the token contract.

## Docs vs code: where the ElizaOS EVM plugin story splits

The confusion isn't your fault. Three official sources describe three different transfer actions.

| Source                                                                                                                | What it says about transfers                                                                         | Matches npm 1.0.13?             |
| --------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | ------------------------------- |
| [Docs overview](https://docs.elizaos.ai/plugin-registry/defi/evm)                                                     | "Transfer native tokens and ERC20 tokens"; example prompt "Transfer 100 USDC to vitalik.eth on Base" | No                              |
| [Docs developer guide](https://docs.elizaos.ai/plugin-registry/defi/evm/complete-documentation)                       | Shows a resolveTokenAddress helper with USDC addresses for chain IDs 1 and 8453                      | No (helper isn't in the bundle) |
| [Docs examples](https://docs.elizaos.ai/plugin-registry/defi/evm/examples)                                            | Agent replies "Transferring 100 USDC to vitalik.eth on Base network"                                 | No (ENS names are rejected)     |
| [npm README](https://www.npmjs.com/package/@elizaos/plugin-evm)                                                       | "Native token transfers"; "TransferAction: Native token transfers"                                   | Yes                             |
| [Rust crate elizaos-plugin-evm 2.0.0](https://docs.rs/crate/elizaos-plugin-evm/latest/source/src/actions/transfer.rs) | Has execute\_erc20 with real transfer calldata                                                       | Different package               |

Source: pages linked in each row, read 8 October 2026; bundle checked at [unpkg](https://unpkg.com/@elizaos/plugin-evm@1.0.13/dist/index.js).

The README is the honest one. It says native transfers, and that's what ships.

The Rust crate is a separate trap. Its execute\_erc20 builds correct transfer calldata, but it parses the amount with DEFAULT\_DECIMALS, which [constants.rs sets to 18](https://docs.rs/crate/elizaos-plugin-evm/latest/source/src/constants.rs). USDC uses 6, so 100 USDC would be encoded as 100 x 10^18 base units, a trillion times too large. On any normal wallet that reverts. Pass base units yourself if you use the Rust path.

## How to send USDC with the ElizaOS EVM plugin safely

You don't need to fork the plugin. Keep its wallet provider, swap, and bridge, drop the built-in transfer, and register one narrow action that can only move USDC to addresses you approve.

### Step 1: Install the plugin and set the wallet

Add the plugin with the CLI command from the [ElizaOS EVM plugin docs](https://docs.elizaos.ai/plugin-registry/defi/evm), then set the key and an RPC for each chain. The plugin reads ETHEREUM\_PROVIDER\_<CHAIN> first and EVM\_PROVIDER\_<CHAIN> second.

```
elizaos plugins add evm

# .env
EVM_PRIVATE_KEY=0x...            # a dedicated agent key, never a treasury key
ETHEREUM_PROVIDER_BASE=https://your-base-rpc
ETHEREUM_PROVIDER_BASESEPOLIA=https://sepolia.base.org
```

Name the chains explicitly in the character's settings so you don't inherit the mainnet plus Base default. Use viem's chain keys, such as base and baseSepolia. Public endpoints are listed in [Base's connection guide](https://docs.base.org/base-chain/quickstart/connecting-to-base), but production agents should use a dedicated RPC.

```
// character.ts
settings: {
  chains: { evm: ['baseSepolia'] },   // switch to ['base'] only after Step 5 passes
},
```

Key custody is the real decision here. A raw EVM\_PRIVATE\_KEY is fine on a testnet. On mainnet, put a policy-enforcing signer behind it; Stablecoin Insider compares the options in [how to set up an AI agent wallet](https://stablecoininsider.org/how-to-set-up-an-ai-agent-wallet/), with deeper walkthroughs for [Turnkey](https://stablecoininsider.org/how-to-send-turnkey-agentic-usdc-payments/), [Privy](https://stablecoininsider.org/how-to-provision-privy-agent-wallets-for-usdc/), and [Coinbase agentic wallets](https://stablecoininsider.org/how-to-create-a-coinbase-agentic-wallet-for-usdc/).

### Step 2: Remove the built-in transfer action

A plugin is a plain object, so you can register a copy without the action you don't trust. In 1.0.13 the action's name is EVM\_TRANSFER\_TOKENS; in the 2.0 alphas it's TRANSFER.

```
import evmPlugin from '@elizaos/plugin-evm';

const UNSAFE = new Set(['EVM_TRANSFER_TOKENS', 'TRANSFER']);

export const evmNoTransfer = {
  ...evmPlugin,
  name: 'evm-no-transfer',
  actions: (evmPlugin.actions ?? []).filter((a) => !UNSAFE.has(a.name)),
};
```

Do this even if you never plan to send ETH. The action's similes include EVM\_SEND\_TOKENS and EVM\_TOKEN\_TRANSFER, so a model can reach it from almost any "send" phrasing.

### Step 3: Add a guarded SEND\_USDC action

The core of the fix is a function that only knows one token per chain, converts amounts with [parseUnits](https://viem.sh/docs/utilities/parseUnits) at 6 decimals from a string (never a float), and runs [simulateContract](https://viem.sh/docs/contract/simulateContract) before signing. USDC addresses come from [Circle's official list](https://developers.circle.com/stablecoins/usdc-contract-addresses).

```
import { getAddress, isAddress, parseAbi, parseUnits } from 'viem';

const USDC = {
  base: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
  baseSepolia: '0x036CbD53842c5426634e7929541eC2318f3dCF7e',
} as const;
const ALLOW = new Set([/* approved payee addresses */].map((a) => getAddress(a)));
const MAX_PER_TX = parseUnits('250', 6);
const MAX_PER_DAY = parseUnits('1000', 6);
const ERC20 = parseAbi(['function transfer(address to, uint256 amount) returns (bool)']);

export async function sendUsdc({ chain, to, amount }, wp, spentToday: bigint) {
  const token = USDC[chain];
  if (!token) throw new Error(`USDC not configured on ${chain}`);
  if (!isAddress(to)) throw new Error('Recipient must be a 0x address');
  const recipient = getAddress(to);
  if (!ALLOW.has(recipient)) throw new Error('Recipient not on allowlist');
  if (!/^\d+(\.\d{1,6})?$/.test(String(amount))) throw new Error('Bad amount format');
  const units = parseUnits(String(amount), 6);
  if (units <= 0n || units > MAX_PER_TX) throw new Error('Per-transfer cap exceeded');
  if (spentToday + units > MAX_PER_DAY) throw new Error('Daily cap exceeded');

  const wallet = wp.getWalletClient(chain);
  const pub = wp.getPublicClient(chain);
  const { request } = await pub.simulateContract({
    account: wallet.account, address: token, abi: ERC20,
    functionName: 'transfer', args: [recipient, units],
  });
  const hash = await wallet.writeContract(request);
  const receipt = await pub.waitForTransactionReceipt({ hash });
  if (receipt.status !== 'success') throw new Error(`Reverted: ${hash}`);
  return { hash, units };
}
```

Then wrap it in an ElizaOS action. Keep the model's job small: extract chain, recipient, and amount as strings, and let code decide everything else. Persist spentToday in your database or the runtime cache, not in memory, so a restart doesn't reset the daily cap.

```
import { initWalletProvider } from '@elizaos/plugin-evm';

export const sendUsdcAction = {
  name: 'SEND_USDC',
  similes: ['PAY_USDC', 'SEND_STABLECOIN'],
  description: 'Send USDC on Base to an approved address. Never sends ETH.',
  validate: async (runtime) => !!runtime.getSetting('EVM_PRIVATE_KEY'),
  handler: async (runtime, message, state, _opts, callback) => {
    const p = await extractUsdcParams(runtime, message, state); // your XML template
    const wp = await initWalletProvider(runtime);
    const spent = await loadSpentToday(runtime);
    const { hash, units } = await sendUsdc(p, wp, spent);
    await saveSpentToday(runtime, spent + units);
    callback?.({ text: `Sent ${p.amount} USDC on ${p.chain}. Tx: ${hash}` });
    return true;
  },
  examples: [],
};

// character plugins: [evmNoTransfer, { name: 'usdc-pay', actions: [sendUsdcAction] }]
```

### Step 4: Fund a Base Sepolia wallet and run the test cases

Base Sepolia USDC is 0x036CbD53842c5426634e7929541eC2318f3dCF7e, per [Circle's address list](https://developers.circle.com/stablecoins/usdc-contract-addresses), and [Circle's faucet](https://faucet.circle.com/) hands out test USDC. You'll also need a little Sepolia ETH for gas. Then run the cases below before the agent sees a mainnet key.

Stablecoin Insider ran the same guard logic against the mock RPC. Every accepted payment signed a call to the USDC contract with zero ETH value; every bad input was refused before signing.

| Test input                       | Result                                                           | What it proves               |
| -------------------------------- | ---------------------------------------------------------------- | ---------------------------- |
| 25 USDC to allowlisted address   | Signed: to USDC contract, value 0, transfer(recipient, 25000000) | Correct asset and base units |
| 2.01 USDC to allowlisted address | Signed: 2010000 base units exactly                               | No float rounding            |
| 300 USDC                         | Refused: per-transfer cap                                        | Cap holds above 250          |
| 5 USDC to an unknown address     | Refused: not on allowlist                                        | Model can't pick payees      |
| 5 USDC to vitalik.eth            | Refused: must be a 0x address                                    | No ENS guessing              |
| 1.0000001 USDC                   | Refused: more than 6 decimals                                    | No silent truncation         |

Source: Stablecoin Insider guard test, 8 October 2026, using viem [simulateContract](https://viem.sh/docs/contract/simulateContract) and [parseUnits](https://viem.sh/docs/utilities/parseUnits).

Add your own cases for the daily cap and for a retried tool call. The fuller checklist is in [how to test an AI agent USDC payment before going live](https://stablecoininsider.org/how-to-test-ai-agent-usdc-payment-before-going-live/), and [how to set spend limits for an AI agent USDC wallet](https://stablecoininsider.org/how-to-set-spend-limits-ai-agent-usdc-wallet/) covers where caps should live when the signer supports policies.

### Step 5: Switch to Base mainnet and reconcile

Change the chain list to base, fund the agent with a small working balance, and watch the first payments on [Basescan's USDC token page](https://basescan.org/token/0x833589fcd6edb6e08f4c7c32d4f71b54bda02913). Base USDC had 4,384,144,858 tokens in supply at block 52,325,403, read by Stablecoin Insider on 8 October 2026, so liquidity isn't the constraint. Your controls are.

Log the hash, amount, payee, and originating message for every send. That's what lets you match on-chain transfers back to agent decisions later, as covered in [how to reconcile AI agent USDC spend](https://stablecoininsider.org/how-to-reconcile-ai-agent-usdc-spend/). Keep a kill switch ready too; [how to revoke an AI agent's access to USDC](https://stablecoininsider.org/how-to-revoke-ai-agent-usdc-access/) walks through it.

## What about the ElizaOS Solana plugin?

The Solana side does handle tokens, but with sharp edges. In [@elizaos/plugin-solana 1.2.6](https://unpkg.com/@elizaos/plugin-solana@1.2.6/dist/index.js), TRANSFER\_SOLANA sends an SPL transfer when the model returns a mint address, and plain SOL when tokenAddress is null. The prompt tells the model to use null both for SOL and for anything it can't determine, so a request that names USDC without a mint can fall through to a SOL transfer.

The amount math is the same pattern Stablecoin Insider flagged in [Solana Agent Kit](https://stablecoininsider.org/how-to-send-usdc-with-solana-agent-kit/): BigInt(Number(amount) \* 10 \*\* decimals). Running every cent value from 0.01 to 1,000.00 (100,000 amounts) through that exact expression, 2,384 or 2.38% produced non-integers such as 2,009,999.9999999998 for 2.01 USDC, which makes BigInt throw a [RangeError](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Cant%5Fconvert%5Fx%5Fto%5FBigInt) before signing.

- Decimals fallback: if the mint lookup returns nothing, the code assumes 9 decimals, which is 1,000 times too large for 6-decimal USDC (see [plugin source](https://unpkg.com/@elizaos/plugin-solana@1.2.6/dist/index.js)).
- Token-2022: the transfer uses the classic SPL Token program, so Token-2022 stablecoins like PYUSD fail; the program is documented in the [Token-2022 repository](https://github.com/solana-program/token-2022).
- Recipient accounts: the agent pays to create the recipient's token account if it doesn't exist, which an attacker can turn into a small fee drain.

## Swap and bridge get decimals right

Not everything in the EVM plugin is broken. The swap and bridge actions read decimals() from the token contract and convert with parseUnits before quoting through LI.FI or Bebop, per the [1.0.13 source](https://unpkg.com/@elizaos/plugin-evm@1.0.13/dist/index.js.map). A code comment in the bridge action even calls this "THE KEY FIX" over a hardcoded 18\. The bridge falls back to 18 decimals if the on-chain read fails, so give it a reliable RPC.

That makes a practical split. Let the plugin swap ETH to USDC or bridge USDC between chains with route checks, and let your own action handle the final payment. If you're choosing routes, Stablecoin Insider ranks options in [the best DEX aggregators for stablecoin swaps](https://stablecoininsider.org/best-dex-aggregators-for-stablecoin-swaps-in-2026/).

## Built-in transfer vs guarded SEND\_USDC

| Attribute         | EVM\_TRANSFER\_TOKENS (npm 1.0.13)   | Guarded SEND\_USDC                          |
| ----------------- | ------------------------------------ | ------------------------------------------- |
| Asset sent        | Always the chain's native coin       | USDC only, address pinned per chain         |
| Amount conversion | parseEther (18 decimals)             | parseUnits at 6 decimals from a string      |
| Recipient check   | Any address string the model returns | Checksummed allowlist in code               |
| Limits            | None                                 | Per-transfer and daily caps                 |
| Pre-flight        | None                                 | simulateContract, then receipt status check |
| Success message   | Generic "tokens"                     | Names USDC, chain, and hash                 |

Source: [plugin-evm 1.0.13 source map](https://unpkg.com/@elizaos/plugin-evm@1.0.13/dist/index.js.map) and the Stablecoin Insider test above.

## Where this setup still breaks, and who it isn't for

- Prompt injection. An allowlist stops new payees, but not a bad amount to a good payee; the caps are your ceiling.
- Raw keys. EVM\_PRIVATE\_KEY in an env file is one leaked log away from a drained wallet.
- No idempotency. A retried handler can pay twice, so key each payment and check before resending.
- Alpha drift. 2.0.0 alphas change action names and specs, so re-check the filter in Step 2 on every upgrade.
- High-volume payouts. If you're paying hundreds of vendors a day, use a payout API, not a chat agent.

If your agent mostly pays for APIs, the [x402 protocol](https://stablecoininsider.org/x402-protocol/) is a cleaner fit than a free-form transfer action, and [paying for an x402 API with Coinbase AgentKit](https://stablecoininsider.org/how-to-pay-for-an-x402-api-with-coinbase-agentkit/) shows that pattern end to end.

💡

Stablecoin Insider's take: the ElizaOS EVM plugin is a good agent wallet scaffold and a poor stablecoin payment tool. Its transfer action does what the npm README says, native transfers only, while the docs promise USDC, and the gap converts a stablecoin request into an ETH payment. That isn't a subtle edge case; it's the headline use case. Until a stable release ships an ERC-20 path with real decimals, remove the built-in transfer, keep swap and bridge, and route every USDC payment through a narrow action with an allowlist and caps.

---

Running an ElizaOS agent with a funded EVM wallet today? Check which transfer action it has loaded, run the 25 USDC case on Base Sepolia, and read the signed transaction before you trust the chat reply.

[Check the plugin-evm 1.0.13 source](https://unpkg.com/browse/@elizaos/plugin-evm@1.0.13/)

Need a signer that enforces spend policy outside the agent? See how Turnkey handles agentic USDC payments.

[Send Turnkey Agentic USDC Payments ](https://stablecoininsider.org/how-to-send-turnkey-agentic-usdc-payments/) 

[How to Send USDC with Solana Agent Kit (2026)Stablecoin Insider's tested Solana Agent Kit USDC setup, the float-math bug, Token-2022 limits, and a guarded PAY\_USDC action.![](https://stablecoininsider.org/favicon.ico)Stablecoin Insider](https://stablecoininsider.org/how-to-send-usdc-with-solana-agent-kit/)

Mapping the wider agent payments stack? Start with [stablecoin payments for AI agents](https://stablecoininsider.org/stablecoin-payments-for-ai-agents/) and [AI agents for stablecoins in 2026](https://stablecoininsider.org/ai-agents-for-stablecoins-in-2026/). Funding comes first, so see [how to fund an AI agent wallet with USDC](https://stablecoininsider.org/how-to-fund-ai-agent-wallet-with-usdc/).

## FAQ

#### What is the ElizaOS EVM plugin?

It's the official ElizaOS plugin that gives an agent an EVM wallet and actions to transfer, swap, and bridge tokens. It's published on npm as @elizaos/plugin-evm, with 1.0.13 as the current stable release.

#### Can ElizaOS send USDC?

Not with the built-in EVM transfer action in 1.0.13 or 2.0.0-alpha.8, which sends the native coin instead. Add a custom action that calls the USDC contract's transfer function with 6-decimal amounts.

#### Why did my ElizaOS agent send ETH instead of USDC?

The transfer action ignores the token field and calls sendTransaction with value set to parseEther(amount). The amount you asked for in USDC is sent as the same number of ETH if the wallet can cover it.

#### Does the ElizaOS EVM plugin support ENS names?

Not in the transfer action. In Stablecoin Insider's test, the docs example recipient vitalik.eth was rejected as an invalid address, so resolve names yourself and pass a 0x address.

#### How do you stop an ElizaOS agent from overspending USDC?

Remove the built-in transfer action and route payments through one action with a payee allowlist plus per-transfer and daily caps in code. A policy-enforcing signer adds a second layer outside the agent.

#### Can the ElizaOS EVM plugin swap ETH for USDC?

Yes. The swap action reads token decimals on-chain and quotes through LI.FI and Bebop, so USDC amounts are converted correctly. It's the transfer path that's broken, not swaps.

#### Does the ElizaOS Solana plugin send USDC correctly?

Usually, if the model passes the USDC mint address. It uses float math that throws on 2.38% of cent amounts in Stablecoin Insider's test, falls back to SOL when the mint is null, and doesn't support Token-2022 stablecoins.

---

This content is provided for informational and educational purposes only and does not constitute financial, investment, legal, or tax advice; no material herein should be interpreted as a recommendation, endorsement, or solicitation to buy or sell.