feat: superfluid typescript action provider - #823

Merged
CarsonRoscoe merged 1 commit into
coinbase:mainfrom
gtspencer:feat/superfluid-typescript
Aug 21, 2025
Merged

feat: superfluid typescript action provider#823
CarsonRoscoe merged 1 commit into
coinbase:mainfrom
gtspencer:feat/superfluid-typescript

Conversation

@gtspencer

@gtspencergtspencer commented Aug 12, 2025

Copy link
Copy Markdown
Contributor

Link to Issue

Description

Superfluid action providers, broken up by functionality (create stream, create pool, update stream, update pool). We experienced need for Superfluid functionality in our agent and decided to include the implementation in the core repo.

Qualified Impact

Very little qualified impact due to the self contained nature of the action. Only dependency is the graphql-request package, which can be omitted in the event of an error.

Pool Actions:

  • Create pool
  • Update member units

Stream Actions:

  • Create stream
  • Update stream
  • Close stream

Query Actions (easily extensible for other queries):

  • Query open streams

Create Super Token

  • Creates a SuperToken (wrapper) implementation for an underlying ERC20

Wrap Token

  • Wraps an ERC20 into its Super Token implementation

Tests

Chatbot: langchain-cdp-chatbot
Network: Base Mainnet
Setup: Add relevant actions (SuperfluidStreamActionProvider, etc.) to the chatbot
Create Super Token
Prompt: Can you create a super token for this erc20: 0xe8E55A847Bb446d967ef92F4580162fb8f2d3F38
-------------------
0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40
Created super token for 0xe8E55A847Bb446d967ef92F4580162fb8f2d3F38. Super token address at 0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40 Transaction hash: 0xa9ea729bcbe30e3bb8610f9adf91fc4f4162e4cda5c33273c1f0d830388492da
-------------------
The super token has been successfully created for the ERC20 address `0xe8E55A847Bb446d967ef92F4580162fb8f2d3F38`. - **Super token address**: `0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40`
- **Transaction hash**: `0xa9ea729bcbe30e3bb8610f9adf91fc4f4162e4cda5c33273c1f0d830388492da`
Let me know if you need further assistance!
-------------------
Wrap Super Token
Prompt: Can you wrap 1000 of this erc20 0xe8E55A847Bb446d967ef92F4580162fb8f2d3F38 into this super token 0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40 on base mainnet
-------------------
Wallet Details:
- Provider: cdp_evm_wallet_provider
- Address: 0x77841ee4399EFfc9b515c09cf239D784392F897F
- Network:
* Protocol Family: evm
* Network ID: base-mainnet
* Chain ID: 8453
- Native Balance: 37507762138705 WEI
-------------------
-------------------
Wrapped 1000 of token 0xe8E55A847Bb446d967ef92F4580162fb8f2d3F38 as a SuperToken. Transaction hash: 0xd073bb4a85cbc79dc55f9996c500d141a257ccc2e173cce8f09017ec53b7cce6
-------------------
Successfully wrapped 1000 of the ERC20 token (0xe8E55A847Bb446d967ef92F4580162fb8f2d3F38) into the Super token. Transaction hash: **0xd073bb4a85cbc79dc55f9996c500d141a257ccc2e173cce8f09017ec53b7cce6**. If you have any more requests or questions, feel free to ask!
-------------------
Create Stream
Prompt: Can you create a superfluid stream of the super token 0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40 at a rate of 100 to address 0x8F4359D1C2166452b5e7a02742D6fe9ca5448FDe
-------------------
Created stream of token 0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40 to 0x8F4359D1C2166452b5e7a02742D6fe9ca5448FDe at a rate of 100. The link to the stream is https://app.superfluid.finance/stream/base-mainnet/0x77841ee4399EFfc9b515c09cf239D784392F897F-0x8F4359D1C2166452b5e7a02742D6fe9ca5448FDe-0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40. Transaction hash: 0xe108156834b91a52ab431e92095e171c93800d02f590271c5364eac26561dda3
-------------------
The Superfluid stream has been successfully created! You are streaming the SuperToken (0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40) at a rate of 100 to the address **0x8F4359D1C2166452b5e7a02742D6fe9ca5448FDe**.
Here is the transaction hash for your reference: **0xe108156834b91a52ab431e92095e171c93800d02f590271c5364eac26561dda3**.
You can view the stream details using the following link: [View Stream](https://app.superfluid.finance/stream/base-mainnet/0x77841ee4399EFfc9b515c09cf239D784392F897F-0x8F4359D1C2166452b5e7a02742D6fe9ca5448FDe-0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40).
-------------------
Pools
Prompt: Can you create a pool of the token 0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40
-------------------
Created pool of token 0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40 at 0x8651059749637291C4175d539d4eA62340aFBcAD. Transaction hash: 0x0b717b24504452ebf18a3f707b882f247a25cff1d7302d21f71916f21b9379a5
-------------------
The pool for the token **0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40** has been created successfully. - **Pool Address:** 0x8651059749637291C4175d539d4eA62340aFBcAD
- **Transaction Hash:** [0x0b717b24504452ebf18a3f707b882f247a25cff1d7302d21f71916f21b9379a5](https://etherscan.io/tx/0x0b717b24504452ebf18a3f707b882f247a25cff1d7302d21f71916f21b9379a5)
If you need any further assistance, feel free to ask!
-------------------

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 12, 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

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation action provider New action provider typescript labels Aug 12, 2025
@phdargen

Copy link
Copy Markdown
Contributor

Hi @gtspencer, thanks for the contribution! Looks pretty good, a few initial comments below before I will do some manual testing

@phdargen

Copy link
Copy Markdown
Contributor

We require all commits to be verified, unfortunately your last one is not signed. Please rebase against main and resign the commit

@phdargen

Copy link
Copy Markdown
Contributor

Could you please add more comprehensive test output to the PR description, ideally prompts for all new actions?

@@ -0,0 +1,5 @@
---
"@coinbase/agentkit": minor

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.

minor -> patch

export * from "./schemas";
export * from "./superfluidStreamActionProvider";
export * from "./superfluidPoolActionProvider";
export * from "./superfluidQueryActionProvider";

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.

might be good to add a 'SuperfluidActionProvider' wrapper combining the 3 action providers for convenience

export const SuperfluidCreateStreamSchema = z
.object({
erc20TokenAddress: z.string().describe("The ERC20 token to start or update streaming"),
chainId: z.string().describe("The EVM chain ID on which the ERC20 is deployed"),

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.

it appears chainId is unused

).args;

if (success) {
return `Created pool of token ${args.erc20TokenAddress} at ${poolAddress}`;

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.

please add txHashs in all returns

* @param network - The network to check.
* @returns True if the Superfluid action provider supports the network, false otherwise.
*/
supportsNetwork = (network: Network) => network.protocolFamily === "evm";

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.

should be restricted to base, base-sepolia here

*/
export const SuperfluidDeleteStreamSchema = z
.object({
erc20TokenAddress: z.string().describe("The ERC20 token to start streaming"),

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.

suggest to add .regex(/^0x[a-fA-F0-9]{40}$/, "Invalid Ethereum address format") to all addresses


return `Updated member units of pool ${args.poolAddress} for member ${args.recipientAddress}, with new member units ${args.units}`;
} catch (error) {
return `Error creating Superfluid pool: ${error}`;

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.

creating -> updating


return `Current outflows are ${JSON.stringify(activeOutflows)}`;
} catch (error) {
return `Error creating Superfluid pool: ${error}`;

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.

creating -> querying


return `Updated stream of token ${args.erc20TokenAddress} to ${args.recipientAddress} at a rate of ${args.flowRate}`;
} catch (error) {
return `Error creating Superfluid stream: ${error}`;

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.

creating -> updating


return `Stopped stream of token ${args.erc20TokenAddress} to ${args.recipientAddress}`;
} catch (error) {
return `Error creating Superfluid stream: ${error}`;

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.

creating -> stopping

@phdargen

Copy link
Copy Markdown
Contributor

Main concern is Superfluid wrapped tokens vs erc20.

It is my understanding that you cant stream a erc20 token directly but need to supply a superfluid wrapped token.
At the very least, this should clearly be stated in the README and action descriptions.

Ideally, the createStream action would first wrap a given erc20 token before calling createFlow or alternatively a new action added that does the wrapping.

@gtspencer
gtspencerforce-pushed the feat/superfluid-typescript branch from 1d03ef4 to e3f5f84CompareAugust 14, 2025 16:06
@gtspencer

Copy link
Copy Markdown
ContributorAuthor

hey @phdargen thanks for the review! just addressed all your concerns:

  • fixed minor issues involving hash response, networks, changeset type, and verbiage
  • added a superfluid wrapper and Super Token creator action. This way, if a stream action fails, the agent knows it may need to create the super token first and will prompt the user with that action
  • Signed single commit

Ready for a new review at your leisure!

@gtspencergtspencer changed the title Feat/superfluid typescriptfeat: superfluid typescript action providerAug 14, 2025
@gtspencer
gtspencerforce-pushed the feat/superfluid-typescript branch from 7487dfd to 716b63dCompareAugust 15, 2025 15:29
},
] as const;

export const SuperTokenFactoryAddress = "0xe20B9a38E0c96F61d1bA6b42a61512D56Fea1Eb3" as const;

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.

superTokenFactory address is different on base-sepolia (0x7447E94Dfe3d804a9f46Bf12838d467c912C8F6C), see https://docs.superfluid.org/docs/protocol/contract-addresses. Would be good if users could try the workflow on testnet

Comment on lines +1 to +6
export * from "./schemas";
export * from "./superfluidStreamActionProvider";
export * from "./superfluidPoolActionProvider";
export * from "./superfluidQueryActionProvider";
export * from "./superfluidWrapperActionProvider";
export * from "./superfluidSuperTokenCreatorActionProvider";

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.

Please move exports back to index.ts.

Instead add the following here that aggregates all superfluid actions a for a single import in eg chatbot files:

Suggested change
export*from"./schemas";
export*from"./superfluidStreamActionProvider";
export*from"./superfluidPoolActionProvider";
export*from"./superfluidQueryActionProvider";
export*from"./superfluidWrapperActionProvider";
export*from"./superfluidSuperTokenCreatorActionProvider";
import{ActionProvider}from"../actionProvider";
import{EvmWalletProvider}from"../../wallet-providers";
import{Network}from"../../network";
import{SuperfluidStreamActionProvider}from"./superfluidStreamActionProvider";
import{SuperfluidPoolActionProvider}from"./superfluidPoolActionProvider";
import{SuperfluidQueryActionProvider}from"./superfluidQueryActionProvider";
import{SuperfluidWrapperActionProvider}from"./superfluidWrapperActionProvider";
import{SuperfluidSuperTokenCreatorActionProvider}from"./superfluidSuperTokenCreatorActionProvider";
/**
*SuperfluidActionProvideraggregatesallSuperfluid-relatedactionsintoasingleprovider.
*/
exportclassSuperfluidActionProviderextendsActionProvider<EvmWalletProvider>{
constructor(){
super("superfluid",[
newSuperfluidStreamActionProvider(),
newSuperfluidPoolActionProvider(),
newSuperfluidQueryActionProvider(),
newSuperfluidWrapperActionProvider(),
newSuperfluidSuperTokenCreatorActionProvider(),
]);
}
/**
*SupportsBasemainnetandBasesepolialiketheunderlyingSuperfluidproviders.
*/
supportsNetwork=(network: Network)=>
network.networkId==="base-mainnet"||network.networkId==="base-sepolia";
}
exportconstsuperfluidActionProvider=()=>newSuperfluidActionProvider();


return `Created super token for ${args.erc20TokenAddress}. Super token address at ${superTokenAddress} Transaction hash: ${createSuperTokenHash}`;
} catch (error) {
return `Error creating Superfluid stream: ${error}`;

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.

stream -> token

Comment on lines +68 to +71
const publicClient = createPublicClient({
chain: NETWORK_ID_TO_VIEM_CHAIN[walletProvider.getNetwork().networkId || "base-mainnet"],
transport: http(),
});

// We don't get contract call results, so we need to simulate to get the pool address
const { result } = await publicClient.simulateContract({

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.

Following #824, please remove createPublicClient and use walletProvider.getPublicClient().simulateContract instead

@@ -0,0 +1,187 @@
# Superfluid Action Provider

This directory contains the implementation for Superfluid, broken up by functionality:

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.

Please add link to docs: https://docs.superfluid.org/

@phdargen

Copy link
Copy Markdown
Contributor

Please also add an entry in agentkit/README (https://github.com/coinbase/agentkit/blob/main/typescript/agentkit/README.md), should follow alphabetic order

name: "wrap_token",
description: `
This tool will directly wrap an amount of ERC20 tokens into its corresponding Super token.
The user must provide the erc20 address, and the super token address. This will be done on Base Mainnet

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.

remove "This will be done on Base Mainnet"

* @returns A JSON string containing the account details or error message
*/
@CreateAction({
name: "wrap_token",

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.

wrap_token -> wrap_superfluid_token

args: [],
});

const amount = parseUnits(String(args.wrapAmount), Number(decimals));

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.

This didnt work well in my tests, the LLM always confused the units.

Try rephrasing the action description: "The user must provide the erc20 address, and the super token address. " -> " It takes the following inputs:

  • erc20TokenAddress: The address of the ERC20 token to wrap
  • superTokenAddress: The address of the Super token
  • wrapAmount: The amount of tokens to wrap in whole units (e.g. 1.5 WETH, 10 USDC)
    Use wrapAmount units exactly as provided, do not convert to wei or any other units.
    "

and in shema: "The amount of token to wrap. This should be in units of token (and will not take decimals into account)" -> "The amount of tokens to wrap in whole units (e.g. 1.5 WETH, 10 USDC)"

Comment on lines +8 to +19
erc20TokenAddress: z
.string()
.regex(/^0x[a-fA-F0-9]{40}$/, "Invalid Ethereum address format")
.describe("The ERC20 Super token to start or update streaming"),
recipientAddress: z
.string()
.regex(/^0x[a-fA-F0-9]{40}$/, "Invalid Ethereum address format")
.describe("The EVM address to stream the token to."),
flowRate: z.string().describe("The rate at which the ERC20 is streamed to the recipient"),
})

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.

nit: erc20TokenAddress -> superTokenAddress, same in SuperfluidDeleteStreamSchema and SuperfluidCreatePoolSchema

erc20TokenAddress: z
.string()
.regex(/^0x[a-fA-F0-9]{40}$/, "Invalid Ethereum address format")
.describe("The ERC20 token to start streaming"),

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.

token -> Super token

.string()
.regex(/^0x[a-fA-F0-9]{40}$/, "Invalid Ethereum address format")
.describe("The EVM address to stream the token to."),
flowRate: z.string().describe("The rate at which the ERC20 is streamed to the recipient"),

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.

The flow rate needs additional explanation. Is this tokens per sec/min/h/day/...? Does it consider token decimals?
Please also add this info to action description

const receipt = await walletProvider.waitForTransactionReceipt(createSuperTokenHash);

const superTokenAddress = extractCreatedSuperTokenAddressAbi(receipt);
console.log(superTokenAddress);

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.

please remove all console.log


const receipt = await walletProvider.waitForTransactionReceipt(createSuperTokenHash);

const superTokenAddress = extractCreatedSuperTokenAddressAbi(receipt);

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.

Note: this fails for CdpSmartWallets as receipts doesnt include logs. You can ignore this, will look into it

@phdargen

Copy link
Copy Markdown
Contributor

hey @phdargen thanks for the review! just addressed all your concerns:

  • fixed minor issues involving hash response, networks, changeset type, and verbiage
  • added a superfluid wrapper and Super Token creator action. This way, if a stream action fails, the agent knows it may need to create the super token first and will prompt the user with that action
  • Signed single commit

Ready for a new review at your leisure!

Hi @gtspencer, thanks for the update! I tested it and it works well except for some confusion with the units. Please have a look at my follow-up comments, then we should be able to get this in

@gtspencer
gtspencerforce-pushed the feat/superfluid-typescript branch from f5dfe88 to 7a267b3CompareAugust 20, 2025 18:02
@gtspencer

Copy link
Copy Markdown
ContributorAuthor

Hey @phdargen , thanks for the review! Updated everything, made some changes to the prompts, and added more information on flow rate. Let me know if there's anything else outstanding. Thank you!

});

const createSuperTokenHash = await walletProvider.sendTransaction({
to: SuperTokenFactoryAddress as `0x${string}`,

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.

should be: to: walletProvider.getNetwork().networkId==="base-sepolia" ? SuperTokenFactoryAddress_Base_Sepolia as 0x${string}: SuperTokenFactoryAddress as0x${string},

@phdargen

Copy link
Copy Markdown
Contributor

Hey @phdargen , thanks for the review! Updated everything, made some changes to the prompts, and added more information on flow rate. Let me know if there's anything else outstanding. Thank you!

Hi @gtspencer, looks good! Just one more fix to use the correct contract depending on network

@gtspencer
gtspencerforce-pushed the feat/superfluid-typescript branch from 4513f32 to 86b6ba7CompareAugust 20, 2025 23:04
@gtspencer

Copy link
Copy Markdown
ContributorAuthor

oops! Just updated! @phdargen

@phdargen

Copy link
Copy Markdown
Contributor

This is tested and good to go imo @CarsonRoscoe

@CarsonRoscoe
CarsonRoscoe merged commit 4d0e5d1 into coinbase:mainAug 21, 2025
26 checks passed
@phdargenphdargen mentioned this pull request Sep 4, 2025
7 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 documentationtypescript

Development

Successfully merging this pull request may close these issues.

4 participants

@gtspencer@cb-heimdall@phdargen@CarsonRoscoe
, '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

feat: superfluid typescript action provider - #823

Merged
CarsonRoscoe merged 1 commit into
coinbase:mainfrom
gtspencer:feat/superfluid-typescript
Aug 21, 2025
Merged

feat: superfluid typescript action provider#823
CarsonRoscoe merged 1 commit into
coinbase:mainfrom
gtspencer:feat/superfluid-typescript

Conversation

@gtspencer

@gtspencergtspencer commented Aug 12, 2025

Copy link
Copy Markdown
Contributor

Link to Issue

Description

Superfluid action providers, broken up by functionality (create stream, create pool, update stream, update pool). We experienced need for Superfluid functionality in our agent and decided to include the implementation in the core repo.

Qualified Impact

Very little qualified impact due to the self contained nature of the action. Only dependency is the graphql-request package, which can be omitted in the event of an error.

Pool Actions:

  • Create pool
  • Update member units

Stream Actions:

  • Create stream
  • Update stream
  • Close stream

Query Actions (easily extensible for other queries):

  • Query open streams

Create Super Token

  • Creates a SuperToken (wrapper) implementation for an underlying ERC20

Wrap Token

  • Wraps an ERC20 into its Super Token implementation

Tests

Chatbot: langchain-cdp-chatbot
Network: Base Mainnet
Setup: Add relevant actions (SuperfluidStreamActionProvider, etc.) to the chatbot
Create Super Token
Prompt: Can you create a super token for this erc20: 0xe8E55A847Bb446d967ef92F4580162fb8f2d3F38
-------------------
0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40
Created super token for 0xe8E55A847Bb446d967ef92F4580162fb8f2d3F38. Super token address at 0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40 Transaction hash: 0xa9ea729bcbe30e3bb8610f9adf91fc4f4162e4cda5c33273c1f0d830388492da
-------------------
The super token has been successfully created for the ERC20 address `0xe8E55A847Bb446d967ef92F4580162fb8f2d3F38`. - **Super token address**: `0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40`
- **Transaction hash**: `0xa9ea729bcbe30e3bb8610f9adf91fc4f4162e4cda5c33273c1f0d830388492da`
Let me know if you need further assistance!
-------------------
Wrap Super Token
Prompt: Can you wrap 1000 of this erc20 0xe8E55A847Bb446d967ef92F4580162fb8f2d3F38 into this super token 0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40 on base mainnet
-------------------
Wallet Details:
- Provider: cdp_evm_wallet_provider
- Address: 0x77841ee4399EFfc9b515c09cf239D784392F897F
- Network:
* Protocol Family: evm
* Network ID: base-mainnet
* Chain ID: 8453
- Native Balance: 37507762138705 WEI
-------------------
-------------------
Wrapped 1000 of token 0xe8E55A847Bb446d967ef92F4580162fb8f2d3F38 as a SuperToken. Transaction hash: 0xd073bb4a85cbc79dc55f9996c500d141a257ccc2e173cce8f09017ec53b7cce6
-------------------
Successfully wrapped 1000 of the ERC20 token (0xe8E55A847Bb446d967ef92F4580162fb8f2d3F38) into the Super token. Transaction hash: **0xd073bb4a85cbc79dc55f9996c500d141a257ccc2e173cce8f09017ec53b7cce6**. If you have any more requests or questions, feel free to ask!
-------------------
Create Stream
Prompt: Can you create a superfluid stream of the super token 0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40 at a rate of 100 to address 0x8F4359D1C2166452b5e7a02742D6fe9ca5448FDe
-------------------
Created stream of token 0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40 to 0x8F4359D1C2166452b5e7a02742D6fe9ca5448FDe at a rate of 100. The link to the stream is https://app.superfluid.finance/stream/base-mainnet/0x77841ee4399EFfc9b515c09cf239D784392F897F-0x8F4359D1C2166452b5e7a02742D6fe9ca5448FDe-0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40. Transaction hash: 0xe108156834b91a52ab431e92095e171c93800d02f590271c5364eac26561dda3
-------------------
The Superfluid stream has been successfully created! You are streaming the SuperToken (0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40) at a rate of 100 to the address **0x8F4359D1C2166452b5e7a02742D6fe9ca5448FDe**.
Here is the transaction hash for your reference: **0xe108156834b91a52ab431e92095e171c93800d02f590271c5364eac26561dda3**.
You can view the stream details using the following link: [View Stream](https://app.superfluid.finance/stream/base-mainnet/0x77841ee4399EFfc9b515c09cf239D784392F897F-0x8F4359D1C2166452b5e7a02742D6fe9ca5448FDe-0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40).
-------------------
Pools
Prompt: Can you create a pool of the token 0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40
-------------------
Created pool of token 0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40 at 0x8651059749637291C4175d539d4eA62340aFBcAD. Transaction hash: 0x0b717b24504452ebf18a3f707b882f247a25cff1d7302d21f71916f21b9379a5
-------------------
The pool for the token **0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40** has been created successfully. - **Pool Address:** 0x8651059749637291C4175d539d4eA62340aFBcAD
- **Transaction Hash:** [0x0b717b24504452ebf18a3f707b882f247a25cff1d7302d21f71916f21b9379a5](https://etherscan.io/tx/0x0b717b24504452ebf18a3f707b882f247a25cff1d7302d21f71916f21b9379a5)
If you need any further assistance, feel free to ask!
-------------------

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 12, 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

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation action provider New action provider typescript labels Aug 12, 2025
@phdargen

Copy link
Copy Markdown
Contributor

Hi @gtspencer, thanks for the contribution! Looks pretty good, a few initial comments below before I will do some manual testing

@phdargen

Copy link
Copy Markdown
Contributor

We require all commits to be verified, unfortunately your last one is not signed. Please rebase against main and resign the commit

@phdargen

Copy link
Copy Markdown
Contributor

Could you please add more comprehensive test output to the PR description, ideally prompts for all new actions?

@@ -0,0 +1,5 @@
---
"@coinbase/agentkit": minor

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.

minor -> patch

export * from "./schemas";
export * from "./superfluidStreamActionProvider";
export * from "./superfluidPoolActionProvider";
export * from "./superfluidQueryActionProvider";

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.

might be good to add a 'SuperfluidActionProvider' wrapper combining the 3 action providers for convenience

export const SuperfluidCreateStreamSchema = z
.object({
erc20TokenAddress: z.string().describe("The ERC20 token to start or update streaming"),
chainId: z.string().describe("The EVM chain ID on which the ERC20 is deployed"),

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.

it appears chainId is unused

).args;

if (success) {
return `Created pool of token ${args.erc20TokenAddress} at ${poolAddress}`;

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.

please add txHashs in all returns

* @param network - The network to check.
* @returns True if the Superfluid action provider supports the network, false otherwise.
*/
supportsNetwork = (network: Network) => network.protocolFamily === "evm";

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.

should be restricted to base, base-sepolia here

*/
export const SuperfluidDeleteStreamSchema = z
.object({
erc20TokenAddress: z.string().describe("The ERC20 token to start streaming"),

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.

suggest to add .regex(/^0x[a-fA-F0-9]{40}$/, "Invalid Ethereum address format") to all addresses


return `Updated member units of pool ${args.poolAddress} for member ${args.recipientAddress}, with new member units ${args.units}`;
} catch (error) {
return `Error creating Superfluid pool: ${error}`;

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.

creating -> updating


return `Current outflows are ${JSON.stringify(activeOutflows)}`;
} catch (error) {
return `Error creating Superfluid pool: ${error}`;

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.

creating -> querying


return `Updated stream of token ${args.erc20TokenAddress} to ${args.recipientAddress} at a rate of ${args.flowRate}`;
} catch (error) {
return `Error creating Superfluid stream: ${error}`;

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.

creating -> updating


return `Stopped stream of token ${args.erc20TokenAddress} to ${args.recipientAddress}`;
} catch (error) {
return `Error creating Superfluid stream: ${error}`;

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.

creating -> stopping

@phdargen

Copy link
Copy Markdown
Contributor

Main concern is Superfluid wrapped tokens vs erc20.

It is my understanding that you cant stream a erc20 token directly but need to supply a superfluid wrapped token.
At the very least, this should clearly be stated in the README and action descriptions.

Ideally, the createStream action would first wrap a given erc20 token before calling createFlow or alternatively a new action added that does the wrapping.

@gtspencer
gtspencerforce-pushed the feat/superfluid-typescript branch from 1d03ef4 to e3f5f84CompareAugust 14, 2025 16:06
@gtspencer

Copy link
Copy Markdown
ContributorAuthor

hey @phdargen thanks for the review! just addressed all your concerns:

  • fixed minor issues involving hash response, networks, changeset type, and verbiage
  • added a superfluid wrapper and Super Token creator action. This way, if a stream action fails, the agent knows it may need to create the super token first and will prompt the user with that action
  • Signed single commit

Ready for a new review at your leisure!

@gtspencergtspencer changed the title Feat/superfluid typescriptfeat: superfluid typescript action providerAug 14, 2025
@gtspencer
gtspencerforce-pushed the feat/superfluid-typescript branch from 7487dfd to 716b63dCompareAugust 15, 2025 15:29
},
] as const;

export const SuperTokenFactoryAddress = "0xe20B9a38E0c96F61d1bA6b42a61512D56Fea1Eb3" as const;

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.

superTokenFactory address is different on base-sepolia (0x7447E94Dfe3d804a9f46Bf12838d467c912C8F6C), see https://docs.superfluid.org/docs/protocol/contract-addresses. Would be good if users could try the workflow on testnet

Comment on lines +1 to +6
export * from "./schemas";
export * from "./superfluidStreamActionProvider";
export * from "./superfluidPoolActionProvider";
export * from "./superfluidQueryActionProvider";
export * from "./superfluidWrapperActionProvider";
export * from "./superfluidSuperTokenCreatorActionProvider";

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.

Please move exports back to index.ts.

Instead add the following here that aggregates all superfluid actions a for a single import in eg chatbot files:

Suggested change
export*from"./schemas";
export*from"./superfluidStreamActionProvider";
export*from"./superfluidPoolActionProvider";
export*from"./superfluidQueryActionProvider";
export*from"./superfluidWrapperActionProvider";
export*from"./superfluidSuperTokenCreatorActionProvider";
import{ActionProvider}from"../actionProvider";
import{EvmWalletProvider}from"../../wallet-providers";
import{Network}from"../../network";
import{SuperfluidStreamActionProvider}from"./superfluidStreamActionProvider";
import{SuperfluidPoolActionProvider}from"./superfluidPoolActionProvider";
import{SuperfluidQueryActionProvider}from"./superfluidQueryActionProvider";
import{SuperfluidWrapperActionProvider}from"./superfluidWrapperActionProvider";
import{SuperfluidSuperTokenCreatorActionProvider}from"./superfluidSuperTokenCreatorActionProvider";
/**
*SuperfluidActionProvideraggregatesallSuperfluid-relatedactionsintoasingleprovider.
*/
exportclassSuperfluidActionProviderextendsActionProvider<EvmWalletProvider>{
constructor(){
super("superfluid",[
newSuperfluidStreamActionProvider(),
newSuperfluidPoolActionProvider(),
newSuperfluidQueryActionProvider(),
newSuperfluidWrapperActionProvider(),
newSuperfluidSuperTokenCreatorActionProvider(),
]);
}
/**
*SupportsBasemainnetandBasesepolialiketheunderlyingSuperfluidproviders.
*/
supportsNetwork=(network: Network)=>
network.networkId==="base-mainnet"||network.networkId==="base-sepolia";
}
exportconstsuperfluidActionProvider=()=>newSuperfluidActionProvider();


return `Created super token for ${args.erc20TokenAddress}. Super token address at ${superTokenAddress} Transaction hash: ${createSuperTokenHash}`;
} catch (error) {
return `Error creating Superfluid stream: ${error}`;

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.

stream -> token

Comment on lines +68 to +71
const publicClient = createPublicClient({
chain: NETWORK_ID_TO_VIEM_CHAIN[walletProvider.getNetwork().networkId || "base-mainnet"],
transport: http(),
});

// We don't get contract call results, so we need to simulate to get the pool address
const { result } = await publicClient.simulateContract({

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.

Following #824, please remove createPublicClient and use walletProvider.getPublicClient().simulateContract instead

@@ -0,0 +1,187 @@
# Superfluid Action Provider

This directory contains the implementation for Superfluid, broken up by functionality:

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.

Please add link to docs: https://docs.superfluid.org/

@phdargen

Copy link
Copy Markdown
Contributor

Please also add an entry in agentkit/README (https://github.com/coinbase/agentkit/blob/main/typescript/agentkit/README.md), should follow alphabetic order

name: "wrap_token",
description: `
This tool will directly wrap an amount of ERC20 tokens into its corresponding Super token.
The user must provide the erc20 address, and the super token address. This will be done on Base Mainnet

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.

remove "This will be done on Base Mainnet"

* @returns A JSON string containing the account details or error message
*/
@CreateAction({
name: "wrap_token",

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.

wrap_token -> wrap_superfluid_token

args: [],
});

const amount = parseUnits(String(args.wrapAmount), Number(decimals));

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.

This didnt work well in my tests, the LLM always confused the units.

Try rephrasing the action description: "The user must provide the erc20 address, and the super token address. " -> " It takes the following inputs:

  • erc20TokenAddress: The address of the ERC20 token to wrap
  • superTokenAddress: The address of the Super token
  • wrapAmount: The amount of tokens to wrap in whole units (e.g. 1.5 WETH, 10 USDC)
    Use wrapAmount units exactly as provided, do not convert to wei or any other units.
    "

and in shema: "The amount of token to wrap. This should be in units of token (and will not take decimals into account)" -> "The amount of tokens to wrap in whole units (e.g. 1.5 WETH, 10 USDC)"

Comment on lines +8 to +19
erc20TokenAddress: z
.string()
.regex(/^0x[a-fA-F0-9]{40}$/, "Invalid Ethereum address format")
.describe("The ERC20 Super token to start or update streaming"),
recipientAddress: z
.string()
.regex(/^0x[a-fA-F0-9]{40}$/, "Invalid Ethereum address format")
.describe("The EVM address to stream the token to."),
flowRate: z.string().describe("The rate at which the ERC20 is streamed to the recipient"),
})

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.

nit: erc20TokenAddress -> superTokenAddress, same in SuperfluidDeleteStreamSchema and SuperfluidCreatePoolSchema

erc20TokenAddress: z
.string()
.regex(/^0x[a-fA-F0-9]{40}$/, "Invalid Ethereum address format")
.describe("The ERC20 token to start streaming"),

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.

token -> Super token

.string()
.regex(/^0x[a-fA-F0-9]{40}$/, "Invalid Ethereum address format")
.describe("The EVM address to stream the token to."),
flowRate: z.string().describe("The rate at which the ERC20 is streamed to the recipient"),

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.

The flow rate needs additional explanation. Is this tokens per sec/min/h/day/...? Does it consider token decimals?
Please also add this info to action description

const receipt = await walletProvider.waitForTransactionReceipt(createSuperTokenHash);

const superTokenAddress = extractCreatedSuperTokenAddressAbi(receipt);
console.log(superTokenAddress);

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.

please remove all console.log


const receipt = await walletProvider.waitForTransactionReceipt(createSuperTokenHash);

const superTokenAddress = extractCreatedSuperTokenAddressAbi(receipt);

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.

Note: this fails for CdpSmartWallets as receipts doesnt include logs. You can ignore this, will look into it

@phdargen

Copy link
Copy Markdown
Contributor

hey @phdargen thanks for the review! just addressed all your concerns:

  • fixed minor issues involving hash response, networks, changeset type, and verbiage
  • added a superfluid wrapper and Super Token creator action. This way, if a stream action fails, the agent knows it may need to create the super token first and will prompt the user with that action
  • Signed single commit

Ready for a new review at your leisure!

Hi @gtspencer, thanks for the update! I tested it and it works well except for some confusion with the units. Please have a look at my follow-up comments, then we should be able to get this in

@gtspencer
gtspencerforce-pushed the feat/superfluid-typescript branch from f5dfe88 to 7a267b3CompareAugust 20, 2025 18:02
@gtspencer

Copy link
Copy Markdown
ContributorAuthor

Hey @phdargen , thanks for the review! Updated everything, made some changes to the prompts, and added more information on flow rate. Let me know if there's anything else outstanding. Thank you!

});

const createSuperTokenHash = await walletProvider.sendTransaction({
to: SuperTokenFactoryAddress as `0x${string}`,

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.

should be: to: walletProvider.getNetwork().networkId==="base-sepolia" ? SuperTokenFactoryAddress_Base_Sepolia as 0x${string}: SuperTokenFactoryAddress as0x${string},

@phdargen

Copy link
Copy Markdown
Contributor

Hey @phdargen , thanks for the review! Updated everything, made some changes to the prompts, and added more information on flow rate. Let me know if there's anything else outstanding. Thank you!

Hi @gtspencer, looks good! Just one more fix to use the correct contract depending on network

@gtspencer
gtspencerforce-pushed the feat/superfluid-typescript branch from 4513f32 to 86b6ba7CompareAugust 20, 2025 23:04
@gtspencer

Copy link
Copy Markdown
ContributorAuthor

oops! Just updated! @phdargen

@phdargen

Copy link
Copy Markdown
Contributor

This is tested and good to go imo @CarsonRoscoe

@CarsonRoscoe
CarsonRoscoe merged commit 4d0e5d1 into coinbase:mainAug 21, 2025
26 checks passed
@phdargenphdargen mentioned this pull request Sep 4, 2025
7 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 documentationtypescript

Development

Successfully merging this pull request may close these issues.

4 participants

@gtspencer@cb-heimdall@phdargen@CarsonRoscoe
, '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

feat: superfluid typescript action provider - #823

Merged
CarsonRoscoe merged 1 commit into
coinbase:mainfrom
gtspencer:feat/superfluid-typescript
Aug 21, 2025
Merged

feat: superfluid typescript action provider#823
CarsonRoscoe merged 1 commit into
coinbase:mainfrom
gtspencer:feat/superfluid-typescript

Conversation

@gtspencer

@gtspencergtspencer commented Aug 12, 2025

Copy link
Copy Markdown
Contributor

Link to Issue

Description

Superfluid action providers, broken up by functionality (create stream, create pool, update stream, update pool). We experienced need for Superfluid functionality in our agent and decided to include the implementation in the core repo.

Qualified Impact

Very little qualified impact due to the self contained nature of the action. Only dependency is the graphql-request package, which can be omitted in the event of an error.

Pool Actions:

  • Create pool
  • Update member units

Stream Actions:

  • Create stream
  • Update stream
  • Close stream

Query Actions (easily extensible for other queries):

  • Query open streams

Create Super Token

  • Creates a SuperToken (wrapper) implementation for an underlying ERC20

Wrap Token

  • Wraps an ERC20 into its Super Token implementation

Tests

Chatbot: langchain-cdp-chatbot
Network: Base Mainnet
Setup: Add relevant actions (SuperfluidStreamActionProvider, etc.) to the chatbot
Create Super Token
Prompt: Can you create a super token for this erc20: 0xe8E55A847Bb446d967ef92F4580162fb8f2d3F38
-------------------
0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40
Created super token for 0xe8E55A847Bb446d967ef92F4580162fb8f2d3F38. Super token address at 0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40 Transaction hash: 0xa9ea729bcbe30e3bb8610f9adf91fc4f4162e4cda5c33273c1f0d830388492da
-------------------
The super token has been successfully created for the ERC20 address `0xe8E55A847Bb446d967ef92F4580162fb8f2d3F38`. - **Super token address**: `0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40`
- **Transaction hash**: `0xa9ea729bcbe30e3bb8610f9adf91fc4f4162e4cda5c33273c1f0d830388492da`
Let me know if you need further assistance!
-------------------
Wrap Super Token
Prompt: Can you wrap 1000 of this erc20 0xe8E55A847Bb446d967ef92F4580162fb8f2d3F38 into this super token 0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40 on base mainnet
-------------------
Wallet Details:
- Provider: cdp_evm_wallet_provider
- Address: 0x77841ee4399EFfc9b515c09cf239D784392F897F
- Network:
* Protocol Family: evm
* Network ID: base-mainnet
* Chain ID: 8453
- Native Balance: 37507762138705 WEI
-------------------
-------------------
Wrapped 1000 of token 0xe8E55A847Bb446d967ef92F4580162fb8f2d3F38 as a SuperToken. Transaction hash: 0xd073bb4a85cbc79dc55f9996c500d141a257ccc2e173cce8f09017ec53b7cce6
-------------------
Successfully wrapped 1000 of the ERC20 token (0xe8E55A847Bb446d967ef92F4580162fb8f2d3F38) into the Super token. Transaction hash: **0xd073bb4a85cbc79dc55f9996c500d141a257ccc2e173cce8f09017ec53b7cce6**. If you have any more requests or questions, feel free to ask!
-------------------
Create Stream
Prompt: Can you create a superfluid stream of the super token 0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40 at a rate of 100 to address 0x8F4359D1C2166452b5e7a02742D6fe9ca5448FDe
-------------------
Created stream of token 0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40 to 0x8F4359D1C2166452b5e7a02742D6fe9ca5448FDe at a rate of 100. The link to the stream is https://app.superfluid.finance/stream/base-mainnet/0x77841ee4399EFfc9b515c09cf239D784392F897F-0x8F4359D1C2166452b5e7a02742D6fe9ca5448FDe-0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40. Transaction hash: 0xe108156834b91a52ab431e92095e171c93800d02f590271c5364eac26561dda3
-------------------
The Superfluid stream has been successfully created! You are streaming the SuperToken (0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40) at a rate of 100 to the address **0x8F4359D1C2166452b5e7a02742D6fe9ca5448FDe**.
Here is the transaction hash for your reference: **0xe108156834b91a52ab431e92095e171c93800d02f590271c5364eac26561dda3**.
You can view the stream details using the following link: [View Stream](https://app.superfluid.finance/stream/base-mainnet/0x77841ee4399EFfc9b515c09cf239D784392F897F-0x8F4359D1C2166452b5e7a02742D6fe9ca5448FDe-0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40).
-------------------
Pools
Prompt: Can you create a pool of the token 0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40
-------------------
Created pool of token 0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40 at 0x8651059749637291C4175d539d4eA62340aFBcAD. Transaction hash: 0x0b717b24504452ebf18a3f707b882f247a25cff1d7302d21f71916f21b9379a5
-------------------
The pool for the token **0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40** has been created successfully. - **Pool Address:** 0x8651059749637291C4175d539d4eA62340aFBcAD
- **Transaction Hash:** [0x0b717b24504452ebf18a3f707b882f247a25cff1d7302d21f71916f21b9379a5](https://etherscan.io/tx/0x0b717b24504452ebf18a3f707b882f247a25cff1d7302d21f71916f21b9379a5)
If you need any further assistance, feel free to ask!
-------------------

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 12, 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

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation action provider New action provider typescript labels Aug 12, 2025
@phdargen

Copy link
Copy Markdown
Contributor

Hi @gtspencer, thanks for the contribution! Looks pretty good, a few initial comments below before I will do some manual testing

@phdargen

Copy link
Copy Markdown
Contributor

We require all commits to be verified, unfortunately your last one is not signed. Please rebase against main and resign the commit

@phdargen

Copy link
Copy Markdown
Contributor

Could you please add more comprehensive test output to the PR description, ideally prompts for all new actions?

@@ -0,0 +1,5 @@
---
"@coinbase/agentkit": minor

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.

minor -> patch

export * from "./schemas";
export * from "./superfluidStreamActionProvider";
export * from "./superfluidPoolActionProvider";
export * from "./superfluidQueryActionProvider";

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.

might be good to add a 'SuperfluidActionProvider' wrapper combining the 3 action providers for convenience

export const SuperfluidCreateStreamSchema = z
.object({
erc20TokenAddress: z.string().describe("The ERC20 token to start or update streaming"),
chainId: z.string().describe("The EVM chain ID on which the ERC20 is deployed"),

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.

it appears chainId is unused

).args;

if (success) {
return `Created pool of token ${args.erc20TokenAddress} at ${poolAddress}`;

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.

please add txHashs in all returns

* @param network - The network to check.
* @returns True if the Superfluid action provider supports the network, false otherwise.
*/
supportsNetwork = (network: Network) => network.protocolFamily === "evm";

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.

should be restricted to base, base-sepolia here

*/
export const SuperfluidDeleteStreamSchema = z
.object({
erc20TokenAddress: z.string().describe("The ERC20 token to start streaming"),

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.

suggest to add .regex(/^0x[a-fA-F0-9]{40}$/, "Invalid Ethereum address format") to all addresses


return `Updated member units of pool ${args.poolAddress} for member ${args.recipientAddress}, with new member units ${args.units}`;
} catch (error) {
return `Error creating Superfluid pool: ${error}`;

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.

creating -> updating


return `Current outflows are ${JSON.stringify(activeOutflows)}`;
} catch (error) {
return `Error creating Superfluid pool: ${error}`;

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.

creating -> querying


return `Updated stream of token ${args.erc20TokenAddress} to ${args.recipientAddress} at a rate of ${args.flowRate}`;
} catch (error) {
return `Error creating Superfluid stream: ${error}`;

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.

creating -> updating


return `Stopped stream of token ${args.erc20TokenAddress} to ${args.recipientAddress}`;
} catch (error) {
return `Error creating Superfluid stream: ${error}`;

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.

creating -> stopping

@phdargen

Copy link
Copy Markdown
Contributor

Main concern is Superfluid wrapped tokens vs erc20.

It is my understanding that you cant stream a erc20 token directly but need to supply a superfluid wrapped token.
At the very least, this should clearly be stated in the README and action descriptions.

Ideally, the createStream action would first wrap a given erc20 token before calling createFlow or alternatively a new action added that does the wrapping.

@gtspencer
gtspencerforce-pushed the feat/superfluid-typescript branch from 1d03ef4 to e3f5f84CompareAugust 14, 2025 16:06
@gtspencer

Copy link
Copy Markdown
ContributorAuthor

hey @phdargen thanks for the review! just addressed all your concerns:

  • fixed minor issues involving hash response, networks, changeset type, and verbiage
  • added a superfluid wrapper and Super Token creator action. This way, if a stream action fails, the agent knows it may need to create the super token first and will prompt the user with that action
  • Signed single commit

Ready for a new review at your leisure!

@gtspencergtspencer changed the title Feat/superfluid typescriptfeat: superfluid typescript action providerAug 14, 2025
@gtspencer
gtspencerforce-pushed the feat/superfluid-typescript branch from 7487dfd to 716b63dCompareAugust 15, 2025 15:29
},
] as const;

export const SuperTokenFactoryAddress = "0xe20B9a38E0c96F61d1bA6b42a61512D56Fea1Eb3" as const;

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.

superTokenFactory address is different on base-sepolia (0x7447E94Dfe3d804a9f46Bf12838d467c912C8F6C), see https://docs.superfluid.org/docs/protocol/contract-addresses. Would be good if users could try the workflow on testnet

Comment on lines +1 to +6
export * from "./schemas";
export * from "./superfluidStreamActionProvider";
export * from "./superfluidPoolActionProvider";
export * from "./superfluidQueryActionProvider";
export * from "./superfluidWrapperActionProvider";
export * from "./superfluidSuperTokenCreatorActionProvider";

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.

Please move exports back to index.ts.

Instead add the following here that aggregates all superfluid actions a for a single import in eg chatbot files:

Suggested change
export*from"./schemas";
export*from"./superfluidStreamActionProvider";
export*from"./superfluidPoolActionProvider";
export*from"./superfluidQueryActionProvider";
export*from"./superfluidWrapperActionProvider";
export*from"./superfluidSuperTokenCreatorActionProvider";
import{ActionProvider}from"../actionProvider";
import{EvmWalletProvider}from"../../wallet-providers";
import{Network}from"../../network";
import{SuperfluidStreamActionProvider}from"./superfluidStreamActionProvider";
import{SuperfluidPoolActionProvider}from"./superfluidPoolActionProvider";
import{SuperfluidQueryActionProvider}from"./superfluidQueryActionProvider";
import{SuperfluidWrapperActionProvider}from"./superfluidWrapperActionProvider";
import{SuperfluidSuperTokenCreatorActionProvider}from"./superfluidSuperTokenCreatorActionProvider";
/**
*SuperfluidActionProvideraggregatesallSuperfluid-relatedactionsintoasingleprovider.
*/
exportclassSuperfluidActionProviderextendsActionProvider<EvmWalletProvider>{
constructor(){
super("superfluid",[
newSuperfluidStreamActionProvider(),
newSuperfluidPoolActionProvider(),
newSuperfluidQueryActionProvider(),
newSuperfluidWrapperActionProvider(),
newSuperfluidSuperTokenCreatorActionProvider(),
]);
}
/**
*SupportsBasemainnetandBasesepolialiketheunderlyingSuperfluidproviders.
*/
supportsNetwork=(network: Network)=>
network.networkId==="base-mainnet"||network.networkId==="base-sepolia";
}
exportconstsuperfluidActionProvider=()=>newSuperfluidActionProvider();


return `Created super token for ${args.erc20TokenAddress}. Super token address at ${superTokenAddress} Transaction hash: ${createSuperTokenHash}`;
} catch (error) {
return `Error creating Superfluid stream: ${error}`;

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.

stream -> token

Comment on lines +68 to +71
const publicClient = createPublicClient({
chain: NETWORK_ID_TO_VIEM_CHAIN[walletProvider.getNetwork().networkId || "base-mainnet"],
transport: http(),
});

// We don't get contract call results, so we need to simulate to get the pool address
const { result } = await publicClient.simulateContract({

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.

Following #824, please remove createPublicClient and use walletProvider.getPublicClient().simulateContract instead

@@ -0,0 +1,187 @@
# Superfluid Action Provider

This directory contains the implementation for Superfluid, broken up by functionality:

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.

Please add link to docs: https://docs.superfluid.org/

@phdargen

Copy link
Copy Markdown
Contributor

Please also add an entry in agentkit/README (https://github.com/coinbase/agentkit/blob/main/typescript/agentkit/README.md), should follow alphabetic order

name: "wrap_token",
description: `
This tool will directly wrap an amount of ERC20 tokens into its corresponding Super token.
The user must provide the erc20 address, and the super token address. This will be done on Base Mainnet

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.

remove "This will be done on Base Mainnet"

* @returns A JSON string containing the account details or error message
*/
@CreateAction({
name: "wrap_token",

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.

wrap_token -> wrap_superfluid_token

args: [],
});

const amount = parseUnits(String(args.wrapAmount), Number(decimals));

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.

This didnt work well in my tests, the LLM always confused the units.

Try rephrasing the action description: "The user must provide the erc20 address, and the super token address. " -> " It takes the following inputs:

  • erc20TokenAddress: The address of the ERC20 token to wrap
  • superTokenAddress: The address of the Super token
  • wrapAmount: The amount of tokens to wrap in whole units (e.g. 1.5 WETH, 10 USDC)
    Use wrapAmount units exactly as provided, do not convert to wei or any other units.
    "

and in shema: "The amount of token to wrap. This should be in units of token (and will not take decimals into account)" -> "The amount of tokens to wrap in whole units (e.g. 1.5 WETH, 10 USDC)"

Comment on lines +8 to +19
erc20TokenAddress: z
.string()
.regex(/^0x[a-fA-F0-9]{40}$/, "Invalid Ethereum address format")
.describe("The ERC20 Super token to start or update streaming"),
recipientAddress: z
.string()
.regex(/^0x[a-fA-F0-9]{40}$/, "Invalid Ethereum address format")
.describe("The EVM address to stream the token to."),
flowRate: z.string().describe("The rate at which the ERC20 is streamed to the recipient"),
})

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.

nit: erc20TokenAddress -> superTokenAddress, same in SuperfluidDeleteStreamSchema and SuperfluidCreatePoolSchema

erc20TokenAddress: z
.string()
.regex(/^0x[a-fA-F0-9]{40}$/, "Invalid Ethereum address format")
.describe("The ERC20 token to start streaming"),

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.

token -> Super token

.string()
.regex(/^0x[a-fA-F0-9]{40}$/, "Invalid Ethereum address format")
.describe("The EVM address to stream the token to."),
flowRate: z.string().describe("The rate at which the ERC20 is streamed to the recipient"),

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.

The flow rate needs additional explanation. Is this tokens per sec/min/h/day/...? Does it consider token decimals?
Please also add this info to action description

const receipt = await walletProvider.waitForTransactionReceipt(createSuperTokenHash);

const superTokenAddress = extractCreatedSuperTokenAddressAbi(receipt);
console.log(superTokenAddress);

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.

please remove all console.log


const receipt = await walletProvider.waitForTransactionReceipt(createSuperTokenHash);

const superTokenAddress = extractCreatedSuperTokenAddressAbi(receipt);

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.

Note: this fails for CdpSmartWallets as receipts doesnt include logs. You can ignore this, will look into it

@phdargen

Copy link
Copy Markdown
Contributor

hey @phdargen thanks for the review! just addressed all your concerns:

  • fixed minor issues involving hash response, networks, changeset type, and verbiage
  • added a superfluid wrapper and Super Token creator action. This way, if a stream action fails, the agent knows it may need to create the super token first and will prompt the user with that action
  • Signed single commit

Ready for a new review at your leisure!

Hi @gtspencer, thanks for the update! I tested it and it works well except for some confusion with the units. Please have a look at my follow-up comments, then we should be able to get this in

@gtspencer
gtspencerforce-pushed the feat/superfluid-typescript branch from f5dfe88 to 7a267b3CompareAugust 20, 2025 18:02
@gtspencer

Copy link
Copy Markdown
ContributorAuthor

Hey @phdargen , thanks for the review! Updated everything, made some changes to the prompts, and added more information on flow rate. Let me know if there's anything else outstanding. Thank you!

});

const createSuperTokenHash = await walletProvider.sendTransaction({
to: SuperTokenFactoryAddress as `0x${string}`,

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.

should be: to: walletProvider.getNetwork().networkId==="base-sepolia" ? SuperTokenFactoryAddress_Base_Sepolia as 0x${string}: SuperTokenFactoryAddress as0x${string},

@phdargen

Copy link
Copy Markdown
Contributor

Hey @phdargen , thanks for the review! Updated everything, made some changes to the prompts, and added more information on flow rate. Let me know if there's anything else outstanding. Thank you!

Hi @gtspencer, looks good! Just one more fix to use the correct contract depending on network

@gtspencer
gtspencerforce-pushed the feat/superfluid-typescript branch from 4513f32 to 86b6ba7CompareAugust 20, 2025 23:04
@gtspencer

Copy link
Copy Markdown
ContributorAuthor

oops! Just updated! @phdargen

@phdargen

Copy link
Copy Markdown
Contributor

This is tested and good to go imo @CarsonRoscoe

@CarsonRoscoe
CarsonRoscoe merged commit 4d0e5d1 into coinbase:mainAug 21, 2025
26 checks passed
@phdargenphdargen mentioned this pull request Sep 4, 2025
7 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 documentationtypescript

Development

Successfully merging this pull request may close these issues.

4 participants

@gtspencer@cb-heimdall@phdargen@CarsonRoscoe
, '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

feat: superfluid typescript action provider - #823

Merged
CarsonRoscoe merged 1 commit into
coinbase:mainfrom
gtspencer:feat/superfluid-typescript
Aug 21, 2025
Merged

feat: superfluid typescript action provider#823
CarsonRoscoe merged 1 commit into
coinbase:mainfrom
gtspencer:feat/superfluid-typescript

Conversation

@gtspencer

@gtspencergtspencer commented Aug 12, 2025

Copy link
Copy Markdown
Contributor

Link to Issue

Description

Superfluid action providers, broken up by functionality (create stream, create pool, update stream, update pool). We experienced need for Superfluid functionality in our agent and decided to include the implementation in the core repo.

Qualified Impact

Very little qualified impact due to the self contained nature of the action. Only dependency is the graphql-request package, which can be omitted in the event of an error.

Pool Actions:

  • Create pool
  • Update member units

Stream Actions:

  • Create stream
  • Update stream
  • Close stream

Query Actions (easily extensible for other queries):

  • Query open streams

Create Super Token

  • Creates a SuperToken (wrapper) implementation for an underlying ERC20

Wrap Token

  • Wraps an ERC20 into its Super Token implementation

Tests

Chatbot: langchain-cdp-chatbot
Network: Base Mainnet
Setup: Add relevant actions (SuperfluidStreamActionProvider, etc.) to the chatbot
Create Super Token
Prompt: Can you create a super token for this erc20: 0xe8E55A847Bb446d967ef92F4580162fb8f2d3F38
-------------------
0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40
Created super token for 0xe8E55A847Bb446d967ef92F4580162fb8f2d3F38. Super token address at 0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40 Transaction hash: 0xa9ea729bcbe30e3bb8610f9adf91fc4f4162e4cda5c33273c1f0d830388492da
-------------------
The super token has been successfully created for the ERC20 address `0xe8E55A847Bb446d967ef92F4580162fb8f2d3F38`. - **Super token address**: `0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40`
- **Transaction hash**: `0xa9ea729bcbe30e3bb8610f9adf91fc4f4162e4cda5c33273c1f0d830388492da`
Let me know if you need further assistance!
-------------------
Wrap Super Token
Prompt: Can you wrap 1000 of this erc20 0xe8E55A847Bb446d967ef92F4580162fb8f2d3F38 into this super token 0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40 on base mainnet
-------------------
Wallet Details:
- Provider: cdp_evm_wallet_provider
- Address: 0x77841ee4399EFfc9b515c09cf239D784392F897F
- Network:
* Protocol Family: evm
* Network ID: base-mainnet
* Chain ID: 8453
- Native Balance: 37507762138705 WEI
-------------------
-------------------
Wrapped 1000 of token 0xe8E55A847Bb446d967ef92F4580162fb8f2d3F38 as a SuperToken. Transaction hash: 0xd073bb4a85cbc79dc55f9996c500d141a257ccc2e173cce8f09017ec53b7cce6
-------------------
Successfully wrapped 1000 of the ERC20 token (0xe8E55A847Bb446d967ef92F4580162fb8f2d3F38) into the Super token. Transaction hash: **0xd073bb4a85cbc79dc55f9996c500d141a257ccc2e173cce8f09017ec53b7cce6**. If you have any more requests or questions, feel free to ask!
-------------------
Create Stream
Prompt: Can you create a superfluid stream of the super token 0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40 at a rate of 100 to address 0x8F4359D1C2166452b5e7a02742D6fe9ca5448FDe
-------------------
Created stream of token 0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40 to 0x8F4359D1C2166452b5e7a02742D6fe9ca5448FDe at a rate of 100. The link to the stream is https://app.superfluid.finance/stream/base-mainnet/0x77841ee4399EFfc9b515c09cf239D784392F897F-0x8F4359D1C2166452b5e7a02742D6fe9ca5448FDe-0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40. Transaction hash: 0xe108156834b91a52ab431e92095e171c93800d02f590271c5364eac26561dda3
-------------------
The Superfluid stream has been successfully created! You are streaming the SuperToken (0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40) at a rate of 100 to the address **0x8F4359D1C2166452b5e7a02742D6fe9ca5448FDe**.
Here is the transaction hash for your reference: **0xe108156834b91a52ab431e92095e171c93800d02f590271c5364eac26561dda3**.
You can view the stream details using the following link: [View Stream](https://app.superfluid.finance/stream/base-mainnet/0x77841ee4399EFfc9b515c09cf239D784392F897F-0x8F4359D1C2166452b5e7a02742D6fe9ca5448FDe-0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40).
-------------------
Pools
Prompt: Can you create a pool of the token 0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40
-------------------
Created pool of token 0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40 at 0x8651059749637291C4175d539d4eA62340aFBcAD. Transaction hash: 0x0b717b24504452ebf18a3f707b882f247a25cff1d7302d21f71916f21b9379a5
-------------------
The pool for the token **0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40** has been created successfully. - **Pool Address:** 0x8651059749637291C4175d539d4eA62340aFBcAD
- **Transaction Hash:** [0x0b717b24504452ebf18a3f707b882f247a25cff1d7302d21f71916f21b9379a5](https://etherscan.io/tx/0x0b717b24504452ebf18a3f707b882f247a25cff1d7302d21f71916f21b9379a5)
If you need any further assistance, feel free to ask!
-------------------

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 12, 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

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation action provider New action provider typescript labels Aug 12, 2025
@phdargen

Copy link
Copy Markdown
Contributor

Hi @gtspencer, thanks for the contribution! Looks pretty good, a few initial comments below before I will do some manual testing

@phdargen

Copy link
Copy Markdown
Contributor

We require all commits to be verified, unfortunately your last one is not signed. Please rebase against main and resign the commit

@phdargen

Copy link
Copy Markdown
Contributor

Could you please add more comprehensive test output to the PR description, ideally prompts for all new actions?

@@ -0,0 +1,5 @@
---
"@coinbase/agentkit": minor

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.

minor -> patch

export * from "./schemas";
export * from "./superfluidStreamActionProvider";
export * from "./superfluidPoolActionProvider";
export * from "./superfluidQueryActionProvider";

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.

might be good to add a 'SuperfluidActionProvider' wrapper combining the 3 action providers for convenience

export const SuperfluidCreateStreamSchema = z
.object({
erc20TokenAddress: z.string().describe("The ERC20 token to start or update streaming"),
chainId: z.string().describe("The EVM chain ID on which the ERC20 is deployed"),

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.

it appears chainId is unused

).args;

if (success) {
return `Created pool of token ${args.erc20TokenAddress} at ${poolAddress}`;

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.

please add txHashs in all returns

* @param network - The network to check.
* @returns True if the Superfluid action provider supports the network, false otherwise.
*/
supportsNetwork = (network: Network) => network.protocolFamily === "evm";

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.

should be restricted to base, base-sepolia here

*/
export const SuperfluidDeleteStreamSchema = z
.object({
erc20TokenAddress: z.string().describe("The ERC20 token to start streaming"),

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.

suggest to add .regex(/^0x[a-fA-F0-9]{40}$/, "Invalid Ethereum address format") to all addresses


return `Updated member units of pool ${args.poolAddress} for member ${args.recipientAddress}, with new member units ${args.units}`;
} catch (error) {
return `Error creating Superfluid pool: ${error}`;

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.

creating -> updating


return `Current outflows are ${JSON.stringify(activeOutflows)}`;
} catch (error) {
return `Error creating Superfluid pool: ${error}`;

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.

creating -> querying


return `Updated stream of token ${args.erc20TokenAddress} to ${args.recipientAddress} at a rate of ${args.flowRate}`;
} catch (error) {
return `Error creating Superfluid stream: ${error}`;

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.

creating -> updating


return `Stopped stream of token ${args.erc20TokenAddress} to ${args.recipientAddress}`;
} catch (error) {
return `Error creating Superfluid stream: ${error}`;

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.

creating -> stopping

@phdargen

Copy link
Copy Markdown
Contributor

Main concern is Superfluid wrapped tokens vs erc20.

It is my understanding that you cant stream a erc20 token directly but need to supply a superfluid wrapped token.
At the very least, this should clearly be stated in the README and action descriptions.

Ideally, the createStream action would first wrap a given erc20 token before calling createFlow or alternatively a new action added that does the wrapping.

@gtspencer
gtspencerforce-pushed the feat/superfluid-typescript branch from 1d03ef4 to e3f5f84CompareAugust 14, 2025 16:06
@gtspencer

Copy link
Copy Markdown
ContributorAuthor

hey @phdargen thanks for the review! just addressed all your concerns:

  • fixed minor issues involving hash response, networks, changeset type, and verbiage
  • added a superfluid wrapper and Super Token creator action. This way, if a stream action fails, the agent knows it may need to create the super token first and will prompt the user with that action
  • Signed single commit

Ready for a new review at your leisure!

@gtspencergtspencer changed the title Feat/superfluid typescriptfeat: superfluid typescript action providerAug 14, 2025
@gtspencer
gtspencerforce-pushed the feat/superfluid-typescript branch from 7487dfd to 716b63dCompareAugust 15, 2025 15:29
},
] as const;

export const SuperTokenFactoryAddress = "0xe20B9a38E0c96F61d1bA6b42a61512D56Fea1Eb3" as const;

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.

superTokenFactory address is different on base-sepolia (0x7447E94Dfe3d804a9f46Bf12838d467c912C8F6C), see https://docs.superfluid.org/docs/protocol/contract-addresses. Would be good if users could try the workflow on testnet

Comment on lines +1 to +6
export * from "./schemas";
export * from "./superfluidStreamActionProvider";
export * from "./superfluidPoolActionProvider";
export * from "./superfluidQueryActionProvider";
export * from "./superfluidWrapperActionProvider";
export * from "./superfluidSuperTokenCreatorActionProvider";

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.

Please move exports back to index.ts.

Instead add the following here that aggregates all superfluid actions a for a single import in eg chatbot files:

Suggested change
export*from"./schemas";
export*from"./superfluidStreamActionProvider";
export*from"./superfluidPoolActionProvider";
export*from"./superfluidQueryActionProvider";
export*from"./superfluidWrapperActionProvider";
export*from"./superfluidSuperTokenCreatorActionProvider";
import{ActionProvider}from"../actionProvider";
import{EvmWalletProvider}from"../../wallet-providers";
import{Network}from"../../network";
import{SuperfluidStreamActionProvider}from"./superfluidStreamActionProvider";
import{SuperfluidPoolActionProvider}from"./superfluidPoolActionProvider";
import{SuperfluidQueryActionProvider}from"./superfluidQueryActionProvider";
import{SuperfluidWrapperActionProvider}from"./superfluidWrapperActionProvider";
import{SuperfluidSuperTokenCreatorActionProvider}from"./superfluidSuperTokenCreatorActionProvider";
/**
*SuperfluidActionProvideraggregatesallSuperfluid-relatedactionsintoasingleprovider.
*/
exportclassSuperfluidActionProviderextendsActionProvider<EvmWalletProvider>{
constructor(){
super("superfluid",[
newSuperfluidStreamActionProvider(),
newSuperfluidPoolActionProvider(),
newSuperfluidQueryActionProvider(),
newSuperfluidWrapperActionProvider(),
newSuperfluidSuperTokenCreatorActionProvider(),
]);
}
/**
*SupportsBasemainnetandBasesepolialiketheunderlyingSuperfluidproviders.
*/
supportsNetwork=(network: Network)=>
network.networkId==="base-mainnet"||network.networkId==="base-sepolia";
}
exportconstsuperfluidActionProvider=()=>newSuperfluidActionProvider();


return `Created super token for ${args.erc20TokenAddress}. Super token address at ${superTokenAddress} Transaction hash: ${createSuperTokenHash}`;
} catch (error) {
return `Error creating Superfluid stream: ${error}`;

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.

stream -> token

Comment on lines +68 to +71
const publicClient = createPublicClient({
chain: NETWORK_ID_TO_VIEM_CHAIN[walletProvider.getNetwork().networkId || "base-mainnet"],
transport: http(),
});

// We don't get contract call results, so we need to simulate to get the pool address
const { result } = await publicClient.simulateContract({

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.

Following #824, please remove createPublicClient and use walletProvider.getPublicClient().simulateContract instead

@@ -0,0 +1,187 @@
# Superfluid Action Provider

This directory contains the implementation for Superfluid, broken up by functionality:

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.

Please add link to docs: https://docs.superfluid.org/

@phdargen

Copy link
Copy Markdown
Contributor

Please also add an entry in agentkit/README (https://github.com/coinbase/agentkit/blob/main/typescript/agentkit/README.md), should follow alphabetic order

name: "wrap_token",
description: `
This tool will directly wrap an amount of ERC20 tokens into its corresponding Super token.
The user must provide the erc20 address, and the super token address. This will be done on Base Mainnet

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.

remove "This will be done on Base Mainnet"

* @returns A JSON string containing the account details or error message
*/
@CreateAction({
name: "wrap_token",

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.

wrap_token -> wrap_superfluid_token

args: [],
});

const amount = parseUnits(String(args.wrapAmount), Number(decimals));

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.

This didnt work well in my tests, the LLM always confused the units.

Try rephrasing the action description: "The user must provide the erc20 address, and the super token address. " -> " It takes the following inputs:

  • erc20TokenAddress: The address of the ERC20 token to wrap
  • superTokenAddress: The address of the Super token
  • wrapAmount: The amount of tokens to wrap in whole units (e.g. 1.5 WETH, 10 USDC)
    Use wrapAmount units exactly as provided, do not convert to wei or any other units.
    "

and in shema: "The amount of token to wrap. This should be in units of token (and will not take decimals into account)" -> "The amount of tokens to wrap in whole units (e.g. 1.5 WETH, 10 USDC)"

Comment on lines +8 to +19
erc20TokenAddress: z
.string()
.regex(/^0x[a-fA-F0-9]{40}$/, "Invalid Ethereum address format")
.describe("The ERC20 Super token to start or update streaming"),
recipientAddress: z
.string()
.regex(/^0x[a-fA-F0-9]{40}$/, "Invalid Ethereum address format")
.describe("The EVM address to stream the token to."),
flowRate: z.string().describe("The rate at which the ERC20 is streamed to the recipient"),
})

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.

nit: erc20TokenAddress -> superTokenAddress, same in SuperfluidDeleteStreamSchema and SuperfluidCreatePoolSchema

erc20TokenAddress: z
.string()
.regex(/^0x[a-fA-F0-9]{40}$/, "Invalid Ethereum address format")
.describe("The ERC20 token to start streaming"),

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.

token -> Super token

.string()
.regex(/^0x[a-fA-F0-9]{40}$/, "Invalid Ethereum address format")
.describe("The EVM address to stream the token to."),
flowRate: z.string().describe("The rate at which the ERC20 is streamed to the recipient"),

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.

The flow rate needs additional explanation. Is this tokens per sec/min/h/day/...? Does it consider token decimals?
Please also add this info to action description

const receipt = await walletProvider.waitForTransactionReceipt(createSuperTokenHash);

const superTokenAddress = extractCreatedSuperTokenAddressAbi(receipt);
console.log(superTokenAddress);

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.

please remove all console.log


const receipt = await walletProvider.waitForTransactionReceipt(createSuperTokenHash);

const superTokenAddress = extractCreatedSuperTokenAddressAbi(receipt);

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.

Note: this fails for CdpSmartWallets as receipts doesnt include logs. You can ignore this, will look into it

@phdargen

Copy link
Copy Markdown
Contributor

hey @phdargen thanks for the review! just addressed all your concerns:

  • fixed minor issues involving hash response, networks, changeset type, and verbiage
  • added a superfluid wrapper and Super Token creator action. This way, if a stream action fails, the agent knows it may need to create the super token first and will prompt the user with that action
  • Signed single commit

Ready for a new review at your leisure!

Hi @gtspencer, thanks for the update! I tested it and it works well except for some confusion with the units. Please have a look at my follow-up comments, then we should be able to get this in

@gtspencer
gtspencerforce-pushed the feat/superfluid-typescript branch from f5dfe88 to 7a267b3CompareAugust 20, 2025 18:02
@gtspencer

Copy link
Copy Markdown
ContributorAuthor

Hey @phdargen , thanks for the review! Updated everything, made some changes to the prompts, and added more information on flow rate. Let me know if there's anything else outstanding. Thank you!

});

const createSuperTokenHash = await walletProvider.sendTransaction({
to: SuperTokenFactoryAddress as `0x${string}`,

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.

should be: to: walletProvider.getNetwork().networkId==="base-sepolia" ? SuperTokenFactoryAddress_Base_Sepolia as 0x${string}: SuperTokenFactoryAddress as0x${string},

@phdargen

Copy link
Copy Markdown
Contributor

Hey @phdargen , thanks for the review! Updated everything, made some changes to the prompts, and added more information on flow rate. Let me know if there's anything else outstanding. Thank you!

Hi @gtspencer, looks good! Just one more fix to use the correct contract depending on network

@gtspencer
gtspencerforce-pushed the feat/superfluid-typescript branch from 4513f32 to 86b6ba7CompareAugust 20, 2025 23:04
@gtspencer

Copy link
Copy Markdown
ContributorAuthor

oops! Just updated! @phdargen

@phdargen

Copy link
Copy Markdown
Contributor

This is tested and good to go imo @CarsonRoscoe

@CarsonRoscoe
CarsonRoscoe merged commit 4d0e5d1 into coinbase:mainAug 21, 2025
26 checks passed
@phdargenphdargen mentioned this pull request Sep 4, 2025
7 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 documentationtypescript

Development

Successfully merging this pull request may close these issues.

4 participants

@gtspencer@cb-heimdall@phdargen@CarsonRoscoe
, '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

feat: superfluid typescript action provider - #823

Merged
CarsonRoscoe merged 1 commit into
coinbase:mainfrom
gtspencer:feat/superfluid-typescript
Aug 21, 2025
Merged

feat: superfluid typescript action provider#823
CarsonRoscoe merged 1 commit into
coinbase:mainfrom
gtspencer:feat/superfluid-typescript

Conversation

@gtspencer

@gtspencergtspencer commented Aug 12, 2025

Copy link
Copy Markdown
Contributor

Link to Issue

Description

Superfluid action providers, broken up by functionality (create stream, create pool, update stream, update pool). We experienced need for Superfluid functionality in our agent and decided to include the implementation in the core repo.

Qualified Impact

Very little qualified impact due to the self contained nature of the action. Only dependency is the graphql-request package, which can be omitted in the event of an error.

Pool Actions:

  • Create pool
  • Update member units

Stream Actions:

  • Create stream
  • Update stream
  • Close stream

Query Actions (easily extensible for other queries):

  • Query open streams

Create Super Token

  • Creates a SuperToken (wrapper) implementation for an underlying ERC20

Wrap Token

  • Wraps an ERC20 into its Super Token implementation

Tests

Chatbot: langchain-cdp-chatbot
Network: Base Mainnet
Setup: Add relevant actions (SuperfluidStreamActionProvider, etc.) to the chatbot
Create Super Token
Prompt: Can you create a super token for this erc20: 0xe8E55A847Bb446d967ef92F4580162fb8f2d3F38
-------------------
0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40
Created super token for 0xe8E55A847Bb446d967ef92F4580162fb8f2d3F38. Super token address at 0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40 Transaction hash: 0xa9ea729bcbe30e3bb8610f9adf91fc4f4162e4cda5c33273c1f0d830388492da
-------------------
The super token has been successfully created for the ERC20 address `0xe8E55A847Bb446d967ef92F4580162fb8f2d3F38`. - **Super token address**: `0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40`
- **Transaction hash**: `0xa9ea729bcbe30e3bb8610f9adf91fc4f4162e4cda5c33273c1f0d830388492da`
Let me know if you need further assistance!
-------------------
Wrap Super Token
Prompt: Can you wrap 1000 of this erc20 0xe8E55A847Bb446d967ef92F4580162fb8f2d3F38 into this super token 0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40 on base mainnet
-------------------
Wallet Details:
- Provider: cdp_evm_wallet_provider
- Address: 0x77841ee4399EFfc9b515c09cf239D784392F897F
- Network:
* Protocol Family: evm
* Network ID: base-mainnet
* Chain ID: 8453
- Native Balance: 37507762138705 WEI
-------------------
-------------------
Wrapped 1000 of token 0xe8E55A847Bb446d967ef92F4580162fb8f2d3F38 as a SuperToken. Transaction hash: 0xd073bb4a85cbc79dc55f9996c500d141a257ccc2e173cce8f09017ec53b7cce6
-------------------
Successfully wrapped 1000 of the ERC20 token (0xe8E55A847Bb446d967ef92F4580162fb8f2d3F38) into the Super token. Transaction hash: **0xd073bb4a85cbc79dc55f9996c500d141a257ccc2e173cce8f09017ec53b7cce6**. If you have any more requests or questions, feel free to ask!
-------------------
Create Stream
Prompt: Can you create a superfluid stream of the super token 0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40 at a rate of 100 to address 0x8F4359D1C2166452b5e7a02742D6fe9ca5448FDe
-------------------
Created stream of token 0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40 to 0x8F4359D1C2166452b5e7a02742D6fe9ca5448FDe at a rate of 100. The link to the stream is https://app.superfluid.finance/stream/base-mainnet/0x77841ee4399EFfc9b515c09cf239D784392F897F-0x8F4359D1C2166452b5e7a02742D6fe9ca5448FDe-0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40. Transaction hash: 0xe108156834b91a52ab431e92095e171c93800d02f590271c5364eac26561dda3
-------------------
The Superfluid stream has been successfully created! You are streaming the SuperToken (0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40) at a rate of 100 to the address **0x8F4359D1C2166452b5e7a02742D6fe9ca5448FDe**.
Here is the transaction hash for your reference: **0xe108156834b91a52ab431e92095e171c93800d02f590271c5364eac26561dda3**.
You can view the stream details using the following link: [View Stream](https://app.superfluid.finance/stream/base-mainnet/0x77841ee4399EFfc9b515c09cf239D784392F897F-0x8F4359D1C2166452b5e7a02742D6fe9ca5448FDe-0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40).
-------------------
Pools
Prompt: Can you create a pool of the token 0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40
-------------------
Created pool of token 0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40 at 0x8651059749637291C4175d539d4eA62340aFBcAD. Transaction hash: 0x0b717b24504452ebf18a3f707b882f247a25cff1d7302d21f71916f21b9379a5
-------------------
The pool for the token **0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40** has been created successfully. - **Pool Address:** 0x8651059749637291C4175d539d4eA62340aFBcAD
- **Transaction Hash:** [0x0b717b24504452ebf18a3f707b882f247a25cff1d7302d21f71916f21b9379a5](https://etherscan.io/tx/0x0b717b24504452ebf18a3f707b882f247a25cff1d7302d21f71916f21b9379a5)
If you need any further assistance, feel free to ask!
-------------------

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 12, 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

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation action provider New action provider typescript labels Aug 12, 2025
@phdargen

Copy link
Copy Markdown
Contributor

Hi @gtspencer, thanks for the contribution! Looks pretty good, a few initial comments below before I will do some manual testing

@phdargen

Copy link
Copy Markdown
Contributor

We require all commits to be verified, unfortunately your last one is not signed. Please rebase against main and resign the commit

@phdargen

Copy link
Copy Markdown
Contributor

Could you please add more comprehensive test output to the PR description, ideally prompts for all new actions?

@@ -0,0 +1,5 @@
---
"@coinbase/agentkit": minor

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.

minor -> patch

export * from "./schemas";
export * from "./superfluidStreamActionProvider";
export * from "./superfluidPoolActionProvider";
export * from "./superfluidQueryActionProvider";

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.

might be good to add a 'SuperfluidActionProvider' wrapper combining the 3 action providers for convenience

export const SuperfluidCreateStreamSchema = z
.object({
erc20TokenAddress: z.string().describe("The ERC20 token to start or update streaming"),
chainId: z.string().describe("The EVM chain ID on which the ERC20 is deployed"),

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.

it appears chainId is unused

).args;

if (success) {
return `Created pool of token ${args.erc20TokenAddress} at ${poolAddress}`;

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.

please add txHashs in all returns

* @param network - The network to check.
* @returns True if the Superfluid action provider supports the network, false otherwise.
*/
supportsNetwork = (network: Network) => network.protocolFamily === "evm";

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.

should be restricted to base, base-sepolia here

*/
export const SuperfluidDeleteStreamSchema = z
.object({
erc20TokenAddress: z.string().describe("The ERC20 token to start streaming"),

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.

suggest to add .regex(/^0x[a-fA-F0-9]{40}$/, "Invalid Ethereum address format") to all addresses


return `Updated member units of pool ${args.poolAddress} for member ${args.recipientAddress}, with new member units ${args.units}`;
} catch (error) {
return `Error creating Superfluid pool: ${error}`;

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.

creating -> updating


return `Current outflows are ${JSON.stringify(activeOutflows)}`;
} catch (error) {
return `Error creating Superfluid pool: ${error}`;

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.

creating -> querying


return `Updated stream of token ${args.erc20TokenAddress} to ${args.recipientAddress} at a rate of ${args.flowRate}`;
} catch (error) {
return `Error creating Superfluid stream: ${error}`;

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.

creating -> updating


return `Stopped stream of token ${args.erc20TokenAddress} to ${args.recipientAddress}`;
} catch (error) {
return `Error creating Superfluid stream: ${error}`;

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.

creating -> stopping

@phdargen

Copy link
Copy Markdown
Contributor

Main concern is Superfluid wrapped tokens vs erc20.

It is my understanding that you cant stream a erc20 token directly but need to supply a superfluid wrapped token.
At the very least, this should clearly be stated in the README and action descriptions.

Ideally, the createStream action would first wrap a given erc20 token before calling createFlow or alternatively a new action added that does the wrapping.

@gtspencer
gtspencerforce-pushed the feat/superfluid-typescript branch from 1d03ef4 to e3f5f84CompareAugust 14, 2025 16:06
@gtspencer

Copy link
Copy Markdown
ContributorAuthor

hey @phdargen thanks for the review! just addressed all your concerns:

  • fixed minor issues involving hash response, networks, changeset type, and verbiage
  • added a superfluid wrapper and Super Token creator action. This way, if a stream action fails, the agent knows it may need to create the super token first and will prompt the user with that action
  • Signed single commit

Ready for a new review at your leisure!

@gtspencergtspencer changed the title Feat/superfluid typescriptfeat: superfluid typescript action providerAug 14, 2025
@gtspencer
gtspencerforce-pushed the feat/superfluid-typescript branch from 7487dfd to 716b63dCompareAugust 15, 2025 15:29
},
] as const;

export const SuperTokenFactoryAddress = "0xe20B9a38E0c96F61d1bA6b42a61512D56Fea1Eb3" as const;

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.

superTokenFactory address is different on base-sepolia (0x7447E94Dfe3d804a9f46Bf12838d467c912C8F6C), see https://docs.superfluid.org/docs/protocol/contract-addresses. Would be good if users could try the workflow on testnet

Comment on lines +1 to +6
export * from "./schemas";
export * from "./superfluidStreamActionProvider";
export * from "./superfluidPoolActionProvider";
export * from "./superfluidQueryActionProvider";
export * from "./superfluidWrapperActionProvider";
export * from "./superfluidSuperTokenCreatorActionProvider";

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.

Please move exports back to index.ts.

Instead add the following here that aggregates all superfluid actions a for a single import in eg chatbot files:

Suggested change
export*from"./schemas";
export*from"./superfluidStreamActionProvider";
export*from"./superfluidPoolActionProvider";
export*from"./superfluidQueryActionProvider";
export*from"./superfluidWrapperActionProvider";
export*from"./superfluidSuperTokenCreatorActionProvider";
import{ActionProvider}from"../actionProvider";
import{EvmWalletProvider}from"../../wallet-providers";
import{Network}from"../../network";
import{SuperfluidStreamActionProvider}from"./superfluidStreamActionProvider";
import{SuperfluidPoolActionProvider}from"./superfluidPoolActionProvider";
import{SuperfluidQueryActionProvider}from"./superfluidQueryActionProvider";
import{SuperfluidWrapperActionProvider}from"./superfluidWrapperActionProvider";
import{SuperfluidSuperTokenCreatorActionProvider}from"./superfluidSuperTokenCreatorActionProvider";
/**
*SuperfluidActionProvideraggregatesallSuperfluid-relatedactionsintoasingleprovider.
*/
exportclassSuperfluidActionProviderextendsActionProvider<EvmWalletProvider>{
constructor(){
super("superfluid",[
newSuperfluidStreamActionProvider(),
newSuperfluidPoolActionProvider(),
newSuperfluidQueryActionProvider(),
newSuperfluidWrapperActionProvider(),
newSuperfluidSuperTokenCreatorActionProvider(),
]);
}
/**
*SupportsBasemainnetandBasesepolialiketheunderlyingSuperfluidproviders.
*/
supportsNetwork=(network: Network)=>
network.networkId==="base-mainnet"||network.networkId==="base-sepolia";
}
exportconstsuperfluidActionProvider=()=>newSuperfluidActionProvider();


return `Created super token for ${args.erc20TokenAddress}. Super token address at ${superTokenAddress} Transaction hash: ${createSuperTokenHash}`;
} catch (error) {
return `Error creating Superfluid stream: ${error}`;

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.

stream -> token

Comment on lines +68 to +71
const publicClient = createPublicClient({
chain: NETWORK_ID_TO_VIEM_CHAIN[walletProvider.getNetwork().networkId || "base-mainnet"],
transport: http(),
});

// We don't get contract call results, so we need to simulate to get the pool address
const { result } = await publicClient.simulateContract({

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.

Following #824, please remove createPublicClient and use walletProvider.getPublicClient().simulateContract instead

@@ -0,0 +1,187 @@
# Superfluid Action Provider

This directory contains the implementation for Superfluid, broken up by functionality:

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.

Please add link to docs: https://docs.superfluid.org/

@phdargen

Copy link
Copy Markdown
Contributor

Please also add an entry in agentkit/README (https://github.com/coinbase/agentkit/blob/main/typescript/agentkit/README.md), should follow alphabetic order

name: "wrap_token",
description: `
This tool will directly wrap an amount of ERC20 tokens into its corresponding Super token.
The user must provide the erc20 address, and the super token address. This will be done on Base Mainnet

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.

remove "This will be done on Base Mainnet"

* @returns A JSON string containing the account details or error message
*/
@CreateAction({
name: "wrap_token",

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.

wrap_token -> wrap_superfluid_token

args: [],
});

const amount = parseUnits(String(args.wrapAmount), Number(decimals));

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.

This didnt work well in my tests, the LLM always confused the units.

Try rephrasing the action description: "The user must provide the erc20 address, and the super token address. " -> " It takes the following inputs:

  • erc20TokenAddress: The address of the ERC20 token to wrap
  • superTokenAddress: The address of the Super token
  • wrapAmount: The amount of tokens to wrap in whole units (e.g. 1.5 WETH, 10 USDC)
    Use wrapAmount units exactly as provided, do not convert to wei or any other units.
    "

and in shema: "The amount of token to wrap. This should be in units of token (and will not take decimals into account)" -> "The amount of tokens to wrap in whole units (e.g. 1.5 WETH, 10 USDC)"

Comment on lines +8 to +19
erc20TokenAddress: z
.string()
.regex(/^0x[a-fA-F0-9]{40}$/, "Invalid Ethereum address format")
.describe("The ERC20 Super token to start or update streaming"),
recipientAddress: z
.string()
.regex(/^0x[a-fA-F0-9]{40}$/, "Invalid Ethereum address format")
.describe("The EVM address to stream the token to."),
flowRate: z.string().describe("The rate at which the ERC20 is streamed to the recipient"),
})

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.

nit: erc20TokenAddress -> superTokenAddress, same in SuperfluidDeleteStreamSchema and SuperfluidCreatePoolSchema

erc20TokenAddress: z
.string()
.regex(/^0x[a-fA-F0-9]{40}$/, "Invalid Ethereum address format")
.describe("The ERC20 token to start streaming"),

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.

token -> Super token

.string()
.regex(/^0x[a-fA-F0-9]{40}$/, "Invalid Ethereum address format")
.describe("The EVM address to stream the token to."),
flowRate: z.string().describe("The rate at which the ERC20 is streamed to the recipient"),

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.

The flow rate needs additional explanation. Is this tokens per sec/min/h/day/...? Does it consider token decimals?
Please also add this info to action description

const receipt = await walletProvider.waitForTransactionReceipt(createSuperTokenHash);

const superTokenAddress = extractCreatedSuperTokenAddressAbi(receipt);
console.log(superTokenAddress);

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.

please remove all console.log


const receipt = await walletProvider.waitForTransactionReceipt(createSuperTokenHash);

const superTokenAddress = extractCreatedSuperTokenAddressAbi(receipt);

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.

Note: this fails for CdpSmartWallets as receipts doesnt include logs. You can ignore this, will look into it

@phdargen

Copy link
Copy Markdown
Contributor

hey @phdargen thanks for the review! just addressed all your concerns:

  • fixed minor issues involving hash response, networks, changeset type, and verbiage
  • added a superfluid wrapper and Super Token creator action. This way, if a stream action fails, the agent knows it may need to create the super token first and will prompt the user with that action
  • Signed single commit

Ready for a new review at your leisure!

Hi @gtspencer, thanks for the update! I tested it and it works well except for some confusion with the units. Please have a look at my follow-up comments, then we should be able to get this in

@gtspencer
gtspencerforce-pushed the feat/superfluid-typescript branch from f5dfe88 to 7a267b3CompareAugust 20, 2025 18:02
@gtspencer

Copy link
Copy Markdown
ContributorAuthor

Hey @phdargen , thanks for the review! Updated everything, made some changes to the prompts, and added more information on flow rate. Let me know if there's anything else outstanding. Thank you!

});

const createSuperTokenHash = await walletProvider.sendTransaction({
to: SuperTokenFactoryAddress as `0x${string}`,

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.

should be: to: walletProvider.getNetwork().networkId==="base-sepolia" ? SuperTokenFactoryAddress_Base_Sepolia as 0x${string}: SuperTokenFactoryAddress as0x${string},

@phdargen

Copy link
Copy Markdown
Contributor

Hey @phdargen , thanks for the review! Updated everything, made some changes to the prompts, and added more information on flow rate. Let me know if there's anything else outstanding. Thank you!

Hi @gtspencer, looks good! Just one more fix to use the correct contract depending on network

@gtspencer
gtspencerforce-pushed the feat/superfluid-typescript branch from 4513f32 to 86b6ba7CompareAugust 20, 2025 23:04
@gtspencer

Copy link
Copy Markdown
ContributorAuthor

oops! Just updated! @phdargen

@phdargen

Copy link
Copy Markdown
Contributor

This is tested and good to go imo @CarsonRoscoe

@CarsonRoscoe
CarsonRoscoe merged commit 4d0e5d1 into coinbase:mainAug 21, 2025
26 checks passed
@phdargenphdargen mentioned this pull request Sep 4, 2025
7 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 documentationtypescript

Development

Successfully merging this pull request may close these issues.

4 participants

@gtspencer@cb-heimdall@phdargen@CarsonRoscoe
, '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

feat: superfluid typescript action provider - #823

Merged
CarsonRoscoe merged 1 commit into
coinbase:mainfrom
gtspencer:feat/superfluid-typescript
Aug 21, 2025
Merged

feat: superfluid typescript action provider#823
CarsonRoscoe merged 1 commit into
coinbase:mainfrom
gtspencer:feat/superfluid-typescript

Conversation

@gtspencer

@gtspencergtspencer commented Aug 12, 2025

Copy link
Copy Markdown
Contributor

Link to Issue

Description

Superfluid action providers, broken up by functionality (create stream, create pool, update stream, update pool). We experienced need for Superfluid functionality in our agent and decided to include the implementation in the core repo.

Qualified Impact

Very little qualified impact due to the self contained nature of the action. Only dependency is the graphql-request package, which can be omitted in the event of an error.

Pool Actions:

  • Create pool
  • Update member units

Stream Actions:

  • Create stream
  • Update stream
  • Close stream

Query Actions (easily extensible for other queries):

  • Query open streams

Create Super Token

  • Creates a SuperToken (wrapper) implementation for an underlying ERC20

Wrap Token

  • Wraps an ERC20 into its Super Token implementation

Tests

Chatbot: langchain-cdp-chatbot
Network: Base Mainnet
Setup: Add relevant actions (SuperfluidStreamActionProvider, etc.) to the chatbot
Create Super Token
Prompt: Can you create a super token for this erc20: 0xe8E55A847Bb446d967ef92F4580162fb8f2d3F38
-------------------
0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40
Created super token for 0xe8E55A847Bb446d967ef92F4580162fb8f2d3F38. Super token address at 0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40 Transaction hash: 0xa9ea729bcbe30e3bb8610f9adf91fc4f4162e4cda5c33273c1f0d830388492da
-------------------
The super token has been successfully created for the ERC20 address `0xe8E55A847Bb446d967ef92F4580162fb8f2d3F38`. - **Super token address**: `0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40`
- **Transaction hash**: `0xa9ea729bcbe30e3bb8610f9adf91fc4f4162e4cda5c33273c1f0d830388492da`
Let me know if you need further assistance!
-------------------
Wrap Super Token
Prompt: Can you wrap 1000 of this erc20 0xe8E55A847Bb446d967ef92F4580162fb8f2d3F38 into this super token 0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40 on base mainnet
-------------------
Wallet Details:
- Provider: cdp_evm_wallet_provider
- Address: 0x77841ee4399EFfc9b515c09cf239D784392F897F
- Network:
* Protocol Family: evm
* Network ID: base-mainnet
* Chain ID: 8453
- Native Balance: 37507762138705 WEI
-------------------
-------------------
Wrapped 1000 of token 0xe8E55A847Bb446d967ef92F4580162fb8f2d3F38 as a SuperToken. Transaction hash: 0xd073bb4a85cbc79dc55f9996c500d141a257ccc2e173cce8f09017ec53b7cce6
-------------------
Successfully wrapped 1000 of the ERC20 token (0xe8E55A847Bb446d967ef92F4580162fb8f2d3F38) into the Super token. Transaction hash: **0xd073bb4a85cbc79dc55f9996c500d141a257ccc2e173cce8f09017ec53b7cce6**. If you have any more requests or questions, feel free to ask!
-------------------
Create Stream
Prompt: Can you create a superfluid stream of the super token 0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40 at a rate of 100 to address 0x8F4359D1C2166452b5e7a02742D6fe9ca5448FDe
-------------------
Created stream of token 0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40 to 0x8F4359D1C2166452b5e7a02742D6fe9ca5448FDe at a rate of 100. The link to the stream is https://app.superfluid.finance/stream/base-mainnet/0x77841ee4399EFfc9b515c09cf239D784392F897F-0x8F4359D1C2166452b5e7a02742D6fe9ca5448FDe-0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40. Transaction hash: 0xe108156834b91a52ab431e92095e171c93800d02f590271c5364eac26561dda3
-------------------
The Superfluid stream has been successfully created! You are streaming the SuperToken (0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40) at a rate of 100 to the address **0x8F4359D1C2166452b5e7a02742D6fe9ca5448FDe**.
Here is the transaction hash for your reference: **0xe108156834b91a52ab431e92095e171c93800d02f590271c5364eac26561dda3**.
You can view the stream details using the following link: [View Stream](https://app.superfluid.finance/stream/base-mainnet/0x77841ee4399EFfc9b515c09cf239D784392F897F-0x8F4359D1C2166452b5e7a02742D6fe9ca5448FDe-0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40).
-------------------
Pools
Prompt: Can you create a pool of the token 0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40
-------------------
Created pool of token 0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40 at 0x8651059749637291C4175d539d4eA62340aFBcAD. Transaction hash: 0x0b717b24504452ebf18a3f707b882f247a25cff1d7302d21f71916f21b9379a5
-------------------
The pool for the token **0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40** has been created successfully. - **Pool Address:** 0x8651059749637291C4175d539d4eA62340aFBcAD
- **Transaction Hash:** [0x0b717b24504452ebf18a3f707b882f247a25cff1d7302d21f71916f21b9379a5](https://etherscan.io/tx/0x0b717b24504452ebf18a3f707b882f247a25cff1d7302d21f71916f21b9379a5)
If you need any further assistance, feel free to ask!
-------------------

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 12, 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

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation action provider New action provider typescript labels Aug 12, 2025
@phdargen

Copy link
Copy Markdown
Contributor

Hi @gtspencer, thanks for the contribution! Looks pretty good, a few initial comments below before I will do some manual testing

@phdargen

Copy link
Copy Markdown
Contributor

We require all commits to be verified, unfortunately your last one is not signed. Please rebase against main and resign the commit

@phdargen

Copy link
Copy Markdown
Contributor

Could you please add more comprehensive test output to the PR description, ideally prompts for all new actions?

@@ -0,0 +1,5 @@
---
"@coinbase/agentkit": minor

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.

minor -> patch

export * from "./schemas";
export * from "./superfluidStreamActionProvider";
export * from "./superfluidPoolActionProvider";
export * from "./superfluidQueryActionProvider";

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.

might be good to add a 'SuperfluidActionProvider' wrapper combining the 3 action providers for convenience

export const SuperfluidCreateStreamSchema = z
.object({
erc20TokenAddress: z.string().describe("The ERC20 token to start or update streaming"),
chainId: z.string().describe("The EVM chain ID on which the ERC20 is deployed"),

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.

it appears chainId is unused

).args;

if (success) {
return `Created pool of token ${args.erc20TokenAddress} at ${poolAddress}`;

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.

please add txHashs in all returns

* @param network - The network to check.
* @returns True if the Superfluid action provider supports the network, false otherwise.
*/
supportsNetwork = (network: Network) => network.protocolFamily === "evm";

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.

should be restricted to base, base-sepolia here

*/
export const SuperfluidDeleteStreamSchema = z
.object({
erc20TokenAddress: z.string().describe("The ERC20 token to start streaming"),

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.

suggest to add .regex(/^0x[a-fA-F0-9]{40}$/, "Invalid Ethereum address format") to all addresses


return `Updated member units of pool ${args.poolAddress} for member ${args.recipientAddress}, with new member units ${args.units}`;
} catch (error) {
return `Error creating Superfluid pool: ${error}`;

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.

creating -> updating


return `Current outflows are ${JSON.stringify(activeOutflows)}`;
} catch (error) {
return `Error creating Superfluid pool: ${error}`;

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.

creating -> querying


return `Updated stream of token ${args.erc20TokenAddress} to ${args.recipientAddress} at a rate of ${args.flowRate}`;
} catch (error) {
return `Error creating Superfluid stream: ${error}`;

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.

creating -> updating


return `Stopped stream of token ${args.erc20TokenAddress} to ${args.recipientAddress}`;
} catch (error) {
return `Error creating Superfluid stream: ${error}`;

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.

creating -> stopping

@phdargen

Copy link
Copy Markdown
Contributor

Main concern is Superfluid wrapped tokens vs erc20.

It is my understanding that you cant stream a erc20 token directly but need to supply a superfluid wrapped token.
At the very least, this should clearly be stated in the README and action descriptions.

Ideally, the createStream action would first wrap a given erc20 token before calling createFlow or alternatively a new action added that does the wrapping.

@gtspencer
gtspencerforce-pushed the feat/superfluid-typescript branch from 1d03ef4 to e3f5f84CompareAugust 14, 2025 16:06
@gtspencer

Copy link
Copy Markdown
ContributorAuthor

hey @phdargen thanks for the review! just addressed all your concerns:

  • fixed minor issues involving hash response, networks, changeset type, and verbiage
  • added a superfluid wrapper and Super Token creator action. This way, if a stream action fails, the agent knows it may need to create the super token first and will prompt the user with that action
  • Signed single commit

Ready for a new review at your leisure!

@gtspencergtspencer changed the title Feat/superfluid typescriptfeat: superfluid typescript action providerAug 14, 2025
@gtspencer
gtspencerforce-pushed the feat/superfluid-typescript branch from 7487dfd to 716b63dCompareAugust 15, 2025 15:29
},
] as const;

export const SuperTokenFactoryAddress = "0xe20B9a38E0c96F61d1bA6b42a61512D56Fea1Eb3" as const;

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.

superTokenFactory address is different on base-sepolia (0x7447E94Dfe3d804a9f46Bf12838d467c912C8F6C), see https://docs.superfluid.org/docs/protocol/contract-addresses. Would be good if users could try the workflow on testnet

Comment on lines +1 to +6
export * from "./schemas";
export * from "./superfluidStreamActionProvider";
export * from "./superfluidPoolActionProvider";
export * from "./superfluidQueryActionProvider";
export * from "./superfluidWrapperActionProvider";
export * from "./superfluidSuperTokenCreatorActionProvider";

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.

Please move exports back to index.ts.

Instead add the following here that aggregates all superfluid actions a for a single import in eg chatbot files:

Suggested change
export*from"./schemas";
export*from"./superfluidStreamActionProvider";
export*from"./superfluidPoolActionProvider";
export*from"./superfluidQueryActionProvider";
export*from"./superfluidWrapperActionProvider";
export*from"./superfluidSuperTokenCreatorActionProvider";
import{ActionProvider}from"../actionProvider";
import{EvmWalletProvider}from"../../wallet-providers";
import{Network}from"../../network";
import{SuperfluidStreamActionProvider}from"./superfluidStreamActionProvider";
import{SuperfluidPoolActionProvider}from"./superfluidPoolActionProvider";
import{SuperfluidQueryActionProvider}from"./superfluidQueryActionProvider";
import{SuperfluidWrapperActionProvider}from"./superfluidWrapperActionProvider";
import{SuperfluidSuperTokenCreatorActionProvider}from"./superfluidSuperTokenCreatorActionProvider";
/**
*SuperfluidActionProvideraggregatesallSuperfluid-relatedactionsintoasingleprovider.
*/
exportclassSuperfluidActionProviderextendsActionProvider<EvmWalletProvider>{
constructor(){
super("superfluid",[
newSuperfluidStreamActionProvider(),
newSuperfluidPoolActionProvider(),
newSuperfluidQueryActionProvider(),
newSuperfluidWrapperActionProvider(),
newSuperfluidSuperTokenCreatorActionProvider(),
]);
}
/**
*SupportsBasemainnetandBasesepolialiketheunderlyingSuperfluidproviders.
*/
supportsNetwork=(network: Network)=>
network.networkId==="base-mainnet"||network.networkId==="base-sepolia";
}
exportconstsuperfluidActionProvider=()=>newSuperfluidActionProvider();


return `Created super token for ${args.erc20TokenAddress}. Super token address at ${superTokenAddress} Transaction hash: ${createSuperTokenHash}`;
} catch (error) {
return `Error creating Superfluid stream: ${error}`;

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.

stream -> token

Comment on lines +68 to +71
const publicClient = createPublicClient({
chain: NETWORK_ID_TO_VIEM_CHAIN[walletProvider.getNetwork().networkId || "base-mainnet"],
transport: http(),
});

// We don't get contract call results, so we need to simulate to get the pool address
const { result } = await publicClient.simulateContract({

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.

Following #824, please remove createPublicClient and use walletProvider.getPublicClient().simulateContract instead

@@ -0,0 +1,187 @@
# Superfluid Action Provider

This directory contains the implementation for Superfluid, broken up by functionality:

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.

Please add link to docs: https://docs.superfluid.org/

@phdargen

Copy link
Copy Markdown
Contributor

Please also add an entry in agentkit/README (https://github.com/coinbase/agentkit/blob/main/typescript/agentkit/README.md), should follow alphabetic order

name: "wrap_token",
description: `
This tool will directly wrap an amount of ERC20 tokens into its corresponding Super token.
The user must provide the erc20 address, and the super token address. This will be done on Base Mainnet

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.

remove "This will be done on Base Mainnet"

* @returns A JSON string containing the account details or error message
*/
@CreateAction({
name: "wrap_token",

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.

wrap_token -> wrap_superfluid_token

args: [],
});

const amount = parseUnits(String(args.wrapAmount), Number(decimals));

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.

This didnt work well in my tests, the LLM always confused the units.

Try rephrasing the action description: "The user must provide the erc20 address, and the super token address. " -> " It takes the following inputs:

  • erc20TokenAddress: The address of the ERC20 token to wrap
  • superTokenAddress: The address of the Super token
  • wrapAmount: The amount of tokens to wrap in whole units (e.g. 1.5 WETH, 10 USDC)
    Use wrapAmount units exactly as provided, do not convert to wei or any other units.
    "

and in shema: "The amount of token to wrap. This should be in units of token (and will not take decimals into account)" -> "The amount of tokens to wrap in whole units (e.g. 1.5 WETH, 10 USDC)"

Comment on lines +8 to +19
erc20TokenAddress: z
.string()
.regex(/^0x[a-fA-F0-9]{40}$/, "Invalid Ethereum address format")
.describe("The ERC20 Super token to start or update streaming"),
recipientAddress: z
.string()
.regex(/^0x[a-fA-F0-9]{40}$/, "Invalid Ethereum address format")
.describe("The EVM address to stream the token to."),
flowRate: z.string().describe("The rate at which the ERC20 is streamed to the recipient"),
})

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.

nit: erc20TokenAddress -> superTokenAddress, same in SuperfluidDeleteStreamSchema and SuperfluidCreatePoolSchema

erc20TokenAddress: z
.string()
.regex(/^0x[a-fA-F0-9]{40}$/, "Invalid Ethereum address format")
.describe("The ERC20 token to start streaming"),

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.

token -> Super token

.string()
.regex(/^0x[a-fA-F0-9]{40}$/, "Invalid Ethereum address format")
.describe("The EVM address to stream the token to."),
flowRate: z.string().describe("The rate at which the ERC20 is streamed to the recipient"),

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.

The flow rate needs additional explanation. Is this tokens per sec/min/h/day/...? Does it consider token decimals?
Please also add this info to action description

const receipt = await walletProvider.waitForTransactionReceipt(createSuperTokenHash);

const superTokenAddress = extractCreatedSuperTokenAddressAbi(receipt);
console.log(superTokenAddress);

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.

please remove all console.log


const receipt = await walletProvider.waitForTransactionReceipt(createSuperTokenHash);

const superTokenAddress = extractCreatedSuperTokenAddressAbi(receipt);

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.

Note: this fails for CdpSmartWallets as receipts doesnt include logs. You can ignore this, will look into it

@phdargen

Copy link
Copy Markdown
Contributor

hey @phdargen thanks for the review! just addressed all your concerns:

  • fixed minor issues involving hash response, networks, changeset type, and verbiage
  • added a superfluid wrapper and Super Token creator action. This way, if a stream action fails, the agent knows it may need to create the super token first and will prompt the user with that action
  • Signed single commit

Ready for a new review at your leisure!

Hi @gtspencer, thanks for the update! I tested it and it works well except for some confusion with the units. Please have a look at my follow-up comments, then we should be able to get this in

@gtspencer
gtspencerforce-pushed the feat/superfluid-typescript branch from f5dfe88 to 7a267b3CompareAugust 20, 2025 18:02
@gtspencer

Copy link
Copy Markdown
ContributorAuthor

Hey @phdargen , thanks for the review! Updated everything, made some changes to the prompts, and added more information on flow rate. Let me know if there's anything else outstanding. Thank you!

});

const createSuperTokenHash = await walletProvider.sendTransaction({
to: SuperTokenFactoryAddress as `0x${string}`,

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.

should be: to: walletProvider.getNetwork().networkId==="base-sepolia" ? SuperTokenFactoryAddress_Base_Sepolia as 0x${string}: SuperTokenFactoryAddress as0x${string},

@phdargen

Copy link
Copy Markdown
Contributor

Hey @phdargen , thanks for the review! Updated everything, made some changes to the prompts, and added more information on flow rate. Let me know if there's anything else outstanding. Thank you!

Hi @gtspencer, looks good! Just one more fix to use the correct contract depending on network

@gtspencer
gtspencerforce-pushed the feat/superfluid-typescript branch from 4513f32 to 86b6ba7CompareAugust 20, 2025 23:04
@gtspencer

Copy link
Copy Markdown
ContributorAuthor

oops! Just updated! @phdargen

@phdargen

Copy link
Copy Markdown
Contributor

This is tested and good to go imo @CarsonRoscoe

@CarsonRoscoe
CarsonRoscoe merged commit 4d0e5d1 into coinbase:mainAug 21, 2025
26 checks passed
@phdargenphdargen mentioned this pull request Sep 4, 2025
7 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 documentationtypescript

Development

Successfully merging this pull request may close these issues.

4 participants

@gtspencer@cb-heimdall@phdargen@CarsonRoscoe
, '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

feat: superfluid typescript action provider - #823

Merged
CarsonRoscoe merged 1 commit into
coinbase:mainfrom
gtspencer:feat/superfluid-typescript
Aug 21, 2025
Merged

feat: superfluid typescript action provider#823
CarsonRoscoe merged 1 commit into
coinbase:mainfrom
gtspencer:feat/superfluid-typescript

Conversation

@gtspencer

@gtspencergtspencer commented Aug 12, 2025

Copy link
Copy Markdown
Contributor

Link to Issue

Description

Superfluid action providers, broken up by functionality (create stream, create pool, update stream, update pool). We experienced need for Superfluid functionality in our agent and decided to include the implementation in the core repo.

Qualified Impact

Very little qualified impact due to the self contained nature of the action. Only dependency is the graphql-request package, which can be omitted in the event of an error.

Pool Actions:

  • Create pool
  • Update member units

Stream Actions:

  • Create stream
  • Update stream
  • Close stream

Query Actions (easily extensible for other queries):

  • Query open streams

Create Super Token

  • Creates a SuperToken (wrapper) implementation for an underlying ERC20

Wrap Token

  • Wraps an ERC20 into its Super Token implementation

Tests

Chatbot: langchain-cdp-chatbot
Network: Base Mainnet
Setup: Add relevant actions (SuperfluidStreamActionProvider, etc.) to the chatbot
Create Super Token
Prompt: Can you create a super token for this erc20: 0xe8E55A847Bb446d967ef92F4580162fb8f2d3F38
-------------------
0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40
Created super token for 0xe8E55A847Bb446d967ef92F4580162fb8f2d3F38. Super token address at 0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40 Transaction hash: 0xa9ea729bcbe30e3bb8610f9adf91fc4f4162e4cda5c33273c1f0d830388492da
-------------------
The super token has been successfully created for the ERC20 address `0xe8E55A847Bb446d967ef92F4580162fb8f2d3F38`. - **Super token address**: `0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40`
- **Transaction hash**: `0xa9ea729bcbe30e3bb8610f9adf91fc4f4162e4cda5c33273c1f0d830388492da`
Let me know if you need further assistance!
-------------------
Wrap Super Token
Prompt: Can you wrap 1000 of this erc20 0xe8E55A847Bb446d967ef92F4580162fb8f2d3F38 into this super token 0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40 on base mainnet
-------------------
Wallet Details:
- Provider: cdp_evm_wallet_provider
- Address: 0x77841ee4399EFfc9b515c09cf239D784392F897F
- Network:
* Protocol Family: evm
* Network ID: base-mainnet
* Chain ID: 8453
- Native Balance: 37507762138705 WEI
-------------------
-------------------
Wrapped 1000 of token 0xe8E55A847Bb446d967ef92F4580162fb8f2d3F38 as a SuperToken. Transaction hash: 0xd073bb4a85cbc79dc55f9996c500d141a257ccc2e173cce8f09017ec53b7cce6
-------------------
Successfully wrapped 1000 of the ERC20 token (0xe8E55A847Bb446d967ef92F4580162fb8f2d3F38) into the Super token. Transaction hash: **0xd073bb4a85cbc79dc55f9996c500d141a257ccc2e173cce8f09017ec53b7cce6**. If you have any more requests or questions, feel free to ask!
-------------------
Create Stream
Prompt: Can you create a superfluid stream of the super token 0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40 at a rate of 100 to address 0x8F4359D1C2166452b5e7a02742D6fe9ca5448FDe
-------------------
Created stream of token 0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40 to 0x8F4359D1C2166452b5e7a02742D6fe9ca5448FDe at a rate of 100. The link to the stream is https://app.superfluid.finance/stream/base-mainnet/0x77841ee4399EFfc9b515c09cf239D784392F897F-0x8F4359D1C2166452b5e7a02742D6fe9ca5448FDe-0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40. Transaction hash: 0xe108156834b91a52ab431e92095e171c93800d02f590271c5364eac26561dda3
-------------------
The Superfluid stream has been successfully created! You are streaming the SuperToken (0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40) at a rate of 100 to the address **0x8F4359D1C2166452b5e7a02742D6fe9ca5448FDe**.
Here is the transaction hash for your reference: **0xe108156834b91a52ab431e92095e171c93800d02f590271c5364eac26561dda3**.
You can view the stream details using the following link: [View Stream](https://app.superfluid.finance/stream/base-mainnet/0x77841ee4399EFfc9b515c09cf239D784392F897F-0x8F4359D1C2166452b5e7a02742D6fe9ca5448FDe-0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40).
-------------------
Pools
Prompt: Can you create a pool of the token 0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40
-------------------
Created pool of token 0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40 at 0x8651059749637291C4175d539d4eA62340aFBcAD. Transaction hash: 0x0b717b24504452ebf18a3f707b882f247a25cff1d7302d21f71916f21b9379a5
-------------------
The pool for the token **0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40** has been created successfully. - **Pool Address:** 0x8651059749637291C4175d539d4eA62340aFBcAD
- **Transaction Hash:** [0x0b717b24504452ebf18a3f707b882f247a25cff1d7302d21f71916f21b9379a5](https://etherscan.io/tx/0x0b717b24504452ebf18a3f707b882f247a25cff1d7302d21f71916f21b9379a5)
If you need any further assistance, feel free to ask!
-------------------

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 12, 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

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation action provider New action provider typescript labels Aug 12, 2025
@phdargen

Copy link
Copy Markdown
Contributor

Hi @gtspencer, thanks for the contribution! Looks pretty good, a few initial comments below before I will do some manual testing

@phdargen

Copy link
Copy Markdown
Contributor

We require all commits to be verified, unfortunately your last one is not signed. Please rebase against main and resign the commit

@phdargen

Copy link
Copy Markdown
Contributor

Could you please add more comprehensive test output to the PR description, ideally prompts for all new actions?

@@ -0,0 +1,5 @@
---
"@coinbase/agentkit": minor

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.

minor -> patch

export * from "./schemas";
export * from "./superfluidStreamActionProvider";
export * from "./superfluidPoolActionProvider";
export * from "./superfluidQueryActionProvider";

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.

might be good to add a 'SuperfluidActionProvider' wrapper combining the 3 action providers for convenience

export const SuperfluidCreateStreamSchema = z
.object({
erc20TokenAddress: z.string().describe("The ERC20 token to start or update streaming"),
chainId: z.string().describe("The EVM chain ID on which the ERC20 is deployed"),

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.

it appears chainId is unused

).args;

if (success) {
return `Created pool of token ${args.erc20TokenAddress} at ${poolAddress}`;

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.

please add txHashs in all returns

* @param network - The network to check.
* @returns True if the Superfluid action provider supports the network, false otherwise.
*/
supportsNetwork = (network: Network) => network.protocolFamily === "evm";

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.

should be restricted to base, base-sepolia here

*/
export const SuperfluidDeleteStreamSchema = z
.object({
erc20TokenAddress: z.string().describe("The ERC20 token to start streaming"),

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.

suggest to add .regex(/^0x[a-fA-F0-9]{40}$/, "Invalid Ethereum address format") to all addresses


return `Updated member units of pool ${args.poolAddress} for member ${args.recipientAddress}, with new member units ${args.units}`;
} catch (error) {
return `Error creating Superfluid pool: ${error}`;

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.

creating -> updating


return `Current outflows are ${JSON.stringify(activeOutflows)}`;
} catch (error) {
return `Error creating Superfluid pool: ${error}`;

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.

creating -> querying


return `Updated stream of token ${args.erc20TokenAddress} to ${args.recipientAddress} at a rate of ${args.flowRate}`;
} catch (error) {
return `Error creating Superfluid stream: ${error}`;

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.

creating -> updating


return `Stopped stream of token ${args.erc20TokenAddress} to ${args.recipientAddress}`;
} catch (error) {
return `Error creating Superfluid stream: ${error}`;

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.

creating -> stopping

@phdargen

Copy link
Copy Markdown
Contributor

Main concern is Superfluid wrapped tokens vs erc20.

It is my understanding that you cant stream a erc20 token directly but need to supply a superfluid wrapped token.
At the very least, this should clearly be stated in the README and action descriptions.

Ideally, the createStream action would first wrap a given erc20 token before calling createFlow or alternatively a new action added that does the wrapping.

@gtspencer
gtspencerforce-pushed the feat/superfluid-typescript branch from 1d03ef4 to e3f5f84CompareAugust 14, 2025 16:06
@gtspencer

Copy link
Copy Markdown
ContributorAuthor

hey @phdargen thanks for the review! just addressed all your concerns:

  • fixed minor issues involving hash response, networks, changeset type, and verbiage
  • added a superfluid wrapper and Super Token creator action. This way, if a stream action fails, the agent knows it may need to create the super token first and will prompt the user with that action
  • Signed single commit

Ready for a new review at your leisure!

@gtspencergtspencer changed the title Feat/superfluid typescriptfeat: superfluid typescript action providerAug 14, 2025
@gtspencer
gtspencerforce-pushed the feat/superfluid-typescript branch from 7487dfd to 716b63dCompareAugust 15, 2025 15:29
},
] as const;

export const SuperTokenFactoryAddress = "0xe20B9a38E0c96F61d1bA6b42a61512D56Fea1Eb3" as const;

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.

superTokenFactory address is different on base-sepolia (0x7447E94Dfe3d804a9f46Bf12838d467c912C8F6C), see https://docs.superfluid.org/docs/protocol/contract-addresses. Would be good if users could try the workflow on testnet

Comment on lines +1 to +6
export * from "./schemas";
export * from "./superfluidStreamActionProvider";
export * from "./superfluidPoolActionProvider";
export * from "./superfluidQueryActionProvider";
export * from "./superfluidWrapperActionProvider";
export * from "./superfluidSuperTokenCreatorActionProvider";

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.

Please move exports back to index.ts.

Instead add the following here that aggregates all superfluid actions a for a single import in eg chatbot files:

Suggested change
export*from"./schemas";
export*from"./superfluidStreamActionProvider";
export*from"./superfluidPoolActionProvider";
export*from"./superfluidQueryActionProvider";
export*from"./superfluidWrapperActionProvider";
export*from"./superfluidSuperTokenCreatorActionProvider";
import{ActionProvider}from"../actionProvider";
import{EvmWalletProvider}from"../../wallet-providers";
import{Network}from"../../network";
import{SuperfluidStreamActionProvider}from"./superfluidStreamActionProvider";
import{SuperfluidPoolActionProvider}from"./superfluidPoolActionProvider";
import{SuperfluidQueryActionProvider}from"./superfluidQueryActionProvider";
import{SuperfluidWrapperActionProvider}from"./superfluidWrapperActionProvider";
import{SuperfluidSuperTokenCreatorActionProvider}from"./superfluidSuperTokenCreatorActionProvider";
/**
*SuperfluidActionProvideraggregatesallSuperfluid-relatedactionsintoasingleprovider.
*/
exportclassSuperfluidActionProviderextendsActionProvider<EvmWalletProvider>{
constructor(){
super("superfluid",[
newSuperfluidStreamActionProvider(),
newSuperfluidPoolActionProvider(),
newSuperfluidQueryActionProvider(),
newSuperfluidWrapperActionProvider(),
newSuperfluidSuperTokenCreatorActionProvider(),
]);
}
/**
*SupportsBasemainnetandBasesepolialiketheunderlyingSuperfluidproviders.
*/
supportsNetwork=(network: Network)=>
network.networkId==="base-mainnet"||network.networkId==="base-sepolia";
}
exportconstsuperfluidActionProvider=()=>newSuperfluidActionProvider();


return `Created super token for ${args.erc20TokenAddress}. Super token address at ${superTokenAddress} Transaction hash: ${createSuperTokenHash}`;
} catch (error) {
return `Error creating Superfluid stream: ${error}`;

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.

stream -> token

Comment on lines +68 to +71
const publicClient = createPublicClient({
chain: NETWORK_ID_TO_VIEM_CHAIN[walletProvider.getNetwork().networkId || "base-mainnet"],
transport: http(),
});

// We don't get contract call results, so we need to simulate to get the pool address
const { result } = await publicClient.simulateContract({

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.

Following #824, please remove createPublicClient and use walletProvider.getPublicClient().simulateContract instead

@@ -0,0 +1,187 @@
# Superfluid Action Provider

This directory contains the implementation for Superfluid, broken up by functionality:

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.

Please add link to docs: https://docs.superfluid.org/

@phdargen

Copy link
Copy Markdown
Contributor

Please also add an entry in agentkit/README (https://github.com/coinbase/agentkit/blob/main/typescript/agentkit/README.md), should follow alphabetic order

name: "wrap_token",
description: `
This tool will directly wrap an amount of ERC20 tokens into its corresponding Super token.
The user must provide the erc20 address, and the super token address. This will be done on Base Mainnet

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.

remove "This will be done on Base Mainnet"

* @returns A JSON string containing the account details or error message
*/
@CreateAction({
name: "wrap_token",

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.

wrap_token -> wrap_superfluid_token

args: [],
});

const amount = parseUnits(String(args.wrapAmount), Number(decimals));

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.

This didnt work well in my tests, the LLM always confused the units.

Try rephrasing the action description: "The user must provide the erc20 address, and the super token address. " -> " It takes the following inputs:

  • erc20TokenAddress: The address of the ERC20 token to wrap
  • superTokenAddress: The address of the Super token
  • wrapAmount: The amount of tokens to wrap in whole units (e.g. 1.5 WETH, 10 USDC)
    Use wrapAmount units exactly as provided, do not convert to wei or any other units.
    "

and in shema: "The amount of token to wrap. This should be in units of token (and will not take decimals into account)" -> "The amount of tokens to wrap in whole units (e.g. 1.5 WETH, 10 USDC)"

Comment on lines +8 to +19
erc20TokenAddress: z
.string()
.regex(/^0x[a-fA-F0-9]{40}$/, "Invalid Ethereum address format")
.describe("The ERC20 Super token to start or update streaming"),
recipientAddress: z
.string()
.regex(/^0x[a-fA-F0-9]{40}$/, "Invalid Ethereum address format")
.describe("The EVM address to stream the token to."),
flowRate: z.string().describe("The rate at which the ERC20 is streamed to the recipient"),
})

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.

nit: erc20TokenAddress -> superTokenAddress, same in SuperfluidDeleteStreamSchema and SuperfluidCreatePoolSchema

erc20TokenAddress: z
.string()
.regex(/^0x[a-fA-F0-9]{40}$/, "Invalid Ethereum address format")
.describe("The ERC20 token to start streaming"),

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.

token -> Super token

.string()
.regex(/^0x[a-fA-F0-9]{40}$/, "Invalid Ethereum address format")
.describe("The EVM address to stream the token to."),
flowRate: z.string().describe("The rate at which the ERC20 is streamed to the recipient"),

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.

The flow rate needs additional explanation. Is this tokens per sec/min/h/day/...? Does it consider token decimals?
Please also add this info to action description

const receipt = await walletProvider.waitForTransactionReceipt(createSuperTokenHash);

const superTokenAddress = extractCreatedSuperTokenAddressAbi(receipt);
console.log(superTokenAddress);

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.

please remove all console.log


const receipt = await walletProvider.waitForTransactionReceipt(createSuperTokenHash);

const superTokenAddress = extractCreatedSuperTokenAddressAbi(receipt);

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.

Note: this fails for CdpSmartWallets as receipts doesnt include logs. You can ignore this, will look into it

@phdargen

Copy link
Copy Markdown
Contributor

hey @phdargen thanks for the review! just addressed all your concerns:

  • fixed minor issues involving hash response, networks, changeset type, and verbiage
  • added a superfluid wrapper and Super Token creator action. This way, if a stream action fails, the agent knows it may need to create the super token first and will prompt the user with that action
  • Signed single commit

Ready for a new review at your leisure!

Hi @gtspencer, thanks for the update! I tested it and it works well except for some confusion with the units. Please have a look at my follow-up comments, then we should be able to get this in

@gtspencer
gtspencerforce-pushed the feat/superfluid-typescript branch from f5dfe88 to 7a267b3CompareAugust 20, 2025 18:02
@gtspencer

Copy link
Copy Markdown
ContributorAuthor

Hey @phdargen , thanks for the review! Updated everything, made some changes to the prompts, and added more information on flow rate. Let me know if there's anything else outstanding. Thank you!

});

const createSuperTokenHash = await walletProvider.sendTransaction({
to: SuperTokenFactoryAddress as `0x${string}`,

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.

should be: to: walletProvider.getNetwork().networkId==="base-sepolia" ? SuperTokenFactoryAddress_Base_Sepolia as 0x${string}: SuperTokenFactoryAddress as0x${string},

@phdargen

Copy link
Copy Markdown
Contributor

Hey @phdargen , thanks for the review! Updated everything, made some changes to the prompts, and added more information on flow rate. Let me know if there's anything else outstanding. Thank you!

Hi @gtspencer, looks good! Just one more fix to use the correct contract depending on network

@gtspencer
gtspencerforce-pushed the feat/superfluid-typescript branch from 4513f32 to 86b6ba7CompareAugust 20, 2025 23:04
@gtspencer

Copy link
Copy Markdown
ContributorAuthor

oops! Just updated! @phdargen

@phdargen

Copy link
Copy Markdown
Contributor

This is tested and good to go imo @CarsonRoscoe

@CarsonRoscoe
CarsonRoscoe merged commit 4d0e5d1 into coinbase:mainAug 21, 2025
26 checks passed
@phdargenphdargen mentioned this pull request Sep 4, 2025
7 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 documentationtypescript

Development

Successfully merging this pull request may close these issues.

4 participants

@gtspencer@cb-heimdall@phdargen@CarsonRoscoe
, '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

feat: superfluid typescript action provider - #823

Merged
CarsonRoscoe merged 1 commit into
coinbase:mainfrom
gtspencer:feat/superfluid-typescript
Aug 21, 2025
Merged

feat: superfluid typescript action provider#823
CarsonRoscoe merged 1 commit into
coinbase:mainfrom
gtspencer:feat/superfluid-typescript

Conversation

@gtspencer

@gtspencergtspencer commented Aug 12, 2025

Copy link
Copy Markdown
Contributor

Link to Issue

Description

Superfluid action providers, broken up by functionality (create stream, create pool, update stream, update pool). We experienced need for Superfluid functionality in our agent and decided to include the implementation in the core repo.

Qualified Impact

Very little qualified impact due to the self contained nature of the action. Only dependency is the graphql-request package, which can be omitted in the event of an error.

Pool Actions:

  • Create pool
  • Update member units

Stream Actions:

  • Create stream
  • Update stream
  • Close stream

Query Actions (easily extensible for other queries):

  • Query open streams

Create Super Token

  • Creates a SuperToken (wrapper) implementation for an underlying ERC20

Wrap Token

  • Wraps an ERC20 into its Super Token implementation

Tests

Chatbot: langchain-cdp-chatbot
Network: Base Mainnet
Setup: Add relevant actions (SuperfluidStreamActionProvider, etc.) to the chatbot
Create Super Token
Prompt: Can you create a super token for this erc20: 0xe8E55A847Bb446d967ef92F4580162fb8f2d3F38
-------------------
0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40
Created super token for 0xe8E55A847Bb446d967ef92F4580162fb8f2d3F38. Super token address at 0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40 Transaction hash: 0xa9ea729bcbe30e3bb8610f9adf91fc4f4162e4cda5c33273c1f0d830388492da
-------------------
The super token has been successfully created for the ERC20 address `0xe8E55A847Bb446d967ef92F4580162fb8f2d3F38`. - **Super token address**: `0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40`
- **Transaction hash**: `0xa9ea729bcbe30e3bb8610f9adf91fc4f4162e4cda5c33273c1f0d830388492da`
Let me know if you need further assistance!
-------------------
Wrap Super Token
Prompt: Can you wrap 1000 of this erc20 0xe8E55A847Bb446d967ef92F4580162fb8f2d3F38 into this super token 0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40 on base mainnet
-------------------
Wallet Details:
- Provider: cdp_evm_wallet_provider
- Address: 0x77841ee4399EFfc9b515c09cf239D784392F897F
- Network:
* Protocol Family: evm
* Network ID: base-mainnet
* Chain ID: 8453
- Native Balance: 37507762138705 WEI
-------------------
-------------------
Wrapped 1000 of token 0xe8E55A847Bb446d967ef92F4580162fb8f2d3F38 as a SuperToken. Transaction hash: 0xd073bb4a85cbc79dc55f9996c500d141a257ccc2e173cce8f09017ec53b7cce6
-------------------
Successfully wrapped 1000 of the ERC20 token (0xe8E55A847Bb446d967ef92F4580162fb8f2d3F38) into the Super token. Transaction hash: **0xd073bb4a85cbc79dc55f9996c500d141a257ccc2e173cce8f09017ec53b7cce6**. If you have any more requests or questions, feel free to ask!
-------------------
Create Stream
Prompt: Can you create a superfluid stream of the super token 0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40 at a rate of 100 to address 0x8F4359D1C2166452b5e7a02742D6fe9ca5448FDe
-------------------
Created stream of token 0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40 to 0x8F4359D1C2166452b5e7a02742D6fe9ca5448FDe at a rate of 100. The link to the stream is https://app.superfluid.finance/stream/base-mainnet/0x77841ee4399EFfc9b515c09cf239D784392F897F-0x8F4359D1C2166452b5e7a02742D6fe9ca5448FDe-0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40. Transaction hash: 0xe108156834b91a52ab431e92095e171c93800d02f590271c5364eac26561dda3
-------------------
The Superfluid stream has been successfully created! You are streaming the SuperToken (0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40) at a rate of 100 to the address **0x8F4359D1C2166452b5e7a02742D6fe9ca5448FDe**.
Here is the transaction hash for your reference: **0xe108156834b91a52ab431e92095e171c93800d02f590271c5364eac26561dda3**.
You can view the stream details using the following link: [View Stream](https://app.superfluid.finance/stream/base-mainnet/0x77841ee4399EFfc9b515c09cf239D784392F897F-0x8F4359D1C2166452b5e7a02742D6fe9ca5448FDe-0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40).
-------------------
Pools
Prompt: Can you create a pool of the token 0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40
-------------------
Created pool of token 0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40 at 0x8651059749637291C4175d539d4eA62340aFBcAD. Transaction hash: 0x0b717b24504452ebf18a3f707b882f247a25cff1d7302d21f71916f21b9379a5
-------------------
The pool for the token **0x2D92C37A341f9C7AE0438179ee2835B9Bb692e40** has been created successfully. - **Pool Address:** 0x8651059749637291C4175d539d4eA62340aFBcAD
- **Transaction Hash:** [0x0b717b24504452ebf18a3f707b882f247a25cff1d7302d21f71916f21b9379a5](https://etherscan.io/tx/0x0b717b24504452ebf18a3f707b882f247a25cff1d7302d21f71916f21b9379a5)
If you need any further assistance, feel free to ask!
-------------------

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 12, 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

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation action provider New action provider typescript labels Aug 12, 2025
@phdargen

Copy link
Copy Markdown
Contributor

Hi @gtspencer, thanks for the contribution! Looks pretty good, a few initial comments below before I will do some manual testing

@phdargen

Copy link
Copy Markdown
Contributor

We require all commits to be verified, unfortunately your last one is not signed. Please rebase against main and resign the commit

@phdargen

Copy link
Copy Markdown
Contributor

Could you please add more comprehensive test output to the PR description, ideally prompts for all new actions?

@@ -0,0 +1,5 @@
---
"@coinbase/agentkit": minor

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.

minor -> patch

export * from "./schemas";
export * from "./superfluidStreamActionProvider";
export * from "./superfluidPoolActionProvider";
export * from "./superfluidQueryActionProvider";

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.

might be good to add a 'SuperfluidActionProvider' wrapper combining the 3 action providers for convenience

export const SuperfluidCreateStreamSchema = z
.object({
erc20TokenAddress: z.string().describe("The ERC20 token to start or update streaming"),
chainId: z.string().describe("The EVM chain ID on which the ERC20 is deployed"),

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.

it appears chainId is unused

).args;

if (success) {
return `Created pool of token ${args.erc20TokenAddress} at ${poolAddress}`;

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.

please add txHashs in all returns

* @param network - The network to check.
* @returns True if the Superfluid action provider supports the network, false otherwise.
*/
supportsNetwork = (network: Network) => network.protocolFamily === "evm";

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.

should be restricted to base, base-sepolia here

*/
export const SuperfluidDeleteStreamSchema = z
.object({
erc20TokenAddress: z.string().describe("The ERC20 token to start streaming"),

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.

suggest to add .regex(/^0x[a-fA-F0-9]{40}$/, "Invalid Ethereum address format") to all addresses


return `Updated member units of pool ${args.poolAddress} for member ${args.recipientAddress}, with new member units ${args.units}`;
} catch (error) {
return `Error creating Superfluid pool: ${error}`;

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.

creating -> updating


return `Current outflows are ${JSON.stringify(activeOutflows)}`;
} catch (error) {
return `Error creating Superfluid pool: ${error}`;

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.

creating -> querying


return `Updated stream of token ${args.erc20TokenAddress} to ${args.recipientAddress} at a rate of ${args.flowRate}`;
} catch (error) {
return `Error creating Superfluid stream: ${error}`;

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.

creating -> updating


return `Stopped stream of token ${args.erc20TokenAddress} to ${args.recipientAddress}`;
} catch (error) {
return `Error creating Superfluid stream: ${error}`;

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.

creating -> stopping

@phdargen

Copy link
Copy Markdown
Contributor

Main concern is Superfluid wrapped tokens vs erc20.

It is my understanding that you cant stream a erc20 token directly but need to supply a superfluid wrapped token.
At the very least, this should clearly be stated in the README and action descriptions.

Ideally, the createStream action would first wrap a given erc20 token before calling createFlow or alternatively a new action added that does the wrapping.

@gtspencer
gtspencerforce-pushed the feat/superfluid-typescript branch from 1d03ef4 to e3f5f84CompareAugust 14, 2025 16:06
@gtspencer

Copy link
Copy Markdown
ContributorAuthor

hey @phdargen thanks for the review! just addressed all your concerns:

  • fixed minor issues involving hash response, networks, changeset type, and verbiage
  • added a superfluid wrapper and Super Token creator action. This way, if a stream action fails, the agent knows it may need to create the super token first and will prompt the user with that action
  • Signed single commit

Ready for a new review at your leisure!

@gtspencergtspencer changed the title Feat/superfluid typescriptfeat: superfluid typescript action providerAug 14, 2025
@gtspencer
gtspencerforce-pushed the feat/superfluid-typescript branch from 7487dfd to 716b63dCompareAugust 15, 2025 15:29
},
] as const;

export const SuperTokenFactoryAddress = "0xe20B9a38E0c96F61d1bA6b42a61512D56Fea1Eb3" as const;

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.

superTokenFactory address is different on base-sepolia (0x7447E94Dfe3d804a9f46Bf12838d467c912C8F6C), see https://docs.superfluid.org/docs/protocol/contract-addresses. Would be good if users could try the workflow on testnet

Comment on lines +1 to +6
export * from "./schemas";
export * from "./superfluidStreamActionProvider";
export * from "./superfluidPoolActionProvider";
export * from "./superfluidQueryActionProvider";
export * from "./superfluidWrapperActionProvider";
export * from "./superfluidSuperTokenCreatorActionProvider";

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.

Please move exports back to index.ts.

Instead add the following here that aggregates all superfluid actions a for a single import in eg chatbot files:

Suggested change
export*from"./schemas";
export*from"./superfluidStreamActionProvider";
export*from"./superfluidPoolActionProvider";
export*from"./superfluidQueryActionProvider";
export*from"./superfluidWrapperActionProvider";
export*from"./superfluidSuperTokenCreatorActionProvider";
import{ActionProvider}from"../actionProvider";
import{EvmWalletProvider}from"../../wallet-providers";
import{Network}from"../../network";
import{SuperfluidStreamActionProvider}from"./superfluidStreamActionProvider";
import{SuperfluidPoolActionProvider}from"./superfluidPoolActionProvider";
import{SuperfluidQueryActionProvider}from"./superfluidQueryActionProvider";
import{SuperfluidWrapperActionProvider}from"./superfluidWrapperActionProvider";
import{SuperfluidSuperTokenCreatorActionProvider}from"./superfluidSuperTokenCreatorActionProvider";
/**
*SuperfluidActionProvideraggregatesallSuperfluid-relatedactionsintoasingleprovider.
*/
exportclassSuperfluidActionProviderextendsActionProvider<EvmWalletProvider>{
constructor(){
super("superfluid",[
newSuperfluidStreamActionProvider(),
newSuperfluidPoolActionProvider(),
newSuperfluidQueryActionProvider(),
newSuperfluidWrapperActionProvider(),
newSuperfluidSuperTokenCreatorActionProvider(),
]);
}
/**
*SupportsBasemainnetandBasesepolialiketheunderlyingSuperfluidproviders.
*/
supportsNetwork=(network: Network)=>
network.networkId==="base-mainnet"||network.networkId==="base-sepolia";
}
exportconstsuperfluidActionProvider=()=>newSuperfluidActionProvider();


return `Created super token for ${args.erc20TokenAddress}. Super token address at ${superTokenAddress} Transaction hash: ${createSuperTokenHash}`;
} catch (error) {
return `Error creating Superfluid stream: ${error}`;

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.

stream -> token

Comment on lines +68 to +71
const publicClient = createPublicClient({
chain: NETWORK_ID_TO_VIEM_CHAIN[walletProvider.getNetwork().networkId || "base-mainnet"],
transport: http(),
});

// We don't get contract call results, so we need to simulate to get the pool address
const { result } = await publicClient.simulateContract({

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.

Following #824, please remove createPublicClient and use walletProvider.getPublicClient().simulateContract instead

@@ -0,0 +1,187 @@
# Superfluid Action Provider

This directory contains the implementation for Superfluid, broken up by functionality:

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.

Please add link to docs: https://docs.superfluid.org/

@phdargen

Copy link
Copy Markdown
Contributor

Please also add an entry in agentkit/README (https://github.com/coinbase/agentkit/blob/main/typescript/agentkit/README.md), should follow alphabetic order

name: "wrap_token",
description: `
This tool will directly wrap an amount of ERC20 tokens into its corresponding Super token.
The user must provide the erc20 address, and the super token address. This will be done on Base Mainnet

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.

remove "This will be done on Base Mainnet"

* @returns A JSON string containing the account details or error message
*/
@CreateAction({
name: "wrap_token",

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.

wrap_token -> wrap_superfluid_token

args: [],
});

const amount = parseUnits(String(args.wrapAmount), Number(decimals));

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.

This didnt work well in my tests, the LLM always confused the units.

Try rephrasing the action description: "The user must provide the erc20 address, and the super token address. " -> " It takes the following inputs:

  • erc20TokenAddress: The address of the ERC20 token to wrap
  • superTokenAddress: The address of the Super token
  • wrapAmount: The amount of tokens to wrap in whole units (e.g. 1.5 WETH, 10 USDC)
    Use wrapAmount units exactly as provided, do not convert to wei or any other units.
    "

and in shema: "The amount of token to wrap. This should be in units of token (and will not take decimals into account)" -> "The amount of tokens to wrap in whole units (e.g. 1.5 WETH, 10 USDC)"

Comment on lines +8 to +19
erc20TokenAddress: z
.string()
.regex(/^0x[a-fA-F0-9]{40}$/, "Invalid Ethereum address format")
.describe("The ERC20 Super token to start or update streaming"),
recipientAddress: z
.string()
.regex(/^0x[a-fA-F0-9]{40}$/, "Invalid Ethereum address format")
.describe("The EVM address to stream the token to."),
flowRate: z.string().describe("The rate at which the ERC20 is streamed to the recipient"),
})

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.

nit: erc20TokenAddress -> superTokenAddress, same in SuperfluidDeleteStreamSchema and SuperfluidCreatePoolSchema

erc20TokenAddress: z
.string()
.regex(/^0x[a-fA-F0-9]{40}$/, "Invalid Ethereum address format")
.describe("The ERC20 token to start streaming"),

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.

token -> Super token

.string()
.regex(/^0x[a-fA-F0-9]{40}$/, "Invalid Ethereum address format")
.describe("The EVM address to stream the token to."),
flowRate: z.string().describe("The rate at which the ERC20 is streamed to the recipient"),

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.

The flow rate needs additional explanation. Is this tokens per sec/min/h/day/...? Does it consider token decimals?
Please also add this info to action description

const receipt = await walletProvider.waitForTransactionReceipt(createSuperTokenHash);

const superTokenAddress = extractCreatedSuperTokenAddressAbi(receipt);
console.log(superTokenAddress);

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.

please remove all console.log


const receipt = await walletProvider.waitForTransactionReceipt(createSuperTokenHash);

const superTokenAddress = extractCreatedSuperTokenAddressAbi(receipt);

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.

Note: this fails for CdpSmartWallets as receipts doesnt include logs. You can ignore this, will look into it

@phdargen

Copy link
Copy Markdown
Contributor

hey @phdargen thanks for the review! just addressed all your concerns:

  • fixed minor issues involving hash response, networks, changeset type, and verbiage
  • added a superfluid wrapper and Super Token creator action. This way, if a stream action fails, the agent knows it may need to create the super token first and will prompt the user with that action
  • Signed single commit

Ready for a new review at your leisure!

Hi @gtspencer, thanks for the update! I tested it and it works well except for some confusion with the units. Please have a look at my follow-up comments, then we should be able to get this in

@gtspencer
gtspencerforce-pushed the feat/superfluid-typescript branch from f5dfe88 to 7a267b3CompareAugust 20, 2025 18:02
@gtspencer

Copy link
Copy Markdown
ContributorAuthor

Hey @phdargen , thanks for the review! Updated everything, made some changes to the prompts, and added more information on flow rate. Let me know if there's anything else outstanding. Thank you!

});

const createSuperTokenHash = await walletProvider.sendTransaction({
to: SuperTokenFactoryAddress as `0x${string}`,

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.

should be: to: walletProvider.getNetwork().networkId==="base-sepolia" ? SuperTokenFactoryAddress_Base_Sepolia as 0x${string}: SuperTokenFactoryAddress as0x${string},

@phdargen

Copy link
Copy Markdown
Contributor

Hey @phdargen , thanks for the review! Updated everything, made some changes to the prompts, and added more information on flow rate. Let me know if there's anything else outstanding. Thank you!

Hi @gtspencer, looks good! Just one more fix to use the correct contract depending on network

@gtspencer
gtspencerforce-pushed the feat/superfluid-typescript branch from 4513f32 to 86b6ba7CompareAugust 20, 2025 23:04
@gtspencer

Copy link
Copy Markdown
ContributorAuthor

oops! Just updated! @phdargen

@phdargen

Copy link
Copy Markdown
Contributor

This is tested and good to go imo @CarsonRoscoe

@CarsonRoscoe
CarsonRoscoe merged commit 4d0e5d1 into coinbase:mainAug 21, 2025
26 checks passed
@phdargenphdargen mentioned this pull request Sep 4, 2025
7 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 documentationtypescript

Development

Successfully merging this pull request may close these issues.

4 participants

@gtspencer@cb-heimdall@phdargen@CarsonRoscoe