fix:swap api - #817

Merged
CarsonRoscoe merged 13 commits into
coinbase:mainfrom
phdargen:swap-api-ts
Aug 21, 2025
Merged

fix:swap api#817
CarsonRoscoe merged 13 commits into
coinbase:mainfrom
phdargen:swap-api-ts

Conversation

@phdargen

@phdargenphdargen commented Aug 8, 2025

Copy link
Copy Markdown
Contributor

Description

Fixes various issues with the swap API implementation in CdpApiActionProvider

  • bumps cdp-sdk version
  • throws error for unsupported networks
  • changes unsupported assetId to tokenContract for input arguments
  • adds slippage config
  • changes fromAmount to human readable format
  • handles permit2 approval if needed
  • adds get swap quote action to check price before executing
  • checks if swap failed
  • retrieves correct account for cdpSmartWallet
  • throws error when owner account is not a CDP server wallet

Other changes:

Tests

Chatbot: typescript/examples/langchain-cdp-chatbot/chatbot.ts
Network: Base mainnet
Prompt: price quote for 1 eth to usdc (0x833589fcd6edb6e08f4c7c32d4f71b54bda02913)
-------------------
{"success":true,"fromAmount":"1","fromTokenName":"ETH","fromToken":"0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee","toAmount":"3815.31087","minToAmount":"3777.11957","toTokenName":"USD Coin","toToken":"0x833589fcd6edb6e08f4c7c32d4f71b54bda02913","slippageBps":100,"liquidityAvailable":true,"balanceEnough":false,"allowanceEnough":true,"priceOfBuyTokenInSellToken":"0.0002621018402099434","priceOfSellTokenInBuyToken":"3815.31087"}
-------------------
The price quote for swapping 1 ETH to USDC (0x833589fcd6edb6e08f4c7c32d4f71b54bda02913) is approximately **3815.31 USDC**. - Minimum amount you can receive: **3777.12 USDC**
- Price of 1 ETH in USDC: **3815.31 USDC**
- Price of 1 USDC in ETH: **0.0002621 ETH**
-------------------
Prompt: swap 0.00001 eth to USDC (0x833589fcd6edb6e08f4c7c32d4f71b54bda02913) -------------------
{"success":true,"transactionHash":{"transactionHash":"0x96264f7ab5eac524ab658b61e78d31187cbce3e3442263cdef18d592b8130116"},"fromAmount":"0.00001","fromTokenName":"ETH","fromToken":"0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee","toAmount":"0.038313","minToAmount":"0.037929","toTokenName":"USD Coin","toToken":"0x833589fcd6edb6e08f4c7c32d4f71b54bda02913","slippageBps":100,"network":"base-mainnet"}
-------------------
The swap of **0.00001 ETH** to USDC (0x833589fcd6edb6e08f4c7c32d4f71b54bda02913) was successful!
- Amount received: **0.038313 USDC**
- Minimum amount you can receive: **0.037929 USDC**
- Transaction Hash: **[0x96264f7ab5eac524ab658b61e78d31187cbce3e3442263cdef18d592b8130116](https://explorer.base.org/tx/0x96264f7ab5eac524ab658b61e78d31187cbce3e3442263cdef18d592b8130116)**
-------------------
Prompt: swap 0.01 usdc to cbBTC 0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf -------------------
{"success":true,"approvalTxHash":"0x28cbe8be28a3d192ede5a870a4778363a5302e55aa60f0ac38eeeb4c09b7e89e","transactionHash":{"transactionHash":"0xccb181ce15d17d108e08be721d7c13bbcb35a959fd8508831c8fa779f3a9ac4a"},"fromAmount":"0.01","fromTokenName":"USD Coin","fromToken":"0x833589fcd6edb6e08f4c7c32d4f71b54bda02913","toAmount":"0.00000009","minToAmount":"0.00000009","toTokenName":"Coinbase Wrapped BTC","toToken":"0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf","slippageBps":100,"network":"base-mainnet"}
-------------------
The swap of **0.01 USDC** to Coinbase Wrapped BTC (0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf) was successful!
- Amount received: **0.00000009 CBTC**
- Minimum amount you can receive: **0.00000009 CBTC**
- Transaction Hash: **[0xccb181ce15d17d108e08be721d7c13bbcb35a959fd8508831c8fa779f3a9ac4a](https://explorer.base.org/tx/0xccb181ce15d17d108e08be721d7c13bbcb35a959fd8508831c8fa779f3a9ac4a)**
Chatbot: typescript/examples/langchain-smart-wallet-chatbot/chatbot.ts
Network: Base mainnet
Setup: Funded with erc20 token, no ETH, paymaster enabled
Prompt: swap 1 token 0xD769d56f479E9E72a77bB1523e866A33098Feec5 to usdc (0x833589fcd6edb6e08f4c7c32d4f71b54bda02913) -------------------
{"success":true,"approvalTxHash":"0x37a0fd65e87bbbdd88d7e7742b0f2fd51f7c8c73e6c1ff87e833fa4689a6000b","transactionHash":"0x694eb62df1f62f97b47d3d7f4397d9be9d50e7714c4afed49f9a51776cb479b1","fromAmount":"1","fromTokenName":"Base is for everyone","fromToken":"0xD769d56f479E9E72a77bB1523e866A33098Feec5","toAmount":"0.003855","minToAmount":"0.003816","toTokenName":"USD Coin","toToken":"0x833589fcd6edb6e08f4c7c32d4f71b54bda02913","slippageBps":100,"network":"base-mainnet"}
-------------------
The swap has been successfully executed!
- **Swapped**: 1 Base is for everyone
- **Received**: Approximately 0.00386 USDC
- **Transaction Hash**: [0x694eb62df1f62f97b47d3d7f4397d9be9d50e7714c4afed49f9a51776cb479b1](https://etherscan.io/tx/0x694eb62df1f62f97b47d3d7f4397d9be9d50e7714c4afed49f9a51776cb479b1)

Checklist

A couple of things to include in your PR for completeness:

  • Added documentation to all relevant README.md files
  • Added a changelog entry

@cb-heimdall

cb-heimdall commented Aug 8, 2025

Copy link
Copy Markdown

✅ Heimdall Review Status

RequirementStatusMore Info
Reviews1/1
Denominator calculation
Show calculation
1 if user is bot0
1 if user is external0
2 if repo is sensitive0
From .codeflow.yml1
Additional review requirements
Show calculation
Max0
0
From CODEOWNERS0
Global minimum0
Max 1
1
1 if commit is unverified0
Sum1

@phdargen
phdargen marked this pull request as draft August 8, 2025 14:42
@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation action provider New action provider wallet provider New wallet provider typescript labels Aug 8, 2025
@github-actionsgithub-actionsBot removed the wallet provider New wallet provider label Aug 12, 2025
@phdargen

phdargen commented Aug 12, 2025

Copy link
Copy Markdown
ContributorAuthor

Hi @0xRAG, I rebased to account for your changes in #818.

I still can't get the swap API to work with smart accounts, it throws this error APIError: EVM account with given address not found.

Also tried to use createSwapQuote + sign permit2 + sendTransaction instead of the all-in-one pattern.
Same issue but could narrow it down to happen in the sign permit2 step:

const signature = await walletProvider.smartAccount.signTypedData({ network: cdpNetwork as any, domain: swapQuote.permit2.eip712.domain, types: swapQuote.permit2.eip712.types, primaryType: swapQuote.permit2.eip712.primaryType, message: swapQuote.permit2.eip712.message, })

Any ideas how solve this?

@0xRAG

Copy link
Copy Markdown
Contributor

Hey @phdargen, I think this may happen when the smart wallet owner is not a CDP wallet, since walletProvider.smartAccount.signTypedData ends up calling the CDP API with the owner address.

Could you confirm by trying again with a new smart wallet whose owner is a CDP server wallet?

We may need to add a disclaimer that swap only works with smart accounts owned by a CDP wallet.

@github-actionsgithub-actionsBot added wallet provider New wallet provider example New example agent labels Aug 19, 2025
@phdargen
phdargenforce-pushed the swap-api-ts branch 3 times, most recently from 3dca4bb to f7332e9CompareAugust 19, 2025 23:07
@phdargen
phdargen marked this pull request as ready for review August 19, 2025 23:16
@phdargen

Copy link
Copy Markdown
ContributorAuthor

Hey @phdargen, I think this may happen when the smart wallet owner is not a CDP wallet, since walletProvider.smartAccount.signTypedData ends up calling the CDP API with the owner address.

Could you confirm by trying again with a new smart wallet whose owner is a CDP server wallet?

We may need to add a disclaimer that swap only works with smart accounts owned by a CDP wallet.

Hi @0xRAG, thanks that was indeed the problem!

I thus changed the chatbot and next template to create a CDP server wallet as owner by default and the swap action throws a descriptive error when its called with a LocalAccount as owner

? walletProvider.smartAccount
: await walletProvider.getClient().evm.getAccount({
address: walletProvider.getAddress() as Hex,
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given we are getting and later using the account / smart account via .swap, I think this action is better suited to live in the cdpEvmWalletActionProvider / cdpSmartWalletActionProvider. The get_swap_price action is provider-agnostic so that one is fine to remain, although may be worth moving along with swap just to reduce potential confusion on why the actions are on different providers. LMK your thoughts, also cc @CarsonRoscoe

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah good point, this would avoid the messy conditional logic and type casts. Will refactor

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on the codebase today, I am in agreement.

However, this is making me think about a refactor. There seems to be an association between wallet providers & action providers, and I think that's causing some confusion here.

Imagine a world where wallet provider files exported their own action providers, or used the CreateAction() decorator on their own functions, allowing them to declare tools that we can auto-register when a wallet provider is used. This would create a clear separation of concerns between what actions are needed for a specific wallet, and what actions are coming from a shared API like the CDP API. It also would reduce a friction point in the devx, which is knowing that, for example, using the CdpEvmWalletProvider means importing both WalletActionProvider and CdpEvmWalletActionProvider. It then allows CdpApiActionProvider to remain pure and focus only on features that any wallet using CDP can leverage such as get_swap_price and faucet.

@CarsonRoscoe
CarsonRoscoe merged commit 406f994 into coinbase:mainAug 21, 2025
26 checks passed
@phdargenphdargen mentioned this pull request Sep 5, 2025
2 tasks
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

action providerNew action providerdocumentationImprovements or additions to documentationexampleNew example agenttypescriptwallet providerNew wallet provider

Development

Successfully merging this pull request may close these issues.

5 participants

@phdargen@cb-heimdall@0xRAG@CarsonRoscoe@carsonroscoe-cb
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

fix:swap api - #817

Merged
CarsonRoscoe merged 13 commits into
coinbase:mainfrom
phdargen:swap-api-ts
Aug 21, 2025
Merged

fix:swap api#817
CarsonRoscoe merged 13 commits into
coinbase:mainfrom
phdargen:swap-api-ts

Conversation

@phdargen

@phdargenphdargen commented Aug 8, 2025

Copy link
Copy Markdown
Contributor

Description

Fixes various issues with the swap API implementation in CdpApiActionProvider

  • bumps cdp-sdk version
  • throws error for unsupported networks
  • changes unsupported assetId to tokenContract for input arguments
  • adds slippage config
  • changes fromAmount to human readable format
  • handles permit2 approval if needed
  • adds get swap quote action to check price before executing
  • checks if swap failed
  • retrieves correct account for cdpSmartWallet
  • throws error when owner account is not a CDP server wallet

Other changes:

Tests

Chatbot: typescript/examples/langchain-cdp-chatbot/chatbot.ts
Network: Base mainnet
Prompt: price quote for 1 eth to usdc (0x833589fcd6edb6e08f4c7c32d4f71b54bda02913)
-------------------
{"success":true,"fromAmount":"1","fromTokenName":"ETH","fromToken":"0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee","toAmount":"3815.31087","minToAmount":"3777.11957","toTokenName":"USD Coin","toToken":"0x833589fcd6edb6e08f4c7c32d4f71b54bda02913","slippageBps":100,"liquidityAvailable":true,"balanceEnough":false,"allowanceEnough":true,"priceOfBuyTokenInSellToken":"0.0002621018402099434","priceOfSellTokenInBuyToken":"3815.31087"}
-------------------
The price quote for swapping 1 ETH to USDC (0x833589fcd6edb6e08f4c7c32d4f71b54bda02913) is approximately **3815.31 USDC**. - Minimum amount you can receive: **3777.12 USDC**
- Price of 1 ETH in USDC: **3815.31 USDC**
- Price of 1 USDC in ETH: **0.0002621 ETH**
-------------------
Prompt: swap 0.00001 eth to USDC (0x833589fcd6edb6e08f4c7c32d4f71b54bda02913) -------------------
{"success":true,"transactionHash":{"transactionHash":"0x96264f7ab5eac524ab658b61e78d31187cbce3e3442263cdef18d592b8130116"},"fromAmount":"0.00001","fromTokenName":"ETH","fromToken":"0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee","toAmount":"0.038313","minToAmount":"0.037929","toTokenName":"USD Coin","toToken":"0x833589fcd6edb6e08f4c7c32d4f71b54bda02913","slippageBps":100,"network":"base-mainnet"}
-------------------
The swap of **0.00001 ETH** to USDC (0x833589fcd6edb6e08f4c7c32d4f71b54bda02913) was successful!
- Amount received: **0.038313 USDC**
- Minimum amount you can receive: **0.037929 USDC**
- Transaction Hash: **[0x96264f7ab5eac524ab658b61e78d31187cbce3e3442263cdef18d592b8130116](https://explorer.base.org/tx/0x96264f7ab5eac524ab658b61e78d31187cbce3e3442263cdef18d592b8130116)**
-------------------
Prompt: swap 0.01 usdc to cbBTC 0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf -------------------
{"success":true,"approvalTxHash":"0x28cbe8be28a3d192ede5a870a4778363a5302e55aa60f0ac38eeeb4c09b7e89e","transactionHash":{"transactionHash":"0xccb181ce15d17d108e08be721d7c13bbcb35a959fd8508831c8fa779f3a9ac4a"},"fromAmount":"0.01","fromTokenName":"USD Coin","fromToken":"0x833589fcd6edb6e08f4c7c32d4f71b54bda02913","toAmount":"0.00000009","minToAmount":"0.00000009","toTokenName":"Coinbase Wrapped BTC","toToken":"0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf","slippageBps":100,"network":"base-mainnet"}
-------------------
The swap of **0.01 USDC** to Coinbase Wrapped BTC (0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf) was successful!
- Amount received: **0.00000009 CBTC**
- Minimum amount you can receive: **0.00000009 CBTC**
- Transaction Hash: **[0xccb181ce15d17d108e08be721d7c13bbcb35a959fd8508831c8fa779f3a9ac4a](https://explorer.base.org/tx/0xccb181ce15d17d108e08be721d7c13bbcb35a959fd8508831c8fa779f3a9ac4a)**
Chatbot: typescript/examples/langchain-smart-wallet-chatbot/chatbot.ts
Network: Base mainnet
Setup: Funded with erc20 token, no ETH, paymaster enabled
Prompt: swap 1 token 0xD769d56f479E9E72a77bB1523e866A33098Feec5 to usdc (0x833589fcd6edb6e08f4c7c32d4f71b54bda02913) -------------------
{"success":true,"approvalTxHash":"0x37a0fd65e87bbbdd88d7e7742b0f2fd51f7c8c73e6c1ff87e833fa4689a6000b","transactionHash":"0x694eb62df1f62f97b47d3d7f4397d9be9d50e7714c4afed49f9a51776cb479b1","fromAmount":"1","fromTokenName":"Base is for everyone","fromToken":"0xD769d56f479E9E72a77bB1523e866A33098Feec5","toAmount":"0.003855","minToAmount":"0.003816","toTokenName":"USD Coin","toToken":"0x833589fcd6edb6e08f4c7c32d4f71b54bda02913","slippageBps":100,"network":"base-mainnet"}
-------------------
The swap has been successfully executed!
- **Swapped**: 1 Base is for everyone
- **Received**: Approximately 0.00386 USDC
- **Transaction Hash**: [0x694eb62df1f62f97b47d3d7f4397d9be9d50e7714c4afed49f9a51776cb479b1](https://etherscan.io/tx/0x694eb62df1f62f97b47d3d7f4397d9be9d50e7714c4afed49f9a51776cb479b1)

Checklist

A couple of things to include in your PR for completeness:

  • Added documentation to all relevant README.md files
  • Added a changelog entry

@cb-heimdall

cb-heimdall commented Aug 8, 2025

Copy link
Copy Markdown

✅ Heimdall Review Status

RequirementStatusMore Info
Reviews1/1
Denominator calculation
Show calculation
1 if user is bot0
1 if user is external0
2 if repo is sensitive0
From .codeflow.yml1
Additional review requirements
Show calculation
Max0
0
From CODEOWNERS0
Global minimum0
Max 1
1
1 if commit is unverified0
Sum1

@phdargen
phdargen marked this pull request as draft August 8, 2025 14:42
@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation action provider New action provider wallet provider New wallet provider typescript labels Aug 8, 2025
@github-actionsgithub-actionsBot removed the wallet provider New wallet provider label Aug 12, 2025
@phdargen

phdargen commented Aug 12, 2025

Copy link
Copy Markdown
ContributorAuthor

Hi @0xRAG, I rebased to account for your changes in #818.

I still can't get the swap API to work with smart accounts, it throws this error APIError: EVM account with given address not found.

Also tried to use createSwapQuote + sign permit2 + sendTransaction instead of the all-in-one pattern.
Same issue but could narrow it down to happen in the sign permit2 step:

const signature = await walletProvider.smartAccount.signTypedData({ network: cdpNetwork as any, domain: swapQuote.permit2.eip712.domain, types: swapQuote.permit2.eip712.types, primaryType: swapQuote.permit2.eip712.primaryType, message: swapQuote.permit2.eip712.message, })

Any ideas how solve this?

@0xRAG

Copy link
Copy Markdown
Contributor

Hey @phdargen, I think this may happen when the smart wallet owner is not a CDP wallet, since walletProvider.smartAccount.signTypedData ends up calling the CDP API with the owner address.

Could you confirm by trying again with a new smart wallet whose owner is a CDP server wallet?

We may need to add a disclaimer that swap only works with smart accounts owned by a CDP wallet.

@github-actionsgithub-actionsBot added wallet provider New wallet provider example New example agent labels Aug 19, 2025
@phdargen
phdargenforce-pushed the swap-api-ts branch 3 times, most recently from 3dca4bb to f7332e9CompareAugust 19, 2025 23:07
@phdargen
phdargen marked this pull request as ready for review August 19, 2025 23:16
@phdargen

Copy link
Copy Markdown
ContributorAuthor

Hey @phdargen, I think this may happen when the smart wallet owner is not a CDP wallet, since walletProvider.smartAccount.signTypedData ends up calling the CDP API with the owner address.

Could you confirm by trying again with a new smart wallet whose owner is a CDP server wallet?

We may need to add a disclaimer that swap only works with smart accounts owned by a CDP wallet.

Hi @0xRAG, thanks that was indeed the problem!

I thus changed the chatbot and next template to create a CDP server wallet as owner by default and the swap action throws a descriptive error when its called with a LocalAccount as owner

? walletProvider.smartAccount
: await walletProvider.getClient().evm.getAccount({
address: walletProvider.getAddress() as Hex,
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given we are getting and later using the account / smart account via .swap, I think this action is better suited to live in the cdpEvmWalletActionProvider / cdpSmartWalletActionProvider. The get_swap_price action is provider-agnostic so that one is fine to remain, although may be worth moving along with swap just to reduce potential confusion on why the actions are on different providers. LMK your thoughts, also cc @CarsonRoscoe

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah good point, this would avoid the messy conditional logic and type casts. Will refactor

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on the codebase today, I am in agreement.

However, this is making me think about a refactor. There seems to be an association between wallet providers & action providers, and I think that's causing some confusion here.

Imagine a world where wallet provider files exported their own action providers, or used the CreateAction() decorator on their own functions, allowing them to declare tools that we can auto-register when a wallet provider is used. This would create a clear separation of concerns between what actions are needed for a specific wallet, and what actions are coming from a shared API like the CDP API. It also would reduce a friction point in the devx, which is knowing that, for example, using the CdpEvmWalletProvider means importing both WalletActionProvider and CdpEvmWalletActionProvider. It then allows CdpApiActionProvider to remain pure and focus only on features that any wallet using CDP can leverage such as get_swap_price and faucet.

@CarsonRoscoe
CarsonRoscoe merged commit 406f994 into coinbase:mainAug 21, 2025
26 checks passed
@phdargenphdargen mentioned this pull request Sep 5, 2025
2 tasks
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

action providerNew action providerdocumentationImprovements or additions to documentationexampleNew example agenttypescriptwallet providerNew wallet provider

Development

Successfully merging this pull request may close these issues.

5 participants

@phdargen@cb-heimdall@0xRAG@CarsonRoscoe@carsonroscoe-cb
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix:swap api - #817

Merged
CarsonRoscoe merged 13 commits into
coinbase:mainfrom
phdargen:swap-api-ts
Aug 21, 2025
Merged

fix:swap api#817
CarsonRoscoe merged 13 commits into
coinbase:mainfrom
phdargen:swap-api-ts

Conversation

@phdargen

@phdargenphdargen commented Aug 8, 2025

Copy link
Copy Markdown
Contributor

Description

Fixes various issues with the swap API implementation in CdpApiActionProvider

  • bumps cdp-sdk version
  • throws error for unsupported networks
  • changes unsupported assetId to tokenContract for input arguments
  • adds slippage config
  • changes fromAmount to human readable format
  • handles permit2 approval if needed
  • adds get swap quote action to check price before executing
  • checks if swap failed
  • retrieves correct account for cdpSmartWallet
  • throws error when owner account is not a CDP server wallet

Other changes:

Tests

Chatbot: typescript/examples/langchain-cdp-chatbot/chatbot.ts
Network: Base mainnet
Prompt: price quote for 1 eth to usdc (0x833589fcd6edb6e08f4c7c32d4f71b54bda02913)
-------------------
{"success":true,"fromAmount":"1","fromTokenName":"ETH","fromToken":"0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee","toAmount":"3815.31087","minToAmount":"3777.11957","toTokenName":"USD Coin","toToken":"0x833589fcd6edb6e08f4c7c32d4f71b54bda02913","slippageBps":100,"liquidityAvailable":true,"balanceEnough":false,"allowanceEnough":true,"priceOfBuyTokenInSellToken":"0.0002621018402099434","priceOfSellTokenInBuyToken":"3815.31087"}
-------------------
The price quote for swapping 1 ETH to USDC (0x833589fcd6edb6e08f4c7c32d4f71b54bda02913) is approximately **3815.31 USDC**. - Minimum amount you can receive: **3777.12 USDC**
- Price of 1 ETH in USDC: **3815.31 USDC**
- Price of 1 USDC in ETH: **0.0002621 ETH**
-------------------
Prompt: swap 0.00001 eth to USDC (0x833589fcd6edb6e08f4c7c32d4f71b54bda02913) -------------------
{"success":true,"transactionHash":{"transactionHash":"0x96264f7ab5eac524ab658b61e78d31187cbce3e3442263cdef18d592b8130116"},"fromAmount":"0.00001","fromTokenName":"ETH","fromToken":"0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee","toAmount":"0.038313","minToAmount":"0.037929","toTokenName":"USD Coin","toToken":"0x833589fcd6edb6e08f4c7c32d4f71b54bda02913","slippageBps":100,"network":"base-mainnet"}
-------------------
The swap of **0.00001 ETH** to USDC (0x833589fcd6edb6e08f4c7c32d4f71b54bda02913) was successful!
- Amount received: **0.038313 USDC**
- Minimum amount you can receive: **0.037929 USDC**
- Transaction Hash: **[0x96264f7ab5eac524ab658b61e78d31187cbce3e3442263cdef18d592b8130116](https://explorer.base.org/tx/0x96264f7ab5eac524ab658b61e78d31187cbce3e3442263cdef18d592b8130116)**
-------------------
Prompt: swap 0.01 usdc to cbBTC 0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf -------------------
{"success":true,"approvalTxHash":"0x28cbe8be28a3d192ede5a870a4778363a5302e55aa60f0ac38eeeb4c09b7e89e","transactionHash":{"transactionHash":"0xccb181ce15d17d108e08be721d7c13bbcb35a959fd8508831c8fa779f3a9ac4a"},"fromAmount":"0.01","fromTokenName":"USD Coin","fromToken":"0x833589fcd6edb6e08f4c7c32d4f71b54bda02913","toAmount":"0.00000009","minToAmount":"0.00000009","toTokenName":"Coinbase Wrapped BTC","toToken":"0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf","slippageBps":100,"network":"base-mainnet"}
-------------------
The swap of **0.01 USDC** to Coinbase Wrapped BTC (0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf) was successful!
- Amount received: **0.00000009 CBTC**
- Minimum amount you can receive: **0.00000009 CBTC**
- Transaction Hash: **[0xccb181ce15d17d108e08be721d7c13bbcb35a959fd8508831c8fa779f3a9ac4a](https://explorer.base.org/tx/0xccb181ce15d17d108e08be721d7c13bbcb35a959fd8508831c8fa779f3a9ac4a)**
Chatbot: typescript/examples/langchain-smart-wallet-chatbot/chatbot.ts
Network: Base mainnet
Setup: Funded with erc20 token, no ETH, paymaster enabled
Prompt: swap 1 token 0xD769d56f479E9E72a77bB1523e866A33098Feec5 to usdc (0x833589fcd6edb6e08f4c7c32d4f71b54bda02913) -------------------
{"success":true,"approvalTxHash":"0x37a0fd65e87bbbdd88d7e7742b0f2fd51f7c8c73e6c1ff87e833fa4689a6000b","transactionHash":"0x694eb62df1f62f97b47d3d7f4397d9be9d50e7714c4afed49f9a51776cb479b1","fromAmount":"1","fromTokenName":"Base is for everyone","fromToken":"0xD769d56f479E9E72a77bB1523e866A33098Feec5","toAmount":"0.003855","minToAmount":"0.003816","toTokenName":"USD Coin","toToken":"0x833589fcd6edb6e08f4c7c32d4f71b54bda02913","slippageBps":100,"network":"base-mainnet"}
-------------------
The swap has been successfully executed!
- **Swapped**: 1 Base is for everyone
- **Received**: Approximately 0.00386 USDC
- **Transaction Hash**: [0x694eb62df1f62f97b47d3d7f4397d9be9d50e7714c4afed49f9a51776cb479b1](https://etherscan.io/tx/0x694eb62df1f62f97b47d3d7f4397d9be9d50e7714c4afed49f9a51776cb479b1)

Checklist

A couple of things to include in your PR for completeness:

  • Added documentation to all relevant README.md files
  • Added a changelog entry

@cb-heimdall

cb-heimdall commented Aug 8, 2025

Copy link
Copy Markdown

✅ Heimdall Review Status

RequirementStatusMore Info
Reviews1/1
Denominator calculation
Show calculation
1 if user is bot0
1 if user is external0
2 if repo is sensitive0
From .codeflow.yml1
Additional review requirements
Show calculation
Max0
0
From CODEOWNERS0
Global minimum0
Max 1
1
1 if commit is unverified0
Sum1

@phdargen
phdargen marked this pull request as draft August 8, 2025 14:42
@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation action provider New action provider wallet provider New wallet provider typescript labels Aug 8, 2025
@github-actionsgithub-actionsBot removed the wallet provider New wallet provider label Aug 12, 2025
@phdargen

phdargen commented Aug 12, 2025

Copy link
Copy Markdown
ContributorAuthor

Hi @0xRAG, I rebased to account for your changes in #818.

I still can't get the swap API to work with smart accounts, it throws this error APIError: EVM account with given address not found.

Also tried to use createSwapQuote + sign permit2 + sendTransaction instead of the all-in-one pattern.
Same issue but could narrow it down to happen in the sign permit2 step:

const signature = await walletProvider.smartAccount.signTypedData({ network: cdpNetwork as any, domain: swapQuote.permit2.eip712.domain, types: swapQuote.permit2.eip712.types, primaryType: swapQuote.permit2.eip712.primaryType, message: swapQuote.permit2.eip712.message, })

Any ideas how solve this?

@0xRAG

Copy link
Copy Markdown
Contributor

Hey @phdargen, I think this may happen when the smart wallet owner is not a CDP wallet, since walletProvider.smartAccount.signTypedData ends up calling the CDP API with the owner address.

Could you confirm by trying again with a new smart wallet whose owner is a CDP server wallet?

We may need to add a disclaimer that swap only works with smart accounts owned by a CDP wallet.

@github-actionsgithub-actionsBot added wallet provider New wallet provider example New example agent labels Aug 19, 2025
@phdargen
phdargenforce-pushed the swap-api-ts branch 3 times, most recently from 3dca4bb to f7332e9CompareAugust 19, 2025 23:07
@phdargen
phdargen marked this pull request as ready for review August 19, 2025 23:16
@phdargen

Copy link
Copy Markdown
ContributorAuthor

Hey @phdargen, I think this may happen when the smart wallet owner is not a CDP wallet, since walletProvider.smartAccount.signTypedData ends up calling the CDP API with the owner address.

Could you confirm by trying again with a new smart wallet whose owner is a CDP server wallet?

We may need to add a disclaimer that swap only works with smart accounts owned by a CDP wallet.

Hi @0xRAG, thanks that was indeed the problem!

I thus changed the chatbot and next template to create a CDP server wallet as owner by default and the swap action throws a descriptive error when its called with a LocalAccount as owner

? walletProvider.smartAccount
: await walletProvider.getClient().evm.getAccount({
address: walletProvider.getAddress() as Hex,
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given we are getting and later using the account / smart account via .swap, I think this action is better suited to live in the cdpEvmWalletActionProvider / cdpSmartWalletActionProvider. The get_swap_price action is provider-agnostic so that one is fine to remain, although may be worth moving along with swap just to reduce potential confusion on why the actions are on different providers. LMK your thoughts, also cc @CarsonRoscoe

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah good point, this would avoid the messy conditional logic and type casts. Will refactor

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on the codebase today, I am in agreement.

However, this is making me think about a refactor. There seems to be an association between wallet providers & action providers, and I think that's causing some confusion here.

Imagine a world where wallet provider files exported their own action providers, or used the CreateAction() decorator on their own functions, allowing them to declare tools that we can auto-register when a wallet provider is used. This would create a clear separation of concerns between what actions are needed for a specific wallet, and what actions are coming from a shared API like the CDP API. It also would reduce a friction point in the devx, which is knowing that, for example, using the CdpEvmWalletProvider means importing both WalletActionProvider and CdpEvmWalletActionProvider. It then allows CdpApiActionProvider to remain pure and focus only on features that any wallet using CDP can leverage such as get_swap_price and faucet.

@CarsonRoscoe
CarsonRoscoe merged commit 406f994 into coinbase:mainAug 21, 2025
26 checks passed
@phdargenphdargen mentioned this pull request Sep 5, 2025
2 tasks
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

action providerNew action providerdocumentationImprovements or additions to documentationexampleNew example agenttypescriptwallet providerNew wallet provider

Development

Successfully merging this pull request may close these issues.

5 participants

@phdargen@cb-heimdall@0xRAG@CarsonRoscoe@carsonroscoe-cb
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix:swap api - #817

Merged
CarsonRoscoe merged 13 commits into
coinbase:mainfrom
phdargen:swap-api-ts
Aug 21, 2025
Merged

fix:swap api#817
CarsonRoscoe merged 13 commits into
coinbase:mainfrom
phdargen:swap-api-ts

Conversation

@phdargen

@phdargenphdargen commented Aug 8, 2025

Copy link
Copy Markdown
Contributor

Description

Fixes various issues with the swap API implementation in CdpApiActionProvider

  • bumps cdp-sdk version
  • throws error for unsupported networks
  • changes unsupported assetId to tokenContract for input arguments
  • adds slippage config
  • changes fromAmount to human readable format
  • handles permit2 approval if needed
  • adds get swap quote action to check price before executing
  • checks if swap failed
  • retrieves correct account for cdpSmartWallet
  • throws error when owner account is not a CDP server wallet

Other changes:

Tests

Chatbot: typescript/examples/langchain-cdp-chatbot/chatbot.ts
Network: Base mainnet
Prompt: price quote for 1 eth to usdc (0x833589fcd6edb6e08f4c7c32d4f71b54bda02913)
-------------------
{"success":true,"fromAmount":"1","fromTokenName":"ETH","fromToken":"0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee","toAmount":"3815.31087","minToAmount":"3777.11957","toTokenName":"USD Coin","toToken":"0x833589fcd6edb6e08f4c7c32d4f71b54bda02913","slippageBps":100,"liquidityAvailable":true,"balanceEnough":false,"allowanceEnough":true,"priceOfBuyTokenInSellToken":"0.0002621018402099434","priceOfSellTokenInBuyToken":"3815.31087"}
-------------------
The price quote for swapping 1 ETH to USDC (0x833589fcd6edb6e08f4c7c32d4f71b54bda02913) is approximately **3815.31 USDC**. - Minimum amount you can receive: **3777.12 USDC**
- Price of 1 ETH in USDC: **3815.31 USDC**
- Price of 1 USDC in ETH: **0.0002621 ETH**
-------------------
Prompt: swap 0.00001 eth to USDC (0x833589fcd6edb6e08f4c7c32d4f71b54bda02913) -------------------
{"success":true,"transactionHash":{"transactionHash":"0x96264f7ab5eac524ab658b61e78d31187cbce3e3442263cdef18d592b8130116"},"fromAmount":"0.00001","fromTokenName":"ETH","fromToken":"0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee","toAmount":"0.038313","minToAmount":"0.037929","toTokenName":"USD Coin","toToken":"0x833589fcd6edb6e08f4c7c32d4f71b54bda02913","slippageBps":100,"network":"base-mainnet"}
-------------------
The swap of **0.00001 ETH** to USDC (0x833589fcd6edb6e08f4c7c32d4f71b54bda02913) was successful!
- Amount received: **0.038313 USDC**
- Minimum amount you can receive: **0.037929 USDC**
- Transaction Hash: **[0x96264f7ab5eac524ab658b61e78d31187cbce3e3442263cdef18d592b8130116](https://explorer.base.org/tx/0x96264f7ab5eac524ab658b61e78d31187cbce3e3442263cdef18d592b8130116)**
-------------------
Prompt: swap 0.01 usdc to cbBTC 0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf -------------------
{"success":true,"approvalTxHash":"0x28cbe8be28a3d192ede5a870a4778363a5302e55aa60f0ac38eeeb4c09b7e89e","transactionHash":{"transactionHash":"0xccb181ce15d17d108e08be721d7c13bbcb35a959fd8508831c8fa779f3a9ac4a"},"fromAmount":"0.01","fromTokenName":"USD Coin","fromToken":"0x833589fcd6edb6e08f4c7c32d4f71b54bda02913","toAmount":"0.00000009","minToAmount":"0.00000009","toTokenName":"Coinbase Wrapped BTC","toToken":"0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf","slippageBps":100,"network":"base-mainnet"}
-------------------
The swap of **0.01 USDC** to Coinbase Wrapped BTC (0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf) was successful!
- Amount received: **0.00000009 CBTC**
- Minimum amount you can receive: **0.00000009 CBTC**
- Transaction Hash: **[0xccb181ce15d17d108e08be721d7c13bbcb35a959fd8508831c8fa779f3a9ac4a](https://explorer.base.org/tx/0xccb181ce15d17d108e08be721d7c13bbcb35a959fd8508831c8fa779f3a9ac4a)**
Chatbot: typescript/examples/langchain-smart-wallet-chatbot/chatbot.ts
Network: Base mainnet
Setup: Funded with erc20 token, no ETH, paymaster enabled
Prompt: swap 1 token 0xD769d56f479E9E72a77bB1523e866A33098Feec5 to usdc (0x833589fcd6edb6e08f4c7c32d4f71b54bda02913) -------------------
{"success":true,"approvalTxHash":"0x37a0fd65e87bbbdd88d7e7742b0f2fd51f7c8c73e6c1ff87e833fa4689a6000b","transactionHash":"0x694eb62df1f62f97b47d3d7f4397d9be9d50e7714c4afed49f9a51776cb479b1","fromAmount":"1","fromTokenName":"Base is for everyone","fromToken":"0xD769d56f479E9E72a77bB1523e866A33098Feec5","toAmount":"0.003855","minToAmount":"0.003816","toTokenName":"USD Coin","toToken":"0x833589fcd6edb6e08f4c7c32d4f71b54bda02913","slippageBps":100,"network":"base-mainnet"}
-------------------
The swap has been successfully executed!
- **Swapped**: 1 Base is for everyone
- **Received**: Approximately 0.00386 USDC
- **Transaction Hash**: [0x694eb62df1f62f97b47d3d7f4397d9be9d50e7714c4afed49f9a51776cb479b1](https://etherscan.io/tx/0x694eb62df1f62f97b47d3d7f4397d9be9d50e7714c4afed49f9a51776cb479b1)

Checklist

A couple of things to include in your PR for completeness:

  • Added documentation to all relevant README.md files
  • Added a changelog entry

@cb-heimdall

cb-heimdall commented Aug 8, 2025

Copy link
Copy Markdown

✅ Heimdall Review Status

RequirementStatusMore Info
Reviews1/1
Denominator calculation
Show calculation
1 if user is bot0
1 if user is external0
2 if repo is sensitive0
From .codeflow.yml1
Additional review requirements
Show calculation
Max0
0
From CODEOWNERS0
Global minimum0
Max 1
1
1 if commit is unverified0
Sum1

@phdargen
phdargen marked this pull request as draft August 8, 2025 14:42
@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation action provider New action provider wallet provider New wallet provider typescript labels Aug 8, 2025
@github-actionsgithub-actionsBot removed the wallet provider New wallet provider label Aug 12, 2025
@phdargen

phdargen commented Aug 12, 2025

Copy link
Copy Markdown
ContributorAuthor

Hi @0xRAG, I rebased to account for your changes in #818.

I still can't get the swap API to work with smart accounts, it throws this error APIError: EVM account with given address not found.

Also tried to use createSwapQuote + sign permit2 + sendTransaction instead of the all-in-one pattern.
Same issue but could narrow it down to happen in the sign permit2 step:

const signature = await walletProvider.smartAccount.signTypedData({ network: cdpNetwork as any, domain: swapQuote.permit2.eip712.domain, types: swapQuote.permit2.eip712.types, primaryType: swapQuote.permit2.eip712.primaryType, message: swapQuote.permit2.eip712.message, })

Any ideas how solve this?

@0xRAG

Copy link
Copy Markdown
Contributor

Hey @phdargen, I think this may happen when the smart wallet owner is not a CDP wallet, since walletProvider.smartAccount.signTypedData ends up calling the CDP API with the owner address.

Could you confirm by trying again with a new smart wallet whose owner is a CDP server wallet?

We may need to add a disclaimer that swap only works with smart accounts owned by a CDP wallet.

@github-actionsgithub-actionsBot added wallet provider New wallet provider example New example agent labels Aug 19, 2025
@phdargen
phdargenforce-pushed the swap-api-ts branch 3 times, most recently from 3dca4bb to f7332e9CompareAugust 19, 2025 23:07
@phdargen
phdargen marked this pull request as ready for review August 19, 2025 23:16
@phdargen

Copy link
Copy Markdown
ContributorAuthor

Hey @phdargen, I think this may happen when the smart wallet owner is not a CDP wallet, since walletProvider.smartAccount.signTypedData ends up calling the CDP API with the owner address.

Could you confirm by trying again with a new smart wallet whose owner is a CDP server wallet?

We may need to add a disclaimer that swap only works with smart accounts owned by a CDP wallet.

Hi @0xRAG, thanks that was indeed the problem!

I thus changed the chatbot and next template to create a CDP server wallet as owner by default and the swap action throws a descriptive error when its called with a LocalAccount as owner

? walletProvider.smartAccount
: await walletProvider.getClient().evm.getAccount({
address: walletProvider.getAddress() as Hex,
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given we are getting and later using the account / smart account via .swap, I think this action is better suited to live in the cdpEvmWalletActionProvider / cdpSmartWalletActionProvider. The get_swap_price action is provider-agnostic so that one is fine to remain, although may be worth moving along with swap just to reduce potential confusion on why the actions are on different providers. LMK your thoughts, also cc @CarsonRoscoe

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah good point, this would avoid the messy conditional logic and type casts. Will refactor

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on the codebase today, I am in agreement.

However, this is making me think about a refactor. There seems to be an association between wallet providers & action providers, and I think that's causing some confusion here.

Imagine a world where wallet provider files exported their own action providers, or used the CreateAction() decorator on their own functions, allowing them to declare tools that we can auto-register when a wallet provider is used. This would create a clear separation of concerns between what actions are needed for a specific wallet, and what actions are coming from a shared API like the CDP API. It also would reduce a friction point in the devx, which is knowing that, for example, using the CdpEvmWalletProvider means importing both WalletActionProvider and CdpEvmWalletActionProvider. It then allows CdpApiActionProvider to remain pure and focus only on features that any wallet using CDP can leverage such as get_swap_price and faucet.

@CarsonRoscoe
CarsonRoscoe merged commit 406f994 into coinbase:mainAug 21, 2025
26 checks passed
@phdargenphdargen mentioned this pull request Sep 5, 2025
2 tasks
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

action providerNew action providerdocumentationImprovements or additions to documentationexampleNew example agenttypescriptwallet providerNew wallet provider

Development

Successfully merging this pull request may close these issues.

5 participants

@phdargen@cb-heimdall@0xRAG@CarsonRoscoe@carsonroscoe-cb
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

fix:swap api - #817

Merged
CarsonRoscoe merged 13 commits into
coinbase:mainfrom
phdargen:swap-api-ts
Aug 21, 2025
Merged

fix:swap api#817
CarsonRoscoe merged 13 commits into
coinbase:mainfrom
phdargen:swap-api-ts

Conversation

@phdargen

@phdargenphdargen commented Aug 8, 2025

Copy link
Copy Markdown
Contributor

Description

Fixes various issues with the swap API implementation in CdpApiActionProvider

  • bumps cdp-sdk version
  • throws error for unsupported networks
  • changes unsupported assetId to tokenContract for input arguments
  • adds slippage config
  • changes fromAmount to human readable format
  • handles permit2 approval if needed
  • adds get swap quote action to check price before executing
  • checks if swap failed
  • retrieves correct account for cdpSmartWallet
  • throws error when owner account is not a CDP server wallet

Other changes:

Tests

Chatbot: typescript/examples/langchain-cdp-chatbot/chatbot.ts
Network: Base mainnet
Prompt: price quote for 1 eth to usdc (0x833589fcd6edb6e08f4c7c32d4f71b54bda02913)
-------------------
{"success":true,"fromAmount":"1","fromTokenName":"ETH","fromToken":"0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee","toAmount":"3815.31087","minToAmount":"3777.11957","toTokenName":"USD Coin","toToken":"0x833589fcd6edb6e08f4c7c32d4f71b54bda02913","slippageBps":100,"liquidityAvailable":true,"balanceEnough":false,"allowanceEnough":true,"priceOfBuyTokenInSellToken":"0.0002621018402099434","priceOfSellTokenInBuyToken":"3815.31087"}
-------------------
The price quote for swapping 1 ETH to USDC (0x833589fcd6edb6e08f4c7c32d4f71b54bda02913) is approximately **3815.31 USDC**. - Minimum amount you can receive: **3777.12 USDC**
- Price of 1 ETH in USDC: **3815.31 USDC**
- Price of 1 USDC in ETH: **0.0002621 ETH**
-------------------
Prompt: swap 0.00001 eth to USDC (0x833589fcd6edb6e08f4c7c32d4f71b54bda02913) -------------------
{"success":true,"transactionHash":{"transactionHash":"0x96264f7ab5eac524ab658b61e78d31187cbce3e3442263cdef18d592b8130116"},"fromAmount":"0.00001","fromTokenName":"ETH","fromToken":"0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee","toAmount":"0.038313","minToAmount":"0.037929","toTokenName":"USD Coin","toToken":"0x833589fcd6edb6e08f4c7c32d4f71b54bda02913","slippageBps":100,"network":"base-mainnet"}
-------------------
The swap of **0.00001 ETH** to USDC (0x833589fcd6edb6e08f4c7c32d4f71b54bda02913) was successful!
- Amount received: **0.038313 USDC**
- Minimum amount you can receive: **0.037929 USDC**
- Transaction Hash: **[0x96264f7ab5eac524ab658b61e78d31187cbce3e3442263cdef18d592b8130116](https://explorer.base.org/tx/0x96264f7ab5eac524ab658b61e78d31187cbce3e3442263cdef18d592b8130116)**
-------------------
Prompt: swap 0.01 usdc to cbBTC 0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf -------------------
{"success":true,"approvalTxHash":"0x28cbe8be28a3d192ede5a870a4778363a5302e55aa60f0ac38eeeb4c09b7e89e","transactionHash":{"transactionHash":"0xccb181ce15d17d108e08be721d7c13bbcb35a959fd8508831c8fa779f3a9ac4a"},"fromAmount":"0.01","fromTokenName":"USD Coin","fromToken":"0x833589fcd6edb6e08f4c7c32d4f71b54bda02913","toAmount":"0.00000009","minToAmount":"0.00000009","toTokenName":"Coinbase Wrapped BTC","toToken":"0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf","slippageBps":100,"network":"base-mainnet"}
-------------------
The swap of **0.01 USDC** to Coinbase Wrapped BTC (0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf) was successful!
- Amount received: **0.00000009 CBTC**
- Minimum amount you can receive: **0.00000009 CBTC**
- Transaction Hash: **[0xccb181ce15d17d108e08be721d7c13bbcb35a959fd8508831c8fa779f3a9ac4a](https://explorer.base.org/tx/0xccb181ce15d17d108e08be721d7c13bbcb35a959fd8508831c8fa779f3a9ac4a)**
Chatbot: typescript/examples/langchain-smart-wallet-chatbot/chatbot.ts
Network: Base mainnet
Setup: Funded with erc20 token, no ETH, paymaster enabled
Prompt: swap 1 token 0xD769d56f479E9E72a77bB1523e866A33098Feec5 to usdc (0x833589fcd6edb6e08f4c7c32d4f71b54bda02913) -------------------
{"success":true,"approvalTxHash":"0x37a0fd65e87bbbdd88d7e7742b0f2fd51f7c8c73e6c1ff87e833fa4689a6000b","transactionHash":"0x694eb62df1f62f97b47d3d7f4397d9be9d50e7714c4afed49f9a51776cb479b1","fromAmount":"1","fromTokenName":"Base is for everyone","fromToken":"0xD769d56f479E9E72a77bB1523e866A33098Feec5","toAmount":"0.003855","minToAmount":"0.003816","toTokenName":"USD Coin","toToken":"0x833589fcd6edb6e08f4c7c32d4f71b54bda02913","slippageBps":100,"network":"base-mainnet"}
-------------------
The swap has been successfully executed!
- **Swapped**: 1 Base is for everyone
- **Received**: Approximately 0.00386 USDC
- **Transaction Hash**: [0x694eb62df1f62f97b47d3d7f4397d9be9d50e7714c4afed49f9a51776cb479b1](https://etherscan.io/tx/0x694eb62df1f62f97b47d3d7f4397d9be9d50e7714c4afed49f9a51776cb479b1)

Checklist

A couple of things to include in your PR for completeness:

  • Added documentation to all relevant README.md files
  • Added a changelog entry

@cb-heimdall

cb-heimdall commented Aug 8, 2025

Copy link
Copy Markdown

✅ Heimdall Review Status

RequirementStatusMore Info
Reviews1/1
Denominator calculation
Show calculation
1 if user is bot0
1 if user is external0
2 if repo is sensitive0
From .codeflow.yml1
Additional review requirements
Show calculation
Max0
0
From CODEOWNERS0
Global minimum0
Max 1
1
1 if commit is unverified0
Sum1

@phdargen
phdargen marked this pull request as draft August 8, 2025 14:42
@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation action provider New action provider wallet provider New wallet provider typescript labels Aug 8, 2025
@github-actionsgithub-actionsBot removed the wallet provider New wallet provider label Aug 12, 2025
@phdargen

phdargen commented Aug 12, 2025

Copy link
Copy Markdown
ContributorAuthor

Hi @0xRAG, I rebased to account for your changes in #818.

I still can't get the swap API to work with smart accounts, it throws this error APIError: EVM account with given address not found.

Also tried to use createSwapQuote + sign permit2 + sendTransaction instead of the all-in-one pattern.
Same issue but could narrow it down to happen in the sign permit2 step:

const signature = await walletProvider.smartAccount.signTypedData({ network: cdpNetwork as any, domain: swapQuote.permit2.eip712.domain, types: swapQuote.permit2.eip712.types, primaryType: swapQuote.permit2.eip712.primaryType, message: swapQuote.permit2.eip712.message, })

Any ideas how solve this?

@0xRAG

Copy link
Copy Markdown
Contributor

Hey @phdargen, I think this may happen when the smart wallet owner is not a CDP wallet, since walletProvider.smartAccount.signTypedData ends up calling the CDP API with the owner address.

Could you confirm by trying again with a new smart wallet whose owner is a CDP server wallet?

We may need to add a disclaimer that swap only works with smart accounts owned by a CDP wallet.

@github-actionsgithub-actionsBot added wallet provider New wallet provider example New example agent labels Aug 19, 2025
@phdargen
phdargenforce-pushed the swap-api-ts branch 3 times, most recently from 3dca4bb to f7332e9CompareAugust 19, 2025 23:07
@phdargen
phdargen marked this pull request as ready for review August 19, 2025 23:16
@phdargen

Copy link
Copy Markdown
ContributorAuthor

Hey @phdargen, I think this may happen when the smart wallet owner is not a CDP wallet, since walletProvider.smartAccount.signTypedData ends up calling the CDP API with the owner address.

Could you confirm by trying again with a new smart wallet whose owner is a CDP server wallet?

We may need to add a disclaimer that swap only works with smart accounts owned by a CDP wallet.

Hi @0xRAG, thanks that was indeed the problem!

I thus changed the chatbot and next template to create a CDP server wallet as owner by default and the swap action throws a descriptive error when its called with a LocalAccount as owner

? walletProvider.smartAccount
: await walletProvider.getClient().evm.getAccount({
address: walletProvider.getAddress() as Hex,
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given we are getting and later using the account / smart account via .swap, I think this action is better suited to live in the cdpEvmWalletActionProvider / cdpSmartWalletActionProvider. The get_swap_price action is provider-agnostic so that one is fine to remain, although may be worth moving along with swap just to reduce potential confusion on why the actions are on different providers. LMK your thoughts, also cc @CarsonRoscoe

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah good point, this would avoid the messy conditional logic and type casts. Will refactor

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on the codebase today, I am in agreement.

However, this is making me think about a refactor. There seems to be an association between wallet providers & action providers, and I think that's causing some confusion here.

Imagine a world where wallet provider files exported their own action providers, or used the CreateAction() decorator on their own functions, allowing them to declare tools that we can auto-register when a wallet provider is used. This would create a clear separation of concerns between what actions are needed for a specific wallet, and what actions are coming from a shared API like the CDP API. It also would reduce a friction point in the devx, which is knowing that, for example, using the CdpEvmWalletProvider means importing both WalletActionProvider and CdpEvmWalletActionProvider. It then allows CdpApiActionProvider to remain pure and focus only on features that any wallet using CDP can leverage such as get_swap_price and faucet.

@CarsonRoscoe
CarsonRoscoe merged commit 406f994 into coinbase:mainAug 21, 2025
26 checks passed
@phdargenphdargen mentioned this pull request Sep 5, 2025
2 tasks
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

action providerNew action providerdocumentationImprovements or additions to documentationexampleNew example agenttypescriptwallet providerNew wallet provider

Development

Successfully merging this pull request may close these issues.

5 participants

@phdargen@cb-heimdall@0xRAG@CarsonRoscoe@carsonroscoe-cb
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix:swap api - #817

Merged
CarsonRoscoe merged 13 commits into
coinbase:mainfrom
phdargen:swap-api-ts
Aug 21, 2025
Merged

fix:swap api#817
CarsonRoscoe merged 13 commits into
coinbase:mainfrom
phdargen:swap-api-ts

Conversation

@phdargen

@phdargenphdargen commented Aug 8, 2025

Copy link
Copy Markdown
Contributor

Description

Fixes various issues with the swap API implementation in CdpApiActionProvider

  • bumps cdp-sdk version
  • throws error for unsupported networks
  • changes unsupported assetId to tokenContract for input arguments
  • adds slippage config
  • changes fromAmount to human readable format
  • handles permit2 approval if needed
  • adds get swap quote action to check price before executing
  • checks if swap failed
  • retrieves correct account for cdpSmartWallet
  • throws error when owner account is not a CDP server wallet

Other changes:

Tests

Chatbot: typescript/examples/langchain-cdp-chatbot/chatbot.ts
Network: Base mainnet
Prompt: price quote for 1 eth to usdc (0x833589fcd6edb6e08f4c7c32d4f71b54bda02913)
-------------------
{"success":true,"fromAmount":"1","fromTokenName":"ETH","fromToken":"0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee","toAmount":"3815.31087","minToAmount":"3777.11957","toTokenName":"USD Coin","toToken":"0x833589fcd6edb6e08f4c7c32d4f71b54bda02913","slippageBps":100,"liquidityAvailable":true,"balanceEnough":false,"allowanceEnough":true,"priceOfBuyTokenInSellToken":"0.0002621018402099434","priceOfSellTokenInBuyToken":"3815.31087"}
-------------------
The price quote for swapping 1 ETH to USDC (0x833589fcd6edb6e08f4c7c32d4f71b54bda02913) is approximately **3815.31 USDC**. - Minimum amount you can receive: **3777.12 USDC**
- Price of 1 ETH in USDC: **3815.31 USDC**
- Price of 1 USDC in ETH: **0.0002621 ETH**
-------------------
Prompt: swap 0.00001 eth to USDC (0x833589fcd6edb6e08f4c7c32d4f71b54bda02913) -------------------
{"success":true,"transactionHash":{"transactionHash":"0x96264f7ab5eac524ab658b61e78d31187cbce3e3442263cdef18d592b8130116"},"fromAmount":"0.00001","fromTokenName":"ETH","fromToken":"0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee","toAmount":"0.038313","minToAmount":"0.037929","toTokenName":"USD Coin","toToken":"0x833589fcd6edb6e08f4c7c32d4f71b54bda02913","slippageBps":100,"network":"base-mainnet"}
-------------------
The swap of **0.00001 ETH** to USDC (0x833589fcd6edb6e08f4c7c32d4f71b54bda02913) was successful!
- Amount received: **0.038313 USDC**
- Minimum amount you can receive: **0.037929 USDC**
- Transaction Hash: **[0x96264f7ab5eac524ab658b61e78d31187cbce3e3442263cdef18d592b8130116](https://explorer.base.org/tx/0x96264f7ab5eac524ab658b61e78d31187cbce3e3442263cdef18d592b8130116)**
-------------------
Prompt: swap 0.01 usdc to cbBTC 0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf -------------------
{"success":true,"approvalTxHash":"0x28cbe8be28a3d192ede5a870a4778363a5302e55aa60f0ac38eeeb4c09b7e89e","transactionHash":{"transactionHash":"0xccb181ce15d17d108e08be721d7c13bbcb35a959fd8508831c8fa779f3a9ac4a"},"fromAmount":"0.01","fromTokenName":"USD Coin","fromToken":"0x833589fcd6edb6e08f4c7c32d4f71b54bda02913","toAmount":"0.00000009","minToAmount":"0.00000009","toTokenName":"Coinbase Wrapped BTC","toToken":"0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf","slippageBps":100,"network":"base-mainnet"}
-------------------
The swap of **0.01 USDC** to Coinbase Wrapped BTC (0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf) was successful!
- Amount received: **0.00000009 CBTC**
- Minimum amount you can receive: **0.00000009 CBTC**
- Transaction Hash: **[0xccb181ce15d17d108e08be721d7c13bbcb35a959fd8508831c8fa779f3a9ac4a](https://explorer.base.org/tx/0xccb181ce15d17d108e08be721d7c13bbcb35a959fd8508831c8fa779f3a9ac4a)**
Chatbot: typescript/examples/langchain-smart-wallet-chatbot/chatbot.ts
Network: Base mainnet
Setup: Funded with erc20 token, no ETH, paymaster enabled
Prompt: swap 1 token 0xD769d56f479E9E72a77bB1523e866A33098Feec5 to usdc (0x833589fcd6edb6e08f4c7c32d4f71b54bda02913) -------------------
{"success":true,"approvalTxHash":"0x37a0fd65e87bbbdd88d7e7742b0f2fd51f7c8c73e6c1ff87e833fa4689a6000b","transactionHash":"0x694eb62df1f62f97b47d3d7f4397d9be9d50e7714c4afed49f9a51776cb479b1","fromAmount":"1","fromTokenName":"Base is for everyone","fromToken":"0xD769d56f479E9E72a77bB1523e866A33098Feec5","toAmount":"0.003855","minToAmount":"0.003816","toTokenName":"USD Coin","toToken":"0x833589fcd6edb6e08f4c7c32d4f71b54bda02913","slippageBps":100,"network":"base-mainnet"}
-------------------
The swap has been successfully executed!
- **Swapped**: 1 Base is for everyone
- **Received**: Approximately 0.00386 USDC
- **Transaction Hash**: [0x694eb62df1f62f97b47d3d7f4397d9be9d50e7714c4afed49f9a51776cb479b1](https://etherscan.io/tx/0x694eb62df1f62f97b47d3d7f4397d9be9d50e7714c4afed49f9a51776cb479b1)

Checklist

A couple of things to include in your PR for completeness:

  • Added documentation to all relevant README.md files
  • Added a changelog entry

@cb-heimdall

cb-heimdall commented Aug 8, 2025

Copy link
Copy Markdown

✅ Heimdall Review Status

RequirementStatusMore Info
Reviews1/1
Denominator calculation
Show calculation
1 if user is bot0
1 if user is external0
2 if repo is sensitive0
From .codeflow.yml1
Additional review requirements
Show calculation
Max0
0
From CODEOWNERS0
Global minimum0
Max 1
1
1 if commit is unverified0
Sum1

@phdargen
phdargen marked this pull request as draft August 8, 2025 14:42
@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation action provider New action provider wallet provider New wallet provider typescript labels Aug 8, 2025
@github-actionsgithub-actionsBot removed the wallet provider New wallet provider label Aug 12, 2025
@phdargen

phdargen commented Aug 12, 2025

Copy link
Copy Markdown
ContributorAuthor

Hi @0xRAG, I rebased to account for your changes in #818.

I still can't get the swap API to work with smart accounts, it throws this error APIError: EVM account with given address not found.

Also tried to use createSwapQuote + sign permit2 + sendTransaction instead of the all-in-one pattern.
Same issue but could narrow it down to happen in the sign permit2 step:

const signature = await walletProvider.smartAccount.signTypedData({ network: cdpNetwork as any, domain: swapQuote.permit2.eip712.domain, types: swapQuote.permit2.eip712.types, primaryType: swapQuote.permit2.eip712.primaryType, message: swapQuote.permit2.eip712.message, })

Any ideas how solve this?

@0xRAG

Copy link
Copy Markdown
Contributor

Hey @phdargen, I think this may happen when the smart wallet owner is not a CDP wallet, since walletProvider.smartAccount.signTypedData ends up calling the CDP API with the owner address.

Could you confirm by trying again with a new smart wallet whose owner is a CDP server wallet?

We may need to add a disclaimer that swap only works with smart accounts owned by a CDP wallet.

@github-actionsgithub-actionsBot added wallet provider New wallet provider example New example agent labels Aug 19, 2025
@phdargen
phdargenforce-pushed the swap-api-ts branch 3 times, most recently from 3dca4bb to f7332e9CompareAugust 19, 2025 23:07
@phdargen
phdargen marked this pull request as ready for review August 19, 2025 23:16
@phdargen

Copy link
Copy Markdown
ContributorAuthor

Hey @phdargen, I think this may happen when the smart wallet owner is not a CDP wallet, since walletProvider.smartAccount.signTypedData ends up calling the CDP API with the owner address.

Could you confirm by trying again with a new smart wallet whose owner is a CDP server wallet?

We may need to add a disclaimer that swap only works with smart accounts owned by a CDP wallet.

Hi @0xRAG, thanks that was indeed the problem!

I thus changed the chatbot and next template to create a CDP server wallet as owner by default and the swap action throws a descriptive error when its called with a LocalAccount as owner

? walletProvider.smartAccount
: await walletProvider.getClient().evm.getAccount({
address: walletProvider.getAddress() as Hex,
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given we are getting and later using the account / smart account via .swap, I think this action is better suited to live in the cdpEvmWalletActionProvider / cdpSmartWalletActionProvider. The get_swap_price action is provider-agnostic so that one is fine to remain, although may be worth moving along with swap just to reduce potential confusion on why the actions are on different providers. LMK your thoughts, also cc @CarsonRoscoe

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah good point, this would avoid the messy conditional logic and type casts. Will refactor

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on the codebase today, I am in agreement.

However, this is making me think about a refactor. There seems to be an association between wallet providers & action providers, and I think that's causing some confusion here.

Imagine a world where wallet provider files exported their own action providers, or used the CreateAction() decorator on their own functions, allowing them to declare tools that we can auto-register when a wallet provider is used. This would create a clear separation of concerns between what actions are needed for a specific wallet, and what actions are coming from a shared API like the CDP API. It also would reduce a friction point in the devx, which is knowing that, for example, using the CdpEvmWalletProvider means importing both WalletActionProvider and CdpEvmWalletActionProvider. It then allows CdpApiActionProvider to remain pure and focus only on features that any wallet using CDP can leverage such as get_swap_price and faucet.

@CarsonRoscoe
CarsonRoscoe merged commit 406f994 into coinbase:mainAug 21, 2025
26 checks passed
@phdargenphdargen mentioned this pull request Sep 5, 2025
2 tasks
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

action providerNew action providerdocumentationImprovements or additions to documentationexampleNew example agenttypescriptwallet providerNew wallet provider

Development

Successfully merging this pull request may close these issues.

5 participants

@phdargen@cb-heimdall@0xRAG@CarsonRoscoe@carsonroscoe-cb
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix:swap api - #817

Merged
CarsonRoscoe merged 13 commits into
coinbase:mainfrom
phdargen:swap-api-ts
Aug 21, 2025
Merged

fix:swap api#817
CarsonRoscoe merged 13 commits into
coinbase:mainfrom
phdargen:swap-api-ts

Conversation

@phdargen

@phdargenphdargen commented Aug 8, 2025

Copy link
Copy Markdown
Contributor

Description

Fixes various issues with the swap API implementation in CdpApiActionProvider

  • bumps cdp-sdk version
  • throws error for unsupported networks
  • changes unsupported assetId to tokenContract for input arguments
  • adds slippage config
  • changes fromAmount to human readable format
  • handles permit2 approval if needed
  • adds get swap quote action to check price before executing
  • checks if swap failed
  • retrieves correct account for cdpSmartWallet
  • throws error when owner account is not a CDP server wallet

Other changes:

Tests

Chatbot: typescript/examples/langchain-cdp-chatbot/chatbot.ts
Network: Base mainnet
Prompt: price quote for 1 eth to usdc (0x833589fcd6edb6e08f4c7c32d4f71b54bda02913)
-------------------
{"success":true,"fromAmount":"1","fromTokenName":"ETH","fromToken":"0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee","toAmount":"3815.31087","minToAmount":"3777.11957","toTokenName":"USD Coin","toToken":"0x833589fcd6edb6e08f4c7c32d4f71b54bda02913","slippageBps":100,"liquidityAvailable":true,"balanceEnough":false,"allowanceEnough":true,"priceOfBuyTokenInSellToken":"0.0002621018402099434","priceOfSellTokenInBuyToken":"3815.31087"}
-------------------
The price quote for swapping 1 ETH to USDC (0x833589fcd6edb6e08f4c7c32d4f71b54bda02913) is approximately **3815.31 USDC**. - Minimum amount you can receive: **3777.12 USDC**
- Price of 1 ETH in USDC: **3815.31 USDC**
- Price of 1 USDC in ETH: **0.0002621 ETH**
-------------------
Prompt: swap 0.00001 eth to USDC (0x833589fcd6edb6e08f4c7c32d4f71b54bda02913) -------------------
{"success":true,"transactionHash":{"transactionHash":"0x96264f7ab5eac524ab658b61e78d31187cbce3e3442263cdef18d592b8130116"},"fromAmount":"0.00001","fromTokenName":"ETH","fromToken":"0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee","toAmount":"0.038313","minToAmount":"0.037929","toTokenName":"USD Coin","toToken":"0x833589fcd6edb6e08f4c7c32d4f71b54bda02913","slippageBps":100,"network":"base-mainnet"}
-------------------
The swap of **0.00001 ETH** to USDC (0x833589fcd6edb6e08f4c7c32d4f71b54bda02913) was successful!
- Amount received: **0.038313 USDC**
- Minimum amount you can receive: **0.037929 USDC**
- Transaction Hash: **[0x96264f7ab5eac524ab658b61e78d31187cbce3e3442263cdef18d592b8130116](https://explorer.base.org/tx/0x96264f7ab5eac524ab658b61e78d31187cbce3e3442263cdef18d592b8130116)**
-------------------
Prompt: swap 0.01 usdc to cbBTC 0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf -------------------
{"success":true,"approvalTxHash":"0x28cbe8be28a3d192ede5a870a4778363a5302e55aa60f0ac38eeeb4c09b7e89e","transactionHash":{"transactionHash":"0xccb181ce15d17d108e08be721d7c13bbcb35a959fd8508831c8fa779f3a9ac4a"},"fromAmount":"0.01","fromTokenName":"USD Coin","fromToken":"0x833589fcd6edb6e08f4c7c32d4f71b54bda02913","toAmount":"0.00000009","minToAmount":"0.00000009","toTokenName":"Coinbase Wrapped BTC","toToken":"0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf","slippageBps":100,"network":"base-mainnet"}
-------------------
The swap of **0.01 USDC** to Coinbase Wrapped BTC (0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf) was successful!
- Amount received: **0.00000009 CBTC**
- Minimum amount you can receive: **0.00000009 CBTC**
- Transaction Hash: **[0xccb181ce15d17d108e08be721d7c13bbcb35a959fd8508831c8fa779f3a9ac4a](https://explorer.base.org/tx/0xccb181ce15d17d108e08be721d7c13bbcb35a959fd8508831c8fa779f3a9ac4a)**
Chatbot: typescript/examples/langchain-smart-wallet-chatbot/chatbot.ts
Network: Base mainnet
Setup: Funded with erc20 token, no ETH, paymaster enabled
Prompt: swap 1 token 0xD769d56f479E9E72a77bB1523e866A33098Feec5 to usdc (0x833589fcd6edb6e08f4c7c32d4f71b54bda02913) -------------------
{"success":true,"approvalTxHash":"0x37a0fd65e87bbbdd88d7e7742b0f2fd51f7c8c73e6c1ff87e833fa4689a6000b","transactionHash":"0x694eb62df1f62f97b47d3d7f4397d9be9d50e7714c4afed49f9a51776cb479b1","fromAmount":"1","fromTokenName":"Base is for everyone","fromToken":"0xD769d56f479E9E72a77bB1523e866A33098Feec5","toAmount":"0.003855","minToAmount":"0.003816","toTokenName":"USD Coin","toToken":"0x833589fcd6edb6e08f4c7c32d4f71b54bda02913","slippageBps":100,"network":"base-mainnet"}
-------------------
The swap has been successfully executed!
- **Swapped**: 1 Base is for everyone
- **Received**: Approximately 0.00386 USDC
- **Transaction Hash**: [0x694eb62df1f62f97b47d3d7f4397d9be9d50e7714c4afed49f9a51776cb479b1](https://etherscan.io/tx/0x694eb62df1f62f97b47d3d7f4397d9be9d50e7714c4afed49f9a51776cb479b1)

Checklist

A couple of things to include in your PR for completeness:

  • Added documentation to all relevant README.md files
  • Added a changelog entry

@cb-heimdall

cb-heimdall commented Aug 8, 2025

Copy link
Copy Markdown

✅ Heimdall Review Status

RequirementStatusMore Info
Reviews1/1
Denominator calculation
Show calculation
1 if user is bot0
1 if user is external0
2 if repo is sensitive0
From .codeflow.yml1
Additional review requirements
Show calculation
Max0
0
From CODEOWNERS0
Global minimum0
Max 1
1
1 if commit is unverified0
Sum1

@phdargen
phdargen marked this pull request as draft August 8, 2025 14:42
@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation action provider New action provider wallet provider New wallet provider typescript labels Aug 8, 2025
@github-actionsgithub-actionsBot removed the wallet provider New wallet provider label Aug 12, 2025
@phdargen

phdargen commented Aug 12, 2025

Copy link
Copy Markdown
ContributorAuthor

Hi @0xRAG, I rebased to account for your changes in #818.

I still can't get the swap API to work with smart accounts, it throws this error APIError: EVM account with given address not found.

Also tried to use createSwapQuote + sign permit2 + sendTransaction instead of the all-in-one pattern.
Same issue but could narrow it down to happen in the sign permit2 step:

const signature = await walletProvider.smartAccount.signTypedData({ network: cdpNetwork as any, domain: swapQuote.permit2.eip712.domain, types: swapQuote.permit2.eip712.types, primaryType: swapQuote.permit2.eip712.primaryType, message: swapQuote.permit2.eip712.message, })

Any ideas how solve this?

@0xRAG

Copy link
Copy Markdown
Contributor

Hey @phdargen, I think this may happen when the smart wallet owner is not a CDP wallet, since walletProvider.smartAccount.signTypedData ends up calling the CDP API with the owner address.

Could you confirm by trying again with a new smart wallet whose owner is a CDP server wallet?

We may need to add a disclaimer that swap only works with smart accounts owned by a CDP wallet.

@github-actionsgithub-actionsBot added wallet provider New wallet provider example New example agent labels Aug 19, 2025
@phdargen
phdargenforce-pushed the swap-api-ts branch 3 times, most recently from 3dca4bb to f7332e9CompareAugust 19, 2025 23:07
@phdargen
phdargen marked this pull request as ready for review August 19, 2025 23:16
@phdargen

Copy link
Copy Markdown
ContributorAuthor

Hey @phdargen, I think this may happen when the smart wallet owner is not a CDP wallet, since walletProvider.smartAccount.signTypedData ends up calling the CDP API with the owner address.

Could you confirm by trying again with a new smart wallet whose owner is a CDP server wallet?

We may need to add a disclaimer that swap only works with smart accounts owned by a CDP wallet.

Hi @0xRAG, thanks that was indeed the problem!

I thus changed the chatbot and next template to create a CDP server wallet as owner by default and the swap action throws a descriptive error when its called with a LocalAccount as owner

? walletProvider.smartAccount
: await walletProvider.getClient().evm.getAccount({
address: walletProvider.getAddress() as Hex,
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given we are getting and later using the account / smart account via .swap, I think this action is better suited to live in the cdpEvmWalletActionProvider / cdpSmartWalletActionProvider. The get_swap_price action is provider-agnostic so that one is fine to remain, although may be worth moving along with swap just to reduce potential confusion on why the actions are on different providers. LMK your thoughts, also cc @CarsonRoscoe

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah good point, this would avoid the messy conditional logic and type casts. Will refactor

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on the codebase today, I am in agreement.

However, this is making me think about a refactor. There seems to be an association between wallet providers & action providers, and I think that's causing some confusion here.

Imagine a world where wallet provider files exported their own action providers, or used the CreateAction() decorator on their own functions, allowing them to declare tools that we can auto-register when a wallet provider is used. This would create a clear separation of concerns between what actions are needed for a specific wallet, and what actions are coming from a shared API like the CDP API. It also would reduce a friction point in the devx, which is knowing that, for example, using the CdpEvmWalletProvider means importing both WalletActionProvider and CdpEvmWalletActionProvider. It then allows CdpApiActionProvider to remain pure and focus only on features that any wallet using CDP can leverage such as get_swap_price and faucet.

@CarsonRoscoe
CarsonRoscoe merged commit 406f994 into coinbase:mainAug 21, 2025
26 checks passed
@phdargenphdargen mentioned this pull request Sep 5, 2025
2 tasks
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

action providerNew action providerdocumentationImprovements or additions to documentationexampleNew example agenttypescriptwallet providerNew wallet provider

Development

Successfully merging this pull request may close these issues.

5 participants

@phdargen@cb-heimdall@0xRAG@CarsonRoscoe@carsonroscoe-cb
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

fix:swap api - #817

Merged
CarsonRoscoe merged 13 commits into
coinbase:mainfrom
phdargen:swap-api-ts
Aug 21, 2025
Merged

fix:swap api#817
CarsonRoscoe merged 13 commits into
coinbase:mainfrom
phdargen:swap-api-ts

Conversation

@phdargen

@phdargenphdargen commented Aug 8, 2025

Copy link
Copy Markdown
Contributor

Description

Fixes various issues with the swap API implementation in CdpApiActionProvider

  • bumps cdp-sdk version
  • throws error for unsupported networks
  • changes unsupported assetId to tokenContract for input arguments
  • adds slippage config
  • changes fromAmount to human readable format
  • handles permit2 approval if needed
  • adds get swap quote action to check price before executing
  • checks if swap failed
  • retrieves correct account for cdpSmartWallet
  • throws error when owner account is not a CDP server wallet

Other changes:

Tests

Chatbot: typescript/examples/langchain-cdp-chatbot/chatbot.ts
Network: Base mainnet
Prompt: price quote for 1 eth to usdc (0x833589fcd6edb6e08f4c7c32d4f71b54bda02913)
-------------------
{"success":true,"fromAmount":"1","fromTokenName":"ETH","fromToken":"0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee","toAmount":"3815.31087","minToAmount":"3777.11957","toTokenName":"USD Coin","toToken":"0x833589fcd6edb6e08f4c7c32d4f71b54bda02913","slippageBps":100,"liquidityAvailable":true,"balanceEnough":false,"allowanceEnough":true,"priceOfBuyTokenInSellToken":"0.0002621018402099434","priceOfSellTokenInBuyToken":"3815.31087"}
-------------------
The price quote for swapping 1 ETH to USDC (0x833589fcd6edb6e08f4c7c32d4f71b54bda02913) is approximately **3815.31 USDC**. - Minimum amount you can receive: **3777.12 USDC**
- Price of 1 ETH in USDC: **3815.31 USDC**
- Price of 1 USDC in ETH: **0.0002621 ETH**
-------------------
Prompt: swap 0.00001 eth to USDC (0x833589fcd6edb6e08f4c7c32d4f71b54bda02913) -------------------
{"success":true,"transactionHash":{"transactionHash":"0x96264f7ab5eac524ab658b61e78d31187cbce3e3442263cdef18d592b8130116"},"fromAmount":"0.00001","fromTokenName":"ETH","fromToken":"0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee","toAmount":"0.038313","minToAmount":"0.037929","toTokenName":"USD Coin","toToken":"0x833589fcd6edb6e08f4c7c32d4f71b54bda02913","slippageBps":100,"network":"base-mainnet"}
-------------------
The swap of **0.00001 ETH** to USDC (0x833589fcd6edb6e08f4c7c32d4f71b54bda02913) was successful!
- Amount received: **0.038313 USDC**
- Minimum amount you can receive: **0.037929 USDC**
- Transaction Hash: **[0x96264f7ab5eac524ab658b61e78d31187cbce3e3442263cdef18d592b8130116](https://explorer.base.org/tx/0x96264f7ab5eac524ab658b61e78d31187cbce3e3442263cdef18d592b8130116)**
-------------------
Prompt: swap 0.01 usdc to cbBTC 0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf -------------------
{"success":true,"approvalTxHash":"0x28cbe8be28a3d192ede5a870a4778363a5302e55aa60f0ac38eeeb4c09b7e89e","transactionHash":{"transactionHash":"0xccb181ce15d17d108e08be721d7c13bbcb35a959fd8508831c8fa779f3a9ac4a"},"fromAmount":"0.01","fromTokenName":"USD Coin","fromToken":"0x833589fcd6edb6e08f4c7c32d4f71b54bda02913","toAmount":"0.00000009","minToAmount":"0.00000009","toTokenName":"Coinbase Wrapped BTC","toToken":"0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf","slippageBps":100,"network":"base-mainnet"}
-------------------
The swap of **0.01 USDC** to Coinbase Wrapped BTC (0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf) was successful!
- Amount received: **0.00000009 CBTC**
- Minimum amount you can receive: **0.00000009 CBTC**
- Transaction Hash: **[0xccb181ce15d17d108e08be721d7c13bbcb35a959fd8508831c8fa779f3a9ac4a](https://explorer.base.org/tx/0xccb181ce15d17d108e08be721d7c13bbcb35a959fd8508831c8fa779f3a9ac4a)**
Chatbot: typescript/examples/langchain-smart-wallet-chatbot/chatbot.ts
Network: Base mainnet
Setup: Funded with erc20 token, no ETH, paymaster enabled
Prompt: swap 1 token 0xD769d56f479E9E72a77bB1523e866A33098Feec5 to usdc (0x833589fcd6edb6e08f4c7c32d4f71b54bda02913) -------------------
{"success":true,"approvalTxHash":"0x37a0fd65e87bbbdd88d7e7742b0f2fd51f7c8c73e6c1ff87e833fa4689a6000b","transactionHash":"0x694eb62df1f62f97b47d3d7f4397d9be9d50e7714c4afed49f9a51776cb479b1","fromAmount":"1","fromTokenName":"Base is for everyone","fromToken":"0xD769d56f479E9E72a77bB1523e866A33098Feec5","toAmount":"0.003855","minToAmount":"0.003816","toTokenName":"USD Coin","toToken":"0x833589fcd6edb6e08f4c7c32d4f71b54bda02913","slippageBps":100,"network":"base-mainnet"}
-------------------
The swap has been successfully executed!
- **Swapped**: 1 Base is for everyone
- **Received**: Approximately 0.00386 USDC
- **Transaction Hash**: [0x694eb62df1f62f97b47d3d7f4397d9be9d50e7714c4afed49f9a51776cb479b1](https://etherscan.io/tx/0x694eb62df1f62f97b47d3d7f4397d9be9d50e7714c4afed49f9a51776cb479b1)

Checklist

A couple of things to include in your PR for completeness:

  • Added documentation to all relevant README.md files
  • Added a changelog entry

@cb-heimdall

cb-heimdall commented Aug 8, 2025

Copy link
Copy Markdown

✅ Heimdall Review Status

RequirementStatusMore Info
Reviews1/1
Denominator calculation
Show calculation
1 if user is bot0
1 if user is external0
2 if repo is sensitive0
From .codeflow.yml1
Additional review requirements
Show calculation
Max0
0
From CODEOWNERS0
Global minimum0
Max 1
1
1 if commit is unverified0
Sum1

@phdargen
phdargen marked this pull request as draft August 8, 2025 14:42
@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation action provider New action provider wallet provider New wallet provider typescript labels Aug 8, 2025
@github-actionsgithub-actionsBot removed the wallet provider New wallet provider label Aug 12, 2025
@phdargen

phdargen commented Aug 12, 2025

Copy link
Copy Markdown
ContributorAuthor

Hi @0xRAG, I rebased to account for your changes in #818.

I still can't get the swap API to work with smart accounts, it throws this error APIError: EVM account with given address not found.

Also tried to use createSwapQuote + sign permit2 + sendTransaction instead of the all-in-one pattern.
Same issue but could narrow it down to happen in the sign permit2 step:

const signature = await walletProvider.smartAccount.signTypedData({ network: cdpNetwork as any, domain: swapQuote.permit2.eip712.domain, types: swapQuote.permit2.eip712.types, primaryType: swapQuote.permit2.eip712.primaryType, message: swapQuote.permit2.eip712.message, })

Any ideas how solve this?

@0xRAG

Copy link
Copy Markdown
Contributor

Hey @phdargen, I think this may happen when the smart wallet owner is not a CDP wallet, since walletProvider.smartAccount.signTypedData ends up calling the CDP API with the owner address.

Could you confirm by trying again with a new smart wallet whose owner is a CDP server wallet?

We may need to add a disclaimer that swap only works with smart accounts owned by a CDP wallet.

@github-actionsgithub-actionsBot added wallet provider New wallet provider example New example agent labels Aug 19, 2025
@phdargen
phdargenforce-pushed the swap-api-ts branch 3 times, most recently from 3dca4bb to f7332e9CompareAugust 19, 2025 23:07
@phdargen
phdargen marked this pull request as ready for review August 19, 2025 23:16
@phdargen

Copy link
Copy Markdown
ContributorAuthor

Hey @phdargen, I think this may happen when the smart wallet owner is not a CDP wallet, since walletProvider.smartAccount.signTypedData ends up calling the CDP API with the owner address.

Could you confirm by trying again with a new smart wallet whose owner is a CDP server wallet?

We may need to add a disclaimer that swap only works with smart accounts owned by a CDP wallet.

Hi @0xRAG, thanks that was indeed the problem!

I thus changed the chatbot and next template to create a CDP server wallet as owner by default and the swap action throws a descriptive error when its called with a LocalAccount as owner

? walletProvider.smartAccount
: await walletProvider.getClient().evm.getAccount({
address: walletProvider.getAddress() as Hex,
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given we are getting and later using the account / smart account via .swap, I think this action is better suited to live in the cdpEvmWalletActionProvider / cdpSmartWalletActionProvider. The get_swap_price action is provider-agnostic so that one is fine to remain, although may be worth moving along with swap just to reduce potential confusion on why the actions are on different providers. LMK your thoughts, also cc @CarsonRoscoe

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah good point, this would avoid the messy conditional logic and type casts. Will refactor

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on the codebase today, I am in agreement.

However, this is making me think about a refactor. There seems to be an association between wallet providers & action providers, and I think that's causing some confusion here.

Imagine a world where wallet provider files exported their own action providers, or used the CreateAction() decorator on their own functions, allowing them to declare tools that we can auto-register when a wallet provider is used. This would create a clear separation of concerns between what actions are needed for a specific wallet, and what actions are coming from a shared API like the CDP API. It also would reduce a friction point in the devx, which is knowing that, for example, using the CdpEvmWalletProvider means importing both WalletActionProvider and CdpEvmWalletActionProvider. It then allows CdpApiActionProvider to remain pure and focus only on features that any wallet using CDP can leverage such as get_swap_price and faucet.

@CarsonRoscoe
CarsonRoscoe merged commit 406f994 into coinbase:mainAug 21, 2025
26 checks passed
@phdargenphdargen mentioned this pull request Sep 5, 2025
2 tasks
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

action providerNew action providerdocumentationImprovements or additions to documentationexampleNew example agenttypescriptwallet providerNew wallet provider

Development

Successfully merging this pull request may close these issues.

5 participants

@phdargen@cb-heimdall@0xRAG@CarsonRoscoe@carsonroscoe-cb