-
Notifications
You must be signed in to change notification settings - Fork 580
feat(agentkit): Add Uniswap V3 Action Provider for Base Network #771
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| import { uniswapActionProvider } from "../uniswapActionProvider"; | ||
|
|
||
| describe("UniswapActionProvider", () => { | ||
| const actionProvider = uniswapActionProvider(); | ||
|
|
||
| describe("supportsNetwork", () => { | ||
| it("should return true for base-mainnet", () => { | ||
| expect(actionProvider.supportsNetwork({ networkId: "base-mainnet", protocolFamily: "evm" })).toBe(true); | ||
|
Check failure on line 8 in typescript/agentkit/src/action-providers/uniswap/__tests__/uniswapActionProvider.test.ts
|
||
| }); | ||
|
|
||
| it("should return true for base-sepolia", () => { | ||
| expect(actionProvider.supportsNetwork({ networkId: "base-sepolia", protocolFamily: "evm" })).toBe(true); | ||
|
Check failure on line 12 in typescript/agentkit/src/action-providers/uniswap/__tests__/uniswapActionProvider.test.ts
|
||
| }); | ||
|
|
||
| it("should return false for ethereum-mainnet", () => { | ||
| expect(actionProvider.supportsNetwork({ networkId: "ethereum-mainnet", protocolFamily: "evm" })).toBe(false); | ||
|
Check failure on line 16 in typescript/agentkit/src/action-providers/uniswap/__tests__/uniswapActionProvider.test.ts
|
||
| }); | ||
|
|
||
| it("should return false for solana", () => { | ||
| expect(actionProvider.supportsNetwork({ protocolFamily: "solana", networkId: "solana-mainnet" })).toBe(false); | ||
|
Check failure on line 20 in typescript/agentkit/src/action-providers/uniswap/__tests__/uniswapActionProvider.test.ts
|
||
| }); | ||
| }); | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,116 @@ | ||
| import type { Abi } from "abitype"; | ||
|
|
||
| /** | ||
| * Uniswap V3 contract addresses for Base networks | ||
| */ | ||
| export const UNISWAP_ADDRESSES: Record<string, Record<string, string>> = { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Optional: could be extended to a few more chains like ethereum, optimism, arbitrum, polygon (https://github.com/coinbase/agentkit/blob/main/typescript/agentkit/src/network/network.ts) You can find the contract addresses here: |
||
| "base-sepolia": { | ||
| SwapRouter02: "0x94cC0AaC535CCDB3C01d6787D6413C739ae12bc4", | ||
| Quoter: "0xC5290058841028F1614F3A6F0F5816cAd0df5E27", | ||
| WETH: "0x4200000000000000000000000000000000000006", | ||
| }, | ||
| "base-mainnet": { | ||
| SwapRouter02: "0x2626664c2603336E57B271c5C0b26F421741e481", | ||
| Quoter: "0x3d4e44Eb1374240CE5F1B871ab261CD16335B76a", | ||
| WETH: "0x4200000000000000000000000000000000000006", | ||
| }, | ||
| }; | ||
|
|
||
| /** | ||
| * Uniswap V3 Quoter contract ABI (subset for quoting) | ||
| */ | ||
| export const UNISWAP_QUOTER_ABI: Abi = [ | ||
| { | ||
| inputs: [ | ||
| { | ||
| components: [ | ||
| { internalType: "address", name: "tokenIn", type: "address" }, | ||
| { internalType: "address", name: "tokenOut", type: "address" }, | ||
| { internalType: "uint256", name: "amountIn", type: "uint256" }, | ||
| { internalType: "uint24", name: "fee", type: "uint24" }, | ||
| { internalType: "uint160", name: "sqrtPriceLimitX96", type: "uint160" }, | ||
| ], | ||
| internalType: "struct IQuoterV2.QuoteExactInputSingleParams", | ||
| name: "params", | ||
| type: "tuple", | ||
| }, | ||
| ], | ||
| name: "quoteExactInputSingle", | ||
| outputs: [ | ||
| { internalType: "uint256", name: "amountOut", type: "uint256" }, | ||
| { internalType: "uint160", name: "sqrtPriceX96After", type: "uint160" }, | ||
| { internalType: "uint32", name: "initializedTicksCrossed", type: "uint32" }, | ||
| { internalType: "uint256", name: "gasEstimate", type: "uint256" }, | ||
| ], | ||
| stateMutability: "nonpayable", | ||
| type: "function", | ||
| }, | ||
| ] as const; | ||
|
|
||
| /** | ||
| * Uniswap V3 SwapRouter02 contract ABI (subset for swapping) | ||
| */ | ||
| export const UNISWAP_ROUTER_ABI: Abi = [ | ||
| { | ||
| inputs: [ | ||
| { | ||
| components: [ | ||
| { internalType: "address", name: "tokenIn", type: "address" }, | ||
| { internalType: "address", name: "tokenOut", type: "address" }, | ||
| { internalType: "uint24", name: "fee", type: "uint24" }, | ||
| { internalType: "address", name: "recipient", type: "address" }, | ||
| { internalType: "uint256", name: "amountIn", type: "uint256" }, | ||
| { internalType: "uint256", name: "amountOutMinimum", type: "uint256" }, | ||
| { internalType: "uint160", name: "sqrtPriceLimitX96", type: "uint160" }, | ||
| ], | ||
| internalType: "struct IV3SwapRouter.ExactInputSingleParams", | ||
| name: "params", | ||
| type: "tuple", | ||
| }, | ||
| ], | ||
| name: "exactInputSingle", | ||
| outputs: [{ internalType: "uint256", name: "amountOut", type: "uint256" }], | ||
| stateMutability: "payable", | ||
| type: "function", | ||
| }, | ||
| ] as const; | ||
|
|
||
| /** | ||
| * Standard ERC20 token ABI (subset needed for approvals and metadata) | ||
| */ | ||
| export const ERC20_ABI: Abi = [ | ||
| { | ||
| inputs: [ | ||
| { internalType: "address", name: "spender", type: "address" }, | ||
| { internalType: "uint256", name: "amount", type: "uint256" }, | ||
| ], | ||
| name: "approve", | ||
| outputs: [{ internalType: "bool", name: "", type: "bool" }], | ||
| stateMutability: "nonpayable", | ||
| type: "function", | ||
| }, | ||
| { | ||
| inputs: [ | ||
| { internalType: "address", name: "owner", type: "address" }, | ||
| { internalType: "address", name: "spender", type: "address" }, | ||
| ], | ||
| name: "allowance", | ||
| outputs: [{ internalType: "uint256", name: "", type: "uint256" }], | ||
| stateMutability: "view", | ||
| type: "function", | ||
| }, | ||
| { | ||
| inputs: [], | ||
| name: "decimals", | ||
| outputs: [{ internalType: "uint8", name: "", type: "uint8" }], | ||
| stateMutability: "view", | ||
| type: "function", | ||
| }, | ||
| { | ||
| inputs: [], | ||
| name: "symbol", | ||
| outputs: [{ internalType: "string", name: "", type: "string" }], | ||
| stateMutability: "view", | ||
| type: "function", | ||
| }, | ||
| ] as const; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| export * from "./schemas"; | ||
| export * from "./constants"; | ||
| export * from "./uniswapActionProvider"; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| import { z } from "zod"; | ||
|
|
||
| /** | ||
| * Input schema for Uniswap token swap action. | ||
| */ | ||
| export const SwapSchema = z | ||
| .object({ | ||
| tokenIn: z.string().describe("The contract address of the token to swap from"), | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggest to use |
||
| tokenOut: z.string().describe("The contract address of the token to swap to"), | ||
| amount: z.string().describe("The amount of tokenIn to swap (in token units, e.g. '1.5')"), | ||
| slippageTolerance: z | ||
| .number() | ||
| .min(0) | ||
| .max(100) | ||
| .default(0.5) | ||
| .describe("Maximum acceptable slippage percentage (0-100, default: 0.5%)"), | ||
| fee: z | ||
| .number() | ||
| .default(3000) | ||
| .describe("Pool fee tier in hundredths of a bip (default: 3000 = 0.3%)"), | ||
| }) | ||
| .strip() | ||
| .describe("Instructions for swapping tokens on Uniswap V3"); | ||
|
|
||
| /** | ||
| * Input schema for getting a quote for a Uniswap swap. | ||
| */ | ||
| export const QuoteSchema = z | ||
| .object({ | ||
| tokenIn: z.string().describe("The contract address of the token to swap from"), | ||
| tokenOut: z.string().describe("The contract address of the token to swap to"), | ||
| amount: z.string().describe("The amount of tokenIn to quote (in token units, e.g. '1.5')"), | ||
| fee: z | ||
| .number() | ||
| .default(3000) | ||
| .describe("Pool fee tier in hundredths of a bip (default: 3000 = 0.3%)"), | ||
| }) | ||
| .strip() | ||
| .describe("Instructions for getting a quote for a Uniswap V3 swap"); | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add tests for the logic of each action. As a guide you can have a look at eg https://github.com/coinbase/agentkit/blob/main/typescript/agentkit/src/action-providers/flaunch/flaunchActionProvider.test.ts that also tests some swap actions