|
| 1 | +# Actions SDK Integration Guide |
| 2 | + |
| 3 | +This file is an integration playbook for AI coding agents helping developers add Actions SDK to their application. Actions SDK is an open source TypeScript toolkit by Optimism for integrating DeFi (lending, borrowing, swaps, payments) into apps. |
| 4 | + |
| 5 | +> **For agents**: This guide uses concrete examples but the SDK evolves. When you encounter references to specific providers, assets, or chains, always verify against the actual codebase to discover what is currently supported. Source-of-truth locations are called out in each section. |
| 6 | +
|
| 7 | +## Overview |
| 8 | + |
| 9 | +Actions SDK follows a provider pattern. Developers configure: |
| 10 | + |
| 11 | +1. **A wallet provider** — an embedded wallet (Privy, Turnkey, Dynamic, etc.) that handles key management and signing |
| 12 | +2. **DeFi action providers** — protocols for lending (Morpho, Aave), borrowing, swaps, etc. |
| 13 | +3. **Chains** — which EVM networks to support |
| 14 | +4. **Assets** — which tokens to allow or block |
| 15 | + |
| 16 | +Then they call `createActions(config)` to get an `actions` instance, convert a user's wallet into an Actions wallet, and call DeFi operations on it. |
| 17 | + |
| 18 | +## Step 1: Detect Project Environment |
| 19 | + |
| 20 | +Before starting, determine the developer's environment and existing integrations. |
| 21 | + |
| 22 | +**Check the environment** by reading the project's `package.json`: |
| 23 | + |
| 24 | +- If `react`, `next`, `remix`, or similar React framework is in dependencies → **React/frontend** environment. Use `@eth-optimism/actions-sdk/react` imports. |
| 25 | +- If it's a Node.js backend (hono, express, fastify, etc.) → **Node/backend** environment. Use `@eth-optimism/actions-sdk/node` imports (or the default import which resolves to node). |
| 26 | +- The available wallet providers differ by environment. Check the SDK's `peerDependencies` in `node_modules/@eth-optimism/actions-sdk/package.json` to see which provider packages exist for each environment. |
| 27 | + |
| 28 | +**Check for existing wallet provider integrations** by scanning the project's `package.json` dependencies for: |
| 29 | + |
| 30 | +- `@privy-io/react-auth` or `@privy-io/node` → Privy is already integrated |
| 31 | +- `@turnkey/*` packages → Turnkey is already integrated |
| 32 | +- `@dynamic-labs/*` packages → Dynamic is already integrated (React only) |
| 33 | + |
| 34 | +If a wallet provider is already present, use it. If not, see Step 3. |
| 35 | + |
| 36 | +## Step 2: Install the SDK |
| 37 | + |
| 38 | +```bash |
| 39 | +npm install @eth-optimism/actions-sdk |
| 40 | +``` |
| 41 | + |
| 42 | +The SDK has peer dependencies for whichever wallet provider the developer chooses. These are optional — only the selected provider's packages need to be installed. |
| 43 | + |
| 44 | +## Step 3: Choose a Wallet Provider |
| 45 | + |
| 46 | +The wallet provider is the biggest integration decision because it involves third-party account setup, authentication flows, and potentially significant frontend/backend work. |
| 47 | + |
| 48 | +**If the developer already has a wallet provider** (detected in Step 1): skip this step and use their existing integration. |
| 49 | + |
| 50 | +**If no wallet provider exists**: Present the available options and help the developer choose, but do not block the rest of the setup. The wallet provider can be integrated in parallel. |
| 51 | + |
| 52 | +> **For agents with interactive UX** (e.g. choice selection): present the wallet provider options as a choice. Otherwise, list them and ask which the developer prefers. |
| 53 | +
|
| 54 | +Available wallet providers (verify the current list by checking the SDK's `peerDependencies`): |
| 55 | + |
| 56 | +| Provider | Frontend (React) | Backend (Node) | Setup Guide | |
| 57 | +| -------- | :--------------: | :------------: | ---------------------------- | |
| 58 | +| Privy | Yes | Yes | https://docs.privy.io | |
| 59 | +| Turnkey | Yes | Yes | https://docs.turnkey.com | |
| 60 | +| Dynamic | Yes | No | https://www.dynamic.xyz/docs | |
| 61 | + |
| 62 | +Each provider requires its own account setup, API keys, and SDK installation. Direct the developer to the provider's documentation for initial setup, then return here to wire it into Actions. |
| 63 | + |
| 64 | +**Important**: The developer does not need to finish wallet provider setup before continuing. You can scaffold the full Actions config with a placeholder and fill in the wallet section once the provider is ready. |
| 65 | + |
| 66 | +## Step 4: Create the Actions Config File |
| 67 | + |
| 68 | +Create a config file (e.g. `actions.ts`) that centralizes the Actions SDK configuration. This is the main integration point. |
| 69 | + |
| 70 | +### 4a: Wallet Config |
| 71 | + |
| 72 | +The wallet config declares which hosted wallet provider to use and enables smart wallets. Here's an example using Privy on a React frontend: |
| 73 | + |
| 74 | +```typescript |
| 75 | +const walletConfig = { |
| 76 | + hostedWalletConfig: { |
| 77 | + provider: { |
| 78 | + type: 'privy' as const, |
| 79 | + }, |
| 80 | + }, |
| 81 | + smartWalletConfig: { |
| 82 | + provider: { |
| 83 | + type: 'default' as const, |
| 84 | + }, |
| 85 | + }, |
| 86 | +} |
| 87 | +``` |
| 88 | + |
| 89 | +Each provider has different config requirements depending on the environment (React vs Node). For the full setup code for each provider, see the [Integrating Wallets](https://docs.optimism.io/app-developers/reference/actions/integrating-wallets) guide. |
| 90 | + |
| 91 | +> **Source of truth for provider config shapes**: Check the SDK source at `src/wallet/node/providers/hosted/types/` (Node) or `src/wallet/react/providers/hosted/types/` (React) for the exact config each provider expects. |
| 92 | +
|
| 93 | +### 4b: DeFi Provider Config (Lend, Borrow, Swap, etc.) |
| 94 | + |
| 95 | +The SDK uses a modular provider pattern for DeFi actions. Each action type (lend, borrow, swap) can have one or more protocol providers. **Configure DeFi providers first** — the market selections will inform which chains and assets need to be configured in the following steps. |
| 96 | + |
| 97 | +> **For agents**: Ask the developer which DeFi actions and providers they want to support. Check `src/lend/` for lending providers and look for other action directories (e.g. `src/borrow/`, `src/swap/`) as the SDK evolves. The `ActionsConfig` type in `src/types/actions.ts` shows all configurable action categories. |
| 98 | +
|
| 99 | +#### Morpho |
| 100 | + |
| 101 | +Morpho uses individual vault markets. Ask the developer which Morpho vaults they want to support. The developer may: |
| 102 | + |
| 103 | +- **Provide a vault URL** (e.g. `https://app.morpho.org/unichain/vault/0x38f4...`). Parse the chain name and vault address from the URL to construct the `LendMarketConfig`. |
| 104 | +- **Want demo/testnet markets** for development — see `packages/demo/backend/src/config/markets.ts` for examples. |
| 105 | +- **Not know yet** — scaffold with an empty allowlist and a TODO comment. |
| 106 | + |
| 107 | +```typescript |
| 108 | +// Morpho market — address is the vault address from the Morpho app URL |
| 109 | +const MorphoUSDC: LendMarketConfig = { |
| 110 | + address: '0x...', // e.g. from https://app.morpho.org/{chain}/vault/{address} |
| 111 | + chainId: unichain.id, |
| 112 | + name: 'Gauntlet USDC', |
| 113 | + asset: USDC, |
| 114 | + lendProvider: 'morpho', |
| 115 | +} |
| 116 | +``` |
| 117 | + |
| 118 | +#### Aave |
| 119 | + |
| 120 | +Aave uses a singleton lending pool per chain — there is no per-vault address. Instead, each Aave "market" in the config represents an **underlying asset** you want to support lending for on Aave. The `address` field is the asset's token address on that chain. |
| 121 | + |
| 122 | +> **For agents**: Ask the developer which assets they want to lend via Aave and on which chains. Then construct the `LendMarketConfig` for each, using the asset's token address on that chain. Look up the address from the SDK's built-in asset constants in `src/constants/assets.ts`, or if the asset isn't built-in, look up the token address on the target chain. |
| 123 | +
|
| 124 | +```typescript |
| 125 | +// Aave market — address is the underlying token address on the target chain |
| 126 | +const AaveETH: LendMarketConfig = { |
| 127 | + address: '0x4200000000000000000000000000000000000006', // WETH on Optimism |
| 128 | + chainId: optimism.id, |
| 129 | + name: 'Aave ETH', |
| 130 | + asset: ETH, |
| 131 | + lendProvider: 'aave', |
| 132 | +} |
| 133 | +``` |
| 134 | + |
| 135 | +#### Bringing it together |
| 136 | + |
| 137 | +```typescript |
| 138 | +import type { LendConfig, LendMarketConfig } from '@eth-optimism/actions-sdk' |
| 139 | +import { ETH, USDC } from '@eth-optimism/actions-sdk' |
| 140 | +import { optimism, unichain } from 'viem/chains' |
| 141 | + |
| 142 | +const lendConfig: LendConfig = { |
| 143 | + morpho: { |
| 144 | + marketAllowlist: [MorphoUSDC], |
| 145 | + }, |
| 146 | + aave: { |
| 147 | + marketAllowlist: [AaveETH], |
| 148 | + }, |
| 149 | +} |
| 150 | +``` |
| 151 | + |
| 152 | +For more on configuring lend providers, see the [Configuring Actions](https://docs.optimism.io/app-developers/guides/configuring-actions) guide and the [Lend Reference](https://docs.optimism.io/app-developers/reference/actions/lend-documentation). |
| 153 | + |
| 154 | +> **Source of truth**: `src/lend/` for available lending providers. `packages/demo/backend/src/config/markets.ts` for working market examples. |
| 155 | +
|
| 156 | +### 4c: Chain Config |
| 157 | + |
| 158 | +> **For agents**: Start by including every chain already referenced by the markets configured in 4b. Then ask the developer if they want to support any additional chains. Check `src/constants/supportedChains.ts` in the SDK for the full list, and present the remaining chains as options. If your tool supports multi-select, use it here. |
| 159 | +
|
| 160 | +Each chain needs an RPC URL and optionally a bundler for smart wallet gas sponsorship. |
| 161 | + |
| 162 | +```typescript |
| 163 | +import { optimism, base, unichain } from 'viem/chains' |
| 164 | + |
| 165 | +const chains = [ |
| 166 | + { |
| 167 | + chainId: optimism.id, |
| 168 | + rpcUrls: [process.env.OPTIMISM_RPC_URL], |
| 169 | + bundler: { |
| 170 | + type: 'pimlico' as const, // or 'simple' |
| 171 | + url: process.env.OPTIMISM_BUNDLER_URL, |
| 172 | + }, |
| 173 | + }, |
| 174 | + { |
| 175 | + chainId: unichain.id, |
| 176 | + rpcUrls: [process.env.UNICHAIN_RPC_URL], |
| 177 | + bundler: { |
| 178 | + type: 'simple' as const, |
| 179 | + url: process.env.UNICHAIN_BUNDLER_URL, |
| 180 | + }, |
| 181 | + }, |
| 182 | +] |
| 183 | +``` |
| 184 | + |
| 185 | +### 4d: Asset Config |
| 186 | + |
| 187 | +> **For agents**: Start by including every asset already referenced by the markets configured in 4b. Then ask the developer if they want to support any additional tokens. Check `src/constants/assets.ts` for built-in asset constants (ETH, USDC, WETH, etc.) and present remaining options. If the developer needs a token not built into the SDK, help them define a custom `Asset` object — you'll need the token's contract address on each supported chain, decimals, symbol, and name. |
| 188 | +
|
| 189 | +```typescript |
| 190 | +import { USDC, ETH } from '@eth-optimism/actions-sdk' |
| 191 | + |
| 192 | +const assetsConfig = { |
| 193 | + allow: [USDC, ETH], // Only these assets are available in the app |
| 194 | +} |
| 195 | +``` |
| 196 | + |
| 197 | +Custom assets can be defined inline: |
| 198 | + |
| 199 | +```typescript |
| 200 | +import type { Asset } from '@eth-optimism/actions-sdk' |
| 201 | +import { unichain } from 'viem/chains' |
| 202 | + |
| 203 | +const CUSTOM_TOKEN: Asset = { |
| 204 | + address: { |
| 205 | + [unichain.id]: '0x...', |
| 206 | + }, |
| 207 | + metadata: { |
| 208 | + decimals: 18, |
| 209 | + name: 'Custom Token', |
| 210 | + symbol: 'CUSTOM', |
| 211 | + }, |
| 212 | + type: 'erc20', |
| 213 | +} |
| 214 | +``` |
| 215 | + |
| 216 | +> **Source of truth**: `src/constants/assets.ts` for built-in assets, `src/supported/tokens.ts` for the `SUPPORTED_TOKENS` list. |
| 217 | +
|
| 218 | +### 4e: Initialize Actions |
| 219 | + |
| 220 | +Bring everything together: |
| 221 | + |
| 222 | +```typescript |
| 223 | +// React frontend |
| 224 | +import { createActions } from '@eth-optimism/actions-sdk/react' |
| 225 | + |
| 226 | +export const actions = createActions({ |
| 227 | + wallet: walletConfig, |
| 228 | + chains, |
| 229 | + assets: assetsConfig, |
| 230 | + lend: lendConfig, |
| 231 | +}) |
| 232 | +``` |
| 233 | + |
| 234 | +```typescript |
| 235 | +// Node backend |
| 236 | +import { createActions } from '@eth-optimism/actions-sdk/node' |
| 237 | + |
| 238 | +export const actions = createActions({ |
| 239 | + wallet: walletConfig, |
| 240 | + chains, |
| 241 | + assets: assetsConfig, |
| 242 | + lend: lendConfig, |
| 243 | +}) |
| 244 | +``` |
| 245 | + |
| 246 | +In a backend, initialize once at startup and export a singleton. In React, memoize the instance to avoid recreating on every render. |
| 247 | + |
| 248 | +## Step 5: Use Actions |
| 249 | + |
| 250 | +### Convert a wallet to an Actions wallet |
| 251 | + |
| 252 | +Once the developer has a wallet from their provider, convert it to an Actions wallet. Each provider passes different arguments. Example with Privy on React: |
| 253 | + |
| 254 | +```typescript |
| 255 | +import { useWallets } from '@privy-io/react-auth' |
| 256 | + |
| 257 | +const { wallets } = useWallets() |
| 258 | +const embeddedWallet = wallets.find( |
| 259 | + (wallet) => wallet.walletClientType === 'privy', |
| 260 | +) |
| 261 | + |
| 262 | +const wallet = await actions.wallet.toActionsWallet({ |
| 263 | + connectedWallet: embeddedWallet, |
| 264 | +}) |
| 265 | +``` |
| 266 | + |
| 267 | +For all provider-specific `toActionsWallet` code, see the [Quickstart](https://docs.optimism.io/app-developers/quickstarts/actions#choose-a-wallet-provider) guide. |
| 268 | + |
| 269 | +### Create a smart wallet (optional) |
| 270 | + |
| 271 | +Smart wallets add ERC-4337 features (gas sponsorship, batch transactions). Create a signer from the provider wallet, then create a smart wallet. Example with Privy on React: |
| 272 | + |
| 273 | +```typescript |
| 274 | +const signer = await actions.wallet.createSigner({ |
| 275 | + connectedWallet: embeddedWallet, |
| 276 | +}) |
| 277 | + |
| 278 | +const { wallet } = await actions.wallet.createSmartWallet({ signer }) |
| 279 | +``` |
| 280 | + |
| 281 | +For all provider-specific `createSigner` parameters, see the [Integrating Wallets](https://docs.optimism.io/app-developers/reference/actions/integrating-wallets) reference. |
| 282 | + |
| 283 | +### Perform DeFi actions |
| 284 | + |
| 285 | +For the full list of wallet methods and lend operations, see the [Wallet Reference](https://docs.optimism.io/app-developers/reference/actions/wallet-definitions) and [Lend Reference](https://docs.optimism.io/app-developers/reference/actions/lend-documentation). |
| 286 | + |
| 287 | +```typescript |
| 288 | +// Get token balances across all configured chains |
| 289 | +const balances = await wallet.getBalance() |
| 290 | + |
| 291 | +// Open a lending position |
| 292 | +const receipt = await wallet.lend.openPosition({ |
| 293 | + amount: 100, |
| 294 | + asset: USDC, |
| 295 | + ...market, // { address, chainId } of the market — a LendMarketId |
| 296 | +}) |
| 297 | + |
| 298 | +// Get current position |
| 299 | +const position = await wallet.lend.getPosition(market) |
| 300 | + |
| 301 | +// Close a position (withdraw) |
| 302 | +const closeReceipt = await wallet.lend.closePosition({ |
| 303 | + amount: 50, |
| 304 | + asset: USDC, |
| 305 | + ...market, |
| 306 | +}) |
| 307 | + |
| 308 | +// Browse available markets |
| 309 | +const markets = await actions.lend.getMarkets() |
| 310 | +``` |
| 311 | + |
| 312 | +> **Source of truth for available actions**: The wallet instance exposes namespaces for each action type. Check which namespaces exist on the `Wallet` and `SmartWallet` classes in `src/wallet/core/wallets/`. The `actions` instance also exposes top-level namespaces for read-only operations (e.g. `actions.lend.getMarkets()`). |
| 313 | +
|
| 314 | +## Reference |
| 315 | + |
| 316 | +### Key types |
| 317 | + |
| 318 | +```typescript |
| 319 | +import type { |
| 320 | + ActionsConfig, // Top-level config |
| 321 | + Asset, // Token definition (address per chain + metadata) |
| 322 | + TokenBalance, // Balance result (total + per-chain breakdown) |
| 323 | + LendConfig, // Lend provider configuration |
| 324 | + LendMarketConfig, // Market definition |
| 325 | + LendMarketId, // Market identifier ({ address, chainId }) |
| 326 | + LendMarket, // Market data (name, asset, supply, APY) |
| 327 | + LendMarketPosition, // User position in a market |
| 328 | + LendTransaction, // Transaction result from lend operations |
| 329 | + WalletConfig, // Wallet provider configuration |
| 330 | + SupportedChainId, // Union of supported chain IDs |
| 331 | +} from '@eth-optimism/actions-sdk' |
| 332 | +``` |
| 333 | + |
| 334 | +### Key imports |
| 335 | + |
| 336 | +```typescript |
| 337 | +// Assets |
| 338 | +import { ETH, USDC, WETH } from '@eth-optimism/actions-sdk' |
| 339 | + |
| 340 | +// Utilities |
| 341 | +import { |
| 342 | + getTokenAddress, |
| 343 | + getTokenBySymbol, |
| 344 | + SUPPORTED_TOKENS, |
| 345 | +} from '@eth-optimism/actions-sdk' |
| 346 | + |
| 347 | +// Wallet classes (for type checking) |
| 348 | +import { Wallet, SmartWallet } from '@eth-optimism/actions-sdk' |
| 349 | + |
| 350 | +// Factory (environment-specific) |
| 351 | +import { createActions } from '@eth-optimism/actions-sdk/react' // or /node |
| 352 | +``` |
| 353 | +
|
| 354 | +### Documentation |
| 355 | +
|
| 356 | +- Quickstart: https://docs.optimism.io/app-developers/quickstarts/actions |
| 357 | +- Configuring Actions: https://docs.optimism.io/app-developers/guides/configuring-actions |
| 358 | +- Integrating Wallets: https://docs.optimism.io/app-developers/reference/actions/integrating-wallets |
| 359 | +- Wallet Reference: https://docs.optimism.io/app-developers/reference/actions/wallet-definitions |
| 360 | +- Lend Reference: https://docs.optimism.io/app-developers/reference/actions/lend-documentation |
| 361 | +
|
| 362 | +### Example code |
| 363 | +
|
| 364 | +The demo applications in this repository show complete working integrations: |
| 365 | +
|
| 366 | +- `packages/demo/backend/` — Node.js backend with Privy, Morpho, and Aave |
| 367 | +- `packages/demo/frontend/` — React frontend with Privy, Turnkey, and Dynamic |
0 commit comments