feat: add safe wallet provider (ts) - #330

Open
phdargen wants to merge 9 commits into
coinbase:mainfrom
phdargen:safe-ts
Open

feat: add safe wallet provider (ts)#330
phdargen wants to merge 9 commits into
coinbase:mainfrom
phdargen:safe-ts

Conversation

@phdargen

@phdargenphdargen commented Feb 10, 2025

Copy link
Copy Markdown
Contributor

Why? What?

Every AI Agent deserves a Safe wallet!

Adds new safeActionProvider to handle interactions with Safe multi-signature wallets.
This implementation uses the Safe sdk that is added as new dependency.

What changed?

  • New Wallet Provider: safeWalletProvider

    • connects to an existing safe account
    • or automatically creates a new one with the provided private key as single signer
    • getBalance() returns balance of safe wallet, not private-key of agent
    • nativeTransfer() transfers eth from Safe wallet, may require approval of other signers
  • New Action Provider: safeWalletActionProvider with actions:

    • addSigner: Add a new signer to a Safe wallet
    • removeSigner: Remove an existing signer from a Safe wallet
    • changeThreshold: Modify the number of required signatures
    • approvePending: Approve a pending transaction
    • enableAllowanceModule: Activate the allowance module for a Safe
    • setAllowance: Configure spending allowances for specific addresses
  • New Action Provider: safeApiActionProvider with actions:

    • safeInfo: Retrieve detailed information about a Safe wallet
    • getAllowanceInfo: Get current allowance configurations
    • withdrawAllowance: Withdraw funds from an allowance
  • Adds new getPublicClient() method to EvmWalletProvider, so the publicClient can be passed to safeApiActionProvider

  • Adds new signHash() method to EvmWalletProvider that is needed for withdrawAllowance action

  • Adds new langchain-safe-chatbot example

  • Adds safeApiActionProvider to langchain-cdp-chatbot example

Network support

  • All evm chains

How has it been tested?

  • Unit tests
  • Agent tested, example prompts below (using gpt-4o-mini)

Running langchain-safe-chatbot without setting SAFE_ADDRESS:

Prompt: wallet info
-------------
Your wallet details are as follows:
- **Address:** 0xC099a0dE7533f9F0A20979616376c353F330ed44
- **Network:** - **Protocol Family:** EVM
- **Network ID:** ethereum-sepolia
- **Chain ID:** 11155111
- **Native Balance:** 0 WEI
Prompt: safe info
-------------
Here is the information about your Safe:
- **Safe Address:** 0xC099a0dE7533f9F0A20979616376c353F330ed44
- **Chain:** Sepolia
- **Owners:** 1 owner - 0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B
- **Threshold:** 1
- **Nonce:** 0
- **Modules:** None
- **Balance:** 0 ETH
- **Pending Transactions:** 0
Prompt: enable allowance module
-------------------
The allowance module has been successfully enabled for your Safe wallet at address **0xC099a0dE7533f9F0A20979616376c353F330ed44**. - **Transaction Hash**: 0x3eb5159d9a84324a199614a23ba0abfb1f10d56ac35865b988fd99a7c13fc0a6
Prompt: add 0xb7f17bF94DBa50077be037CDc279a868f82D86C2 as signer with threshold 2
-------------------
The signer **0xb7f17bF94DBa50077be037CDc279a868f82D86C2** has been successfully added to your Safe wallet. - **New Threshold**: 2
- **Transaction Hash**: 0x3f1a4de3647c0ee9d7b6e271f8dde7b096ee84437a21e6dabae7531bbce695e0
Prompt: I sent some eth to the safe, withdraw it all to 0xb7f17bF94DBa50077be037CDc279a868f82D86C2
-------------------
The withdrawal transaction has been proposed to transfer **0.00001 ETH** to **0xb7f17bF94DBa50077be037CDc279a868f82D86C2**.
- **Transaction Hash**: 0xdf0fd67b648f7bac6b1684436dd44f812de8963f66f77423bd654cb2d2b78f83
As the threshold is 2, other owners will need to confirm this transaction before it can be executed.
Prompt: safe info
-------------------
Here is the updated information for your Safe multi-sig wallet:
- **Safe Address**: 0xC099a0dE7533f9F0A20979616376c353F330ed44
- **Chain**: Sepolia
- **Owners**: 2 owners - 1. 0xb7f17bF94DBa50077be037CDc279a868f82D86C2
- 2. 0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B
- **Threshold**: 2 (requires two signatures for execution)
- **Nonce**: 2
- **Modules**: 0xCFbFaC74C26F8647cBDb8c5caf80BB5b32E43134
- **Balance**: 0.00001 ETH
- **Pending Transactions**: 1
- **Transaction** 0xdf0fd67b648f7bac6b1684436dd44f812de8963f66f77423bd654cb2d2b78f83 (1/2 confirmations, confirmed by: 0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B)
Prompt: I approved the tx and proposed a new one to change threshold to 1 with safe tx hash: 0xdbc8583147403145e9ea04f5725b3d998f945abe32ce6de5dc8b80cba0841c86. approve and execute it
-------------------
I successfully approved and executed the transaction:
- **Safe Transaction Hash**: 0xdbc8583147403145e9ea04f5725b3d998f945abe32ce6de5dc8b80cba0841c86
- **Execution Transaction Hash**: 0x4c6b40aceeb670f5e3230a34cc30da6fa26681a4f3446b55e3f5d5858226caed
The threshold has now been changed to 1.
Prompt: add 1 weth (0x7b79995e5f793A07Bc00c21412e50Ecae098E7f9) allowance for 0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B
-------------------
The allowance of **1 WETH** (contract address: **0x7b79995e5f793A07Bc00c21412e50Ecae098E7f9**) has been successfully set for the delegate **0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B** (one-time allowance).
- **Transaction Hash**: 0xd282d081f9cb2a437502eda1963818b0d45f1a5589b5ee0cacb734a5f436b1ff 

Running langchain-cdp-chatbot (could be different agent with other private-key, doesn't need to be owner of safe):

Prompt: wallet info
-------------------
Your wallet details are as follows:
- **Provider:** cdp_wallet_provider
- **Address:** 0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B
- **Network:**
- **Protocol Family:** EVM
- **Network ID:** ethereum-sepolia
- **Chain ID:** 11155111
- **Native Balance:** 0.24262 ETH
Prompt: get info for safe 0xC099a0dE7533f9F0A20979616376c353F330ed44
-------------------
Here's the information for the Safe at address **0xC099a0dE7533f9F0A20979616376c353F330ed44**:
- **Chain:** Sepolia
- **Owners:** - 0xb7f17bF94DBa50077be037CDc279a868f82D86C2
- 0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B
- **Threshold:** 1
- **Nonce:** 5
- **Modules:** 0xCFbFaC74C26F8647cBDb8c5caf80BB5b32E43134
- **Balance:** 0 ETH
- **Pending Transactions:** 0
Prompt: get allowance info for this safe and delegate 0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B
-------------------
Here is the allowance information for the delegate **0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B** from the Safe at address **0xC099a0dE7533f9F0A20979616376c353F330ed44**:
- **Token:** WETH (0x7b79995e5f793A07Bc00c21412e50Ecae098E7f9)
- **Current Safe Balance:** 0.00001 WETH
- **Allowance:** 1 available of 1 total (0 spent)
Prompt: withdraw full safe balance from allowance
-------------------
The full Safe balance of **0.00001 WETH** has been successfully withdrawn from the Safe **0xC099a0dE7533f9F0A20979616376c353F330ed44** to the delegate **0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B**.
- **Transaction Hash:** [0xf039503ffb1d213fb373f62cbb7867d99bd71127c8cab6a3e7cc9f2dc53d99c9](https://sepolia.etherscan.io/tx/0xf039503ffb1d213fb373f62cbb7867d99bd71127c8cab6a3e7cc9f2dc53d99c9)

@cb-heimdall

cb-heimdall commented Feb 10, 2025

Copy link
Copy Markdown

🟡 Heimdall Review Status

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

@phdargen
phdargen marked this pull request as draft February 10, 2025 06:28
@John-peterson-coinbase

Copy link
Copy Markdown
Contributor

@phdargen Thanks for the contribution!

A few initial comments:

  • The Safe smart account wallet likely should be a WalletProvider implementation so that you can easily use any of the existing actions with this new wallet type
  • If you would like actions on top of the wallet provider, you can add a SafeWalletActionProvider implementation similar to how CdpWalletActionProvider allows for CdpWalletProvider specific actions

Let me know if you have any questions or comments

@0xRAG0xRAG added action provider New action provider changes requested PR / Issue has changes requested banana typescript labels Feb 10, 2025
@phdargen

Copy link
Copy Markdown
ContributorAuthor

Thanks for the feedback @John-peterson-coinbase. That’s a good suggestion, I will look into it in the coming days. Only problem I see is that one couldn’t use cdp and safe actions at the same time but I guess thats not too bad in this case.

Before getting started, it would be good to clarify some design choices.
Lets say there is a safewalletprovider that is initialised with the private key of the agent and then creates a new safe account with the agent as signer (or connects to exciting safe). Then should the getAddress(), getBalance(), … return the address/balance of the safe rather than the agent wallet? Similar all tx should be done with the safe account, not the agent wallet, whose sole purpose would be to act as signer.

@phdargen

Copy link
Copy Markdown
ContributorAuthor

Hi @John-peterson-coinbase, I got a minimal example working for the new setup:

  • safeWalletProvider connects to an existing safe account or automatically creates a new one with the provided private key as single signer.

  • Get_wallet_info returns the address and balance of the safe, native_transfer sends eth from the safe (or propose tx).

  • Additional signers can be added with a safeWalletActionProvider.

  • safeApiActionProvider implements read-only actions such as the info of a safe account. The safeApiActionProvider actions can also be used with other EvmWalletProviders.

  • Added new langchain-safe-chatbot example

Please let me know if you agree with this implementation, then I'll go ahead and add the remaining actions and clean up by removing the old safeActionProvider

@phdargen

Copy link
Copy Markdown
ContributorAuthor

BTW, I submitted a related project at the safe agentathon (https://devfolio.co/projects/safegpt-d4c5) with a live demo here: https://safe-gpt.vercel.app/

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation wallet provider New wallet provider example New example agent needs triage labels Feb 24, 2025
@phdargen
phdargenforce-pushed the safe-ts branch 4 times, most recently from 82504f2 to c009f79CompareFebruary 26, 2025 17:38
@phdargen

Copy link
Copy Markdown
ContributorAuthor

This is in principle ready for review, however the actions in safeApiActionProvider rely on the safe api that is currently still down, see safe-global/safe-core-sdk#1155.

@phdargen

Copy link
Copy Markdown
ContributorAuthor

This is in principle ready for review, however the actions in safeApiActionProvider rely on the safe api that is currently still down, see safe-global/safe-core-sdk#1155.

The safe api is back and this is ready for review.
Changed the initial commit description to reflect changes.

error => {
throw new Error("Error initializing Safe wallet: " + error);
},
);

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

@John-peterson-coinbase@0xRAG@CarsonRoscoe any clue why the test-agentkit-typescript (20) units tests are failing? There seems to be a network timeout probably related to the fact that I do not call trackInitialization() immediately but wait until the Safe wallet is created (that requires an onchain tx). Running the test locally is successful and test-agentkit-typescript (18) seems to be fine too

@phdargen
phdargen marked this pull request as ready for review March 8, 2025 12:44
@phdargenphdargen changed the title feat: add safe smart account action (ts)feat: add safe wallet provider (ts)Mar 14, 2025
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

action providerNew action providerbananachanges requestedPR / Issue has changes requesteddocumentationImprovements or additions to documentationexampleNew example agenttypescriptwallet providerNew wallet provider

Development

Successfully merging this pull request may close these issues.

5 participants

@phdargen@cb-heimdall@John-peterson-coinbase@0xRAG@Tanker187
, '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: add safe wallet provider (ts) - #330

Open
phdargen wants to merge 9 commits into
coinbase:mainfrom
phdargen:safe-ts
Open

feat: add safe wallet provider (ts)#330
phdargen wants to merge 9 commits into
coinbase:mainfrom
phdargen:safe-ts

Conversation

@phdargen

@phdargenphdargen commented Feb 10, 2025

Copy link
Copy Markdown
Contributor

Why? What?

Every AI Agent deserves a Safe wallet!

Adds new safeActionProvider to handle interactions with Safe multi-signature wallets.
This implementation uses the Safe sdk that is added as new dependency.

What changed?

  • New Wallet Provider: safeWalletProvider

    • connects to an existing safe account
    • or automatically creates a new one with the provided private key as single signer
    • getBalance() returns balance of safe wallet, not private-key of agent
    • nativeTransfer() transfers eth from Safe wallet, may require approval of other signers
  • New Action Provider: safeWalletActionProvider with actions:

    • addSigner: Add a new signer to a Safe wallet
    • removeSigner: Remove an existing signer from a Safe wallet
    • changeThreshold: Modify the number of required signatures
    • approvePending: Approve a pending transaction
    • enableAllowanceModule: Activate the allowance module for a Safe
    • setAllowance: Configure spending allowances for specific addresses
  • New Action Provider: safeApiActionProvider with actions:

    • safeInfo: Retrieve detailed information about a Safe wallet
    • getAllowanceInfo: Get current allowance configurations
    • withdrawAllowance: Withdraw funds from an allowance
  • Adds new getPublicClient() method to EvmWalletProvider, so the publicClient can be passed to safeApiActionProvider

  • Adds new signHash() method to EvmWalletProvider that is needed for withdrawAllowance action

  • Adds new langchain-safe-chatbot example

  • Adds safeApiActionProvider to langchain-cdp-chatbot example

Network support

  • All evm chains

How has it been tested?

  • Unit tests
  • Agent tested, example prompts below (using gpt-4o-mini)

Running langchain-safe-chatbot without setting SAFE_ADDRESS:

Prompt: wallet info
-------------
Your wallet details are as follows:
- **Address:** 0xC099a0dE7533f9F0A20979616376c353F330ed44
- **Network:** - **Protocol Family:** EVM
- **Network ID:** ethereum-sepolia
- **Chain ID:** 11155111
- **Native Balance:** 0 WEI
Prompt: safe info
-------------
Here is the information about your Safe:
- **Safe Address:** 0xC099a0dE7533f9F0A20979616376c353F330ed44
- **Chain:** Sepolia
- **Owners:** 1 owner - 0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B
- **Threshold:** 1
- **Nonce:** 0
- **Modules:** None
- **Balance:** 0 ETH
- **Pending Transactions:** 0
Prompt: enable allowance module
-------------------
The allowance module has been successfully enabled for your Safe wallet at address **0xC099a0dE7533f9F0A20979616376c353F330ed44**. - **Transaction Hash**: 0x3eb5159d9a84324a199614a23ba0abfb1f10d56ac35865b988fd99a7c13fc0a6
Prompt: add 0xb7f17bF94DBa50077be037CDc279a868f82D86C2 as signer with threshold 2
-------------------
The signer **0xb7f17bF94DBa50077be037CDc279a868f82D86C2** has been successfully added to your Safe wallet. - **New Threshold**: 2
- **Transaction Hash**: 0x3f1a4de3647c0ee9d7b6e271f8dde7b096ee84437a21e6dabae7531bbce695e0
Prompt: I sent some eth to the safe, withdraw it all to 0xb7f17bF94DBa50077be037CDc279a868f82D86C2
-------------------
The withdrawal transaction has been proposed to transfer **0.00001 ETH** to **0xb7f17bF94DBa50077be037CDc279a868f82D86C2**.
- **Transaction Hash**: 0xdf0fd67b648f7bac6b1684436dd44f812de8963f66f77423bd654cb2d2b78f83
As the threshold is 2, other owners will need to confirm this transaction before it can be executed.
Prompt: safe info
-------------------
Here is the updated information for your Safe multi-sig wallet:
- **Safe Address**: 0xC099a0dE7533f9F0A20979616376c353F330ed44
- **Chain**: Sepolia
- **Owners**: 2 owners - 1. 0xb7f17bF94DBa50077be037CDc279a868f82D86C2
- 2. 0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B
- **Threshold**: 2 (requires two signatures for execution)
- **Nonce**: 2
- **Modules**: 0xCFbFaC74C26F8647cBDb8c5caf80BB5b32E43134
- **Balance**: 0.00001 ETH
- **Pending Transactions**: 1
- **Transaction** 0xdf0fd67b648f7bac6b1684436dd44f812de8963f66f77423bd654cb2d2b78f83 (1/2 confirmations, confirmed by: 0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B)
Prompt: I approved the tx and proposed a new one to change threshold to 1 with safe tx hash: 0xdbc8583147403145e9ea04f5725b3d998f945abe32ce6de5dc8b80cba0841c86. approve and execute it
-------------------
I successfully approved and executed the transaction:
- **Safe Transaction Hash**: 0xdbc8583147403145e9ea04f5725b3d998f945abe32ce6de5dc8b80cba0841c86
- **Execution Transaction Hash**: 0x4c6b40aceeb670f5e3230a34cc30da6fa26681a4f3446b55e3f5d5858226caed
The threshold has now been changed to 1.
Prompt: add 1 weth (0x7b79995e5f793A07Bc00c21412e50Ecae098E7f9) allowance for 0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B
-------------------
The allowance of **1 WETH** (contract address: **0x7b79995e5f793A07Bc00c21412e50Ecae098E7f9**) has been successfully set for the delegate **0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B** (one-time allowance).
- **Transaction Hash**: 0xd282d081f9cb2a437502eda1963818b0d45f1a5589b5ee0cacb734a5f436b1ff 

Running langchain-cdp-chatbot (could be different agent with other private-key, doesn't need to be owner of safe):

Prompt: wallet info
-------------------
Your wallet details are as follows:
- **Provider:** cdp_wallet_provider
- **Address:** 0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B
- **Network:**
- **Protocol Family:** EVM
- **Network ID:** ethereum-sepolia
- **Chain ID:** 11155111
- **Native Balance:** 0.24262 ETH
Prompt: get info for safe 0xC099a0dE7533f9F0A20979616376c353F330ed44
-------------------
Here's the information for the Safe at address **0xC099a0dE7533f9F0A20979616376c353F330ed44**:
- **Chain:** Sepolia
- **Owners:** - 0xb7f17bF94DBa50077be037CDc279a868f82D86C2
- 0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B
- **Threshold:** 1
- **Nonce:** 5
- **Modules:** 0xCFbFaC74C26F8647cBDb8c5caf80BB5b32E43134
- **Balance:** 0 ETH
- **Pending Transactions:** 0
Prompt: get allowance info for this safe and delegate 0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B
-------------------
Here is the allowance information for the delegate **0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B** from the Safe at address **0xC099a0dE7533f9F0A20979616376c353F330ed44**:
- **Token:** WETH (0x7b79995e5f793A07Bc00c21412e50Ecae098E7f9)
- **Current Safe Balance:** 0.00001 WETH
- **Allowance:** 1 available of 1 total (0 spent)
Prompt: withdraw full safe balance from allowance
-------------------
The full Safe balance of **0.00001 WETH** has been successfully withdrawn from the Safe **0xC099a0dE7533f9F0A20979616376c353F330ed44** to the delegate **0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B**.
- **Transaction Hash:** [0xf039503ffb1d213fb373f62cbb7867d99bd71127c8cab6a3e7cc9f2dc53d99c9](https://sepolia.etherscan.io/tx/0xf039503ffb1d213fb373f62cbb7867d99bd71127c8cab6a3e7cc9f2dc53d99c9)

@cb-heimdall

cb-heimdall commented Feb 10, 2025

Copy link
Copy Markdown

🟡 Heimdall Review Status

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

@phdargen
phdargen marked this pull request as draft February 10, 2025 06:28
@John-peterson-coinbase

Copy link
Copy Markdown
Contributor

@phdargen Thanks for the contribution!

A few initial comments:

  • The Safe smart account wallet likely should be a WalletProvider implementation so that you can easily use any of the existing actions with this new wallet type
  • If you would like actions on top of the wallet provider, you can add a SafeWalletActionProvider implementation similar to how CdpWalletActionProvider allows for CdpWalletProvider specific actions

Let me know if you have any questions or comments

@0xRAG0xRAG added action provider New action provider changes requested PR / Issue has changes requested banana typescript labels Feb 10, 2025
@phdargen

Copy link
Copy Markdown
ContributorAuthor

Thanks for the feedback @John-peterson-coinbase. That’s a good suggestion, I will look into it in the coming days. Only problem I see is that one couldn’t use cdp and safe actions at the same time but I guess thats not too bad in this case.

Before getting started, it would be good to clarify some design choices.
Lets say there is a safewalletprovider that is initialised with the private key of the agent and then creates a new safe account with the agent as signer (or connects to exciting safe). Then should the getAddress(), getBalance(), … return the address/balance of the safe rather than the agent wallet? Similar all tx should be done with the safe account, not the agent wallet, whose sole purpose would be to act as signer.

@phdargen

Copy link
Copy Markdown
ContributorAuthor

Hi @John-peterson-coinbase, I got a minimal example working for the new setup:

  • safeWalletProvider connects to an existing safe account or automatically creates a new one with the provided private key as single signer.

  • Get_wallet_info returns the address and balance of the safe, native_transfer sends eth from the safe (or propose tx).

  • Additional signers can be added with a safeWalletActionProvider.

  • safeApiActionProvider implements read-only actions such as the info of a safe account. The safeApiActionProvider actions can also be used with other EvmWalletProviders.

  • Added new langchain-safe-chatbot example

Please let me know if you agree with this implementation, then I'll go ahead and add the remaining actions and clean up by removing the old safeActionProvider

@phdargen

Copy link
Copy Markdown
ContributorAuthor

BTW, I submitted a related project at the safe agentathon (https://devfolio.co/projects/safegpt-d4c5) with a live demo here: https://safe-gpt.vercel.app/

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation wallet provider New wallet provider example New example agent needs triage labels Feb 24, 2025
@phdargen
phdargenforce-pushed the safe-ts branch 4 times, most recently from 82504f2 to c009f79CompareFebruary 26, 2025 17:38
@phdargen

Copy link
Copy Markdown
ContributorAuthor

This is in principle ready for review, however the actions in safeApiActionProvider rely on the safe api that is currently still down, see safe-global/safe-core-sdk#1155.

@phdargen

Copy link
Copy Markdown
ContributorAuthor

This is in principle ready for review, however the actions in safeApiActionProvider rely on the safe api that is currently still down, see safe-global/safe-core-sdk#1155.

The safe api is back and this is ready for review.
Changed the initial commit description to reflect changes.

error => {
throw new Error("Error initializing Safe wallet: " + error);
},
);

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

@John-peterson-coinbase@0xRAG@CarsonRoscoe any clue why the test-agentkit-typescript (20) units tests are failing? There seems to be a network timeout probably related to the fact that I do not call trackInitialization() immediately but wait until the Safe wallet is created (that requires an onchain tx). Running the test locally is successful and test-agentkit-typescript (18) seems to be fine too

@phdargen
phdargen marked this pull request as ready for review March 8, 2025 12:44
@phdargenphdargen changed the title feat: add safe smart account action (ts)feat: add safe wallet provider (ts)Mar 14, 2025
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

action providerNew action providerbananachanges requestedPR / Issue has changes requesteddocumentationImprovements or additions to documentationexampleNew example agenttypescriptwallet providerNew wallet provider

Development

Successfully merging this pull request may close these issues.

5 participants

@phdargen@cb-heimdall@John-peterson-coinbase@0xRAG@Tanker187
, '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: add safe wallet provider (ts) - #330

Open
phdargen wants to merge 9 commits into
coinbase:mainfrom
phdargen:safe-ts
Open

feat: add safe wallet provider (ts)#330
phdargen wants to merge 9 commits into
coinbase:mainfrom
phdargen:safe-ts

Conversation

@phdargen

@phdargenphdargen commented Feb 10, 2025

Copy link
Copy Markdown
Contributor

Why? What?

Every AI Agent deserves a Safe wallet!

Adds new safeActionProvider to handle interactions with Safe multi-signature wallets.
This implementation uses the Safe sdk that is added as new dependency.

What changed?

  • New Wallet Provider: safeWalletProvider

    • connects to an existing safe account
    • or automatically creates a new one with the provided private key as single signer
    • getBalance() returns balance of safe wallet, not private-key of agent
    • nativeTransfer() transfers eth from Safe wallet, may require approval of other signers
  • New Action Provider: safeWalletActionProvider with actions:

    • addSigner: Add a new signer to a Safe wallet
    • removeSigner: Remove an existing signer from a Safe wallet
    • changeThreshold: Modify the number of required signatures
    • approvePending: Approve a pending transaction
    • enableAllowanceModule: Activate the allowance module for a Safe
    • setAllowance: Configure spending allowances for specific addresses
  • New Action Provider: safeApiActionProvider with actions:

    • safeInfo: Retrieve detailed information about a Safe wallet
    • getAllowanceInfo: Get current allowance configurations
    • withdrawAllowance: Withdraw funds from an allowance
  • Adds new getPublicClient() method to EvmWalletProvider, so the publicClient can be passed to safeApiActionProvider

  • Adds new signHash() method to EvmWalletProvider that is needed for withdrawAllowance action

  • Adds new langchain-safe-chatbot example

  • Adds safeApiActionProvider to langchain-cdp-chatbot example

Network support

  • All evm chains

How has it been tested?

  • Unit tests
  • Agent tested, example prompts below (using gpt-4o-mini)

Running langchain-safe-chatbot without setting SAFE_ADDRESS:

Prompt: wallet info
-------------
Your wallet details are as follows:
- **Address:** 0xC099a0dE7533f9F0A20979616376c353F330ed44
- **Network:** - **Protocol Family:** EVM
- **Network ID:** ethereum-sepolia
- **Chain ID:** 11155111
- **Native Balance:** 0 WEI
Prompt: safe info
-------------
Here is the information about your Safe:
- **Safe Address:** 0xC099a0dE7533f9F0A20979616376c353F330ed44
- **Chain:** Sepolia
- **Owners:** 1 owner - 0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B
- **Threshold:** 1
- **Nonce:** 0
- **Modules:** None
- **Balance:** 0 ETH
- **Pending Transactions:** 0
Prompt: enable allowance module
-------------------
The allowance module has been successfully enabled for your Safe wallet at address **0xC099a0dE7533f9F0A20979616376c353F330ed44**. - **Transaction Hash**: 0x3eb5159d9a84324a199614a23ba0abfb1f10d56ac35865b988fd99a7c13fc0a6
Prompt: add 0xb7f17bF94DBa50077be037CDc279a868f82D86C2 as signer with threshold 2
-------------------
The signer **0xb7f17bF94DBa50077be037CDc279a868f82D86C2** has been successfully added to your Safe wallet. - **New Threshold**: 2
- **Transaction Hash**: 0x3f1a4de3647c0ee9d7b6e271f8dde7b096ee84437a21e6dabae7531bbce695e0
Prompt: I sent some eth to the safe, withdraw it all to 0xb7f17bF94DBa50077be037CDc279a868f82D86C2
-------------------
The withdrawal transaction has been proposed to transfer **0.00001 ETH** to **0xb7f17bF94DBa50077be037CDc279a868f82D86C2**.
- **Transaction Hash**: 0xdf0fd67b648f7bac6b1684436dd44f812de8963f66f77423bd654cb2d2b78f83
As the threshold is 2, other owners will need to confirm this transaction before it can be executed.
Prompt: safe info
-------------------
Here is the updated information for your Safe multi-sig wallet:
- **Safe Address**: 0xC099a0dE7533f9F0A20979616376c353F330ed44
- **Chain**: Sepolia
- **Owners**: 2 owners - 1. 0xb7f17bF94DBa50077be037CDc279a868f82D86C2
- 2. 0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B
- **Threshold**: 2 (requires two signatures for execution)
- **Nonce**: 2
- **Modules**: 0xCFbFaC74C26F8647cBDb8c5caf80BB5b32E43134
- **Balance**: 0.00001 ETH
- **Pending Transactions**: 1
- **Transaction** 0xdf0fd67b648f7bac6b1684436dd44f812de8963f66f77423bd654cb2d2b78f83 (1/2 confirmations, confirmed by: 0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B)
Prompt: I approved the tx and proposed a new one to change threshold to 1 with safe tx hash: 0xdbc8583147403145e9ea04f5725b3d998f945abe32ce6de5dc8b80cba0841c86. approve and execute it
-------------------
I successfully approved and executed the transaction:
- **Safe Transaction Hash**: 0xdbc8583147403145e9ea04f5725b3d998f945abe32ce6de5dc8b80cba0841c86
- **Execution Transaction Hash**: 0x4c6b40aceeb670f5e3230a34cc30da6fa26681a4f3446b55e3f5d5858226caed
The threshold has now been changed to 1.
Prompt: add 1 weth (0x7b79995e5f793A07Bc00c21412e50Ecae098E7f9) allowance for 0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B
-------------------
The allowance of **1 WETH** (contract address: **0x7b79995e5f793A07Bc00c21412e50Ecae098E7f9**) has been successfully set for the delegate **0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B** (one-time allowance).
- **Transaction Hash**: 0xd282d081f9cb2a437502eda1963818b0d45f1a5589b5ee0cacb734a5f436b1ff 

Running langchain-cdp-chatbot (could be different agent with other private-key, doesn't need to be owner of safe):

Prompt: wallet info
-------------------
Your wallet details are as follows:
- **Provider:** cdp_wallet_provider
- **Address:** 0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B
- **Network:**
- **Protocol Family:** EVM
- **Network ID:** ethereum-sepolia
- **Chain ID:** 11155111
- **Native Balance:** 0.24262 ETH
Prompt: get info for safe 0xC099a0dE7533f9F0A20979616376c353F330ed44
-------------------
Here's the information for the Safe at address **0xC099a0dE7533f9F0A20979616376c353F330ed44**:
- **Chain:** Sepolia
- **Owners:** - 0xb7f17bF94DBa50077be037CDc279a868f82D86C2
- 0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B
- **Threshold:** 1
- **Nonce:** 5
- **Modules:** 0xCFbFaC74C26F8647cBDb8c5caf80BB5b32E43134
- **Balance:** 0 ETH
- **Pending Transactions:** 0
Prompt: get allowance info for this safe and delegate 0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B
-------------------
Here is the allowance information for the delegate **0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B** from the Safe at address **0xC099a0dE7533f9F0A20979616376c353F330ed44**:
- **Token:** WETH (0x7b79995e5f793A07Bc00c21412e50Ecae098E7f9)
- **Current Safe Balance:** 0.00001 WETH
- **Allowance:** 1 available of 1 total (0 spent)
Prompt: withdraw full safe balance from allowance
-------------------
The full Safe balance of **0.00001 WETH** has been successfully withdrawn from the Safe **0xC099a0dE7533f9F0A20979616376c353F330ed44** to the delegate **0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B**.
- **Transaction Hash:** [0xf039503ffb1d213fb373f62cbb7867d99bd71127c8cab6a3e7cc9f2dc53d99c9](https://sepolia.etherscan.io/tx/0xf039503ffb1d213fb373f62cbb7867d99bd71127c8cab6a3e7cc9f2dc53d99c9)

@cb-heimdall

cb-heimdall commented Feb 10, 2025

Copy link
Copy Markdown

🟡 Heimdall Review Status

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

@phdargen
phdargen marked this pull request as draft February 10, 2025 06:28
@John-peterson-coinbase

Copy link
Copy Markdown
Contributor

@phdargen Thanks for the contribution!

A few initial comments:

  • The Safe smart account wallet likely should be a WalletProvider implementation so that you can easily use any of the existing actions with this new wallet type
  • If you would like actions on top of the wallet provider, you can add a SafeWalletActionProvider implementation similar to how CdpWalletActionProvider allows for CdpWalletProvider specific actions

Let me know if you have any questions or comments

@0xRAG0xRAG added action provider New action provider changes requested PR / Issue has changes requested banana typescript labels Feb 10, 2025
@phdargen

Copy link
Copy Markdown
ContributorAuthor

Thanks for the feedback @John-peterson-coinbase. That’s a good suggestion, I will look into it in the coming days. Only problem I see is that one couldn’t use cdp and safe actions at the same time but I guess thats not too bad in this case.

Before getting started, it would be good to clarify some design choices.
Lets say there is a safewalletprovider that is initialised with the private key of the agent and then creates a new safe account with the agent as signer (or connects to exciting safe). Then should the getAddress(), getBalance(), … return the address/balance of the safe rather than the agent wallet? Similar all tx should be done with the safe account, not the agent wallet, whose sole purpose would be to act as signer.

@phdargen

Copy link
Copy Markdown
ContributorAuthor

Hi @John-peterson-coinbase, I got a minimal example working for the new setup:

  • safeWalletProvider connects to an existing safe account or automatically creates a new one with the provided private key as single signer.

  • Get_wallet_info returns the address and balance of the safe, native_transfer sends eth from the safe (or propose tx).

  • Additional signers can be added with a safeWalletActionProvider.

  • safeApiActionProvider implements read-only actions such as the info of a safe account. The safeApiActionProvider actions can also be used with other EvmWalletProviders.

  • Added new langchain-safe-chatbot example

Please let me know if you agree with this implementation, then I'll go ahead and add the remaining actions and clean up by removing the old safeActionProvider

@phdargen

Copy link
Copy Markdown
ContributorAuthor

BTW, I submitted a related project at the safe agentathon (https://devfolio.co/projects/safegpt-d4c5) with a live demo here: https://safe-gpt.vercel.app/

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation wallet provider New wallet provider example New example agent needs triage labels Feb 24, 2025
@phdargen
phdargenforce-pushed the safe-ts branch 4 times, most recently from 82504f2 to c009f79CompareFebruary 26, 2025 17:38
@phdargen

Copy link
Copy Markdown
ContributorAuthor

This is in principle ready for review, however the actions in safeApiActionProvider rely on the safe api that is currently still down, see safe-global/safe-core-sdk#1155.

@phdargen

Copy link
Copy Markdown
ContributorAuthor

This is in principle ready for review, however the actions in safeApiActionProvider rely on the safe api that is currently still down, see safe-global/safe-core-sdk#1155.

The safe api is back and this is ready for review.
Changed the initial commit description to reflect changes.

error => {
throw new Error("Error initializing Safe wallet: " + error);
},
);

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

@John-peterson-coinbase@0xRAG@CarsonRoscoe any clue why the test-agentkit-typescript (20) units tests are failing? There seems to be a network timeout probably related to the fact that I do not call trackInitialization() immediately but wait until the Safe wallet is created (that requires an onchain tx). Running the test locally is successful and test-agentkit-typescript (18) seems to be fine too

@phdargen
phdargen marked this pull request as ready for review March 8, 2025 12:44
@phdargenphdargen changed the title feat: add safe smart account action (ts)feat: add safe wallet provider (ts)Mar 14, 2025
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

action providerNew action providerbananachanges requestedPR / Issue has changes requesteddocumentationImprovements or additions to documentationexampleNew example agenttypescriptwallet providerNew wallet provider

Development

Successfully merging this pull request may close these issues.

5 participants

@phdargen@cb-heimdall@John-peterson-coinbase@0xRAG@Tanker187
, '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: add safe wallet provider (ts) - #330

Open
phdargen wants to merge 9 commits into
coinbase:mainfrom
phdargen:safe-ts
Open

feat: add safe wallet provider (ts)#330
phdargen wants to merge 9 commits into
coinbase:mainfrom
phdargen:safe-ts

Conversation

@phdargen

@phdargenphdargen commented Feb 10, 2025

Copy link
Copy Markdown
Contributor

Why? What?

Every AI Agent deserves a Safe wallet!

Adds new safeActionProvider to handle interactions with Safe multi-signature wallets.
This implementation uses the Safe sdk that is added as new dependency.

What changed?

  • New Wallet Provider: safeWalletProvider

    • connects to an existing safe account
    • or automatically creates a new one with the provided private key as single signer
    • getBalance() returns balance of safe wallet, not private-key of agent
    • nativeTransfer() transfers eth from Safe wallet, may require approval of other signers
  • New Action Provider: safeWalletActionProvider with actions:

    • addSigner: Add a new signer to a Safe wallet
    • removeSigner: Remove an existing signer from a Safe wallet
    • changeThreshold: Modify the number of required signatures
    • approvePending: Approve a pending transaction
    • enableAllowanceModule: Activate the allowance module for a Safe
    • setAllowance: Configure spending allowances for specific addresses
  • New Action Provider: safeApiActionProvider with actions:

    • safeInfo: Retrieve detailed information about a Safe wallet
    • getAllowanceInfo: Get current allowance configurations
    • withdrawAllowance: Withdraw funds from an allowance
  • Adds new getPublicClient() method to EvmWalletProvider, so the publicClient can be passed to safeApiActionProvider

  • Adds new signHash() method to EvmWalletProvider that is needed for withdrawAllowance action

  • Adds new langchain-safe-chatbot example

  • Adds safeApiActionProvider to langchain-cdp-chatbot example

Network support

  • All evm chains

How has it been tested?

  • Unit tests
  • Agent tested, example prompts below (using gpt-4o-mini)

Running langchain-safe-chatbot without setting SAFE_ADDRESS:

Prompt: wallet info
-------------
Your wallet details are as follows:
- **Address:** 0xC099a0dE7533f9F0A20979616376c353F330ed44
- **Network:** - **Protocol Family:** EVM
- **Network ID:** ethereum-sepolia
- **Chain ID:** 11155111
- **Native Balance:** 0 WEI
Prompt: safe info
-------------
Here is the information about your Safe:
- **Safe Address:** 0xC099a0dE7533f9F0A20979616376c353F330ed44
- **Chain:** Sepolia
- **Owners:** 1 owner - 0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B
- **Threshold:** 1
- **Nonce:** 0
- **Modules:** None
- **Balance:** 0 ETH
- **Pending Transactions:** 0
Prompt: enable allowance module
-------------------
The allowance module has been successfully enabled for your Safe wallet at address **0xC099a0dE7533f9F0A20979616376c353F330ed44**. - **Transaction Hash**: 0x3eb5159d9a84324a199614a23ba0abfb1f10d56ac35865b988fd99a7c13fc0a6
Prompt: add 0xb7f17bF94DBa50077be037CDc279a868f82D86C2 as signer with threshold 2
-------------------
The signer **0xb7f17bF94DBa50077be037CDc279a868f82D86C2** has been successfully added to your Safe wallet. - **New Threshold**: 2
- **Transaction Hash**: 0x3f1a4de3647c0ee9d7b6e271f8dde7b096ee84437a21e6dabae7531bbce695e0
Prompt: I sent some eth to the safe, withdraw it all to 0xb7f17bF94DBa50077be037CDc279a868f82D86C2
-------------------
The withdrawal transaction has been proposed to transfer **0.00001 ETH** to **0xb7f17bF94DBa50077be037CDc279a868f82D86C2**.
- **Transaction Hash**: 0xdf0fd67b648f7bac6b1684436dd44f812de8963f66f77423bd654cb2d2b78f83
As the threshold is 2, other owners will need to confirm this transaction before it can be executed.
Prompt: safe info
-------------------
Here is the updated information for your Safe multi-sig wallet:
- **Safe Address**: 0xC099a0dE7533f9F0A20979616376c353F330ed44
- **Chain**: Sepolia
- **Owners**: 2 owners - 1. 0xb7f17bF94DBa50077be037CDc279a868f82D86C2
- 2. 0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B
- **Threshold**: 2 (requires two signatures for execution)
- **Nonce**: 2
- **Modules**: 0xCFbFaC74C26F8647cBDb8c5caf80BB5b32E43134
- **Balance**: 0.00001 ETH
- **Pending Transactions**: 1
- **Transaction** 0xdf0fd67b648f7bac6b1684436dd44f812de8963f66f77423bd654cb2d2b78f83 (1/2 confirmations, confirmed by: 0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B)
Prompt: I approved the tx and proposed a new one to change threshold to 1 with safe tx hash: 0xdbc8583147403145e9ea04f5725b3d998f945abe32ce6de5dc8b80cba0841c86. approve and execute it
-------------------
I successfully approved and executed the transaction:
- **Safe Transaction Hash**: 0xdbc8583147403145e9ea04f5725b3d998f945abe32ce6de5dc8b80cba0841c86
- **Execution Transaction Hash**: 0x4c6b40aceeb670f5e3230a34cc30da6fa26681a4f3446b55e3f5d5858226caed
The threshold has now been changed to 1.
Prompt: add 1 weth (0x7b79995e5f793A07Bc00c21412e50Ecae098E7f9) allowance for 0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B
-------------------
The allowance of **1 WETH** (contract address: **0x7b79995e5f793A07Bc00c21412e50Ecae098E7f9**) has been successfully set for the delegate **0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B** (one-time allowance).
- **Transaction Hash**: 0xd282d081f9cb2a437502eda1963818b0d45f1a5589b5ee0cacb734a5f436b1ff 

Running langchain-cdp-chatbot (could be different agent with other private-key, doesn't need to be owner of safe):

Prompt: wallet info
-------------------
Your wallet details are as follows:
- **Provider:** cdp_wallet_provider
- **Address:** 0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B
- **Network:**
- **Protocol Family:** EVM
- **Network ID:** ethereum-sepolia
- **Chain ID:** 11155111
- **Native Balance:** 0.24262 ETH
Prompt: get info for safe 0xC099a0dE7533f9F0A20979616376c353F330ed44
-------------------
Here's the information for the Safe at address **0xC099a0dE7533f9F0A20979616376c353F330ed44**:
- **Chain:** Sepolia
- **Owners:** - 0xb7f17bF94DBa50077be037CDc279a868f82D86C2
- 0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B
- **Threshold:** 1
- **Nonce:** 5
- **Modules:** 0xCFbFaC74C26F8647cBDb8c5caf80BB5b32E43134
- **Balance:** 0 ETH
- **Pending Transactions:** 0
Prompt: get allowance info for this safe and delegate 0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B
-------------------
Here is the allowance information for the delegate **0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B** from the Safe at address **0xC099a0dE7533f9F0A20979616376c353F330ed44**:
- **Token:** WETH (0x7b79995e5f793A07Bc00c21412e50Ecae098E7f9)
- **Current Safe Balance:** 0.00001 WETH
- **Allowance:** 1 available of 1 total (0 spent)
Prompt: withdraw full safe balance from allowance
-------------------
The full Safe balance of **0.00001 WETH** has been successfully withdrawn from the Safe **0xC099a0dE7533f9F0A20979616376c353F330ed44** to the delegate **0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B**.
- **Transaction Hash:** [0xf039503ffb1d213fb373f62cbb7867d99bd71127c8cab6a3e7cc9f2dc53d99c9](https://sepolia.etherscan.io/tx/0xf039503ffb1d213fb373f62cbb7867d99bd71127c8cab6a3e7cc9f2dc53d99c9)

@cb-heimdall

cb-heimdall commented Feb 10, 2025

Copy link
Copy Markdown

🟡 Heimdall Review Status

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

@phdargen
phdargen marked this pull request as draft February 10, 2025 06:28
@John-peterson-coinbase

Copy link
Copy Markdown
Contributor

@phdargen Thanks for the contribution!

A few initial comments:

  • The Safe smart account wallet likely should be a WalletProvider implementation so that you can easily use any of the existing actions with this new wallet type
  • If you would like actions on top of the wallet provider, you can add a SafeWalletActionProvider implementation similar to how CdpWalletActionProvider allows for CdpWalletProvider specific actions

Let me know if you have any questions or comments

@0xRAG0xRAG added action provider New action provider changes requested PR / Issue has changes requested banana typescript labels Feb 10, 2025
@phdargen

Copy link
Copy Markdown
ContributorAuthor

Thanks for the feedback @John-peterson-coinbase. That’s a good suggestion, I will look into it in the coming days. Only problem I see is that one couldn’t use cdp and safe actions at the same time but I guess thats not too bad in this case.

Before getting started, it would be good to clarify some design choices.
Lets say there is a safewalletprovider that is initialised with the private key of the agent and then creates a new safe account with the agent as signer (or connects to exciting safe). Then should the getAddress(), getBalance(), … return the address/balance of the safe rather than the agent wallet? Similar all tx should be done with the safe account, not the agent wallet, whose sole purpose would be to act as signer.

@phdargen

Copy link
Copy Markdown
ContributorAuthor

Hi @John-peterson-coinbase, I got a minimal example working for the new setup:

  • safeWalletProvider connects to an existing safe account or automatically creates a new one with the provided private key as single signer.

  • Get_wallet_info returns the address and balance of the safe, native_transfer sends eth from the safe (or propose tx).

  • Additional signers can be added with a safeWalletActionProvider.

  • safeApiActionProvider implements read-only actions such as the info of a safe account. The safeApiActionProvider actions can also be used with other EvmWalletProviders.

  • Added new langchain-safe-chatbot example

Please let me know if you agree with this implementation, then I'll go ahead and add the remaining actions and clean up by removing the old safeActionProvider

@phdargen

Copy link
Copy Markdown
ContributorAuthor

BTW, I submitted a related project at the safe agentathon (https://devfolio.co/projects/safegpt-d4c5) with a live demo here: https://safe-gpt.vercel.app/

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation wallet provider New wallet provider example New example agent needs triage labels Feb 24, 2025
@phdargen
phdargenforce-pushed the safe-ts branch 4 times, most recently from 82504f2 to c009f79CompareFebruary 26, 2025 17:38
@phdargen

Copy link
Copy Markdown
ContributorAuthor

This is in principle ready for review, however the actions in safeApiActionProvider rely on the safe api that is currently still down, see safe-global/safe-core-sdk#1155.

@phdargen

Copy link
Copy Markdown
ContributorAuthor

This is in principle ready for review, however the actions in safeApiActionProvider rely on the safe api that is currently still down, see safe-global/safe-core-sdk#1155.

The safe api is back and this is ready for review.
Changed the initial commit description to reflect changes.

error => {
throw new Error("Error initializing Safe wallet: " + error);
},
);

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

@John-peterson-coinbase@0xRAG@CarsonRoscoe any clue why the test-agentkit-typescript (20) units tests are failing? There seems to be a network timeout probably related to the fact that I do not call trackInitialization() immediately but wait until the Safe wallet is created (that requires an onchain tx). Running the test locally is successful and test-agentkit-typescript (18) seems to be fine too

@phdargen
phdargen marked this pull request as ready for review March 8, 2025 12:44
@phdargenphdargen changed the title feat: add safe smart account action (ts)feat: add safe wallet provider (ts)Mar 14, 2025
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

action providerNew action providerbananachanges requestedPR / Issue has changes requesteddocumentationImprovements or additions to documentationexampleNew example agenttypescriptwallet providerNew wallet provider

Development

Successfully merging this pull request may close these issues.

5 participants

@phdargen@cb-heimdall@John-peterson-coinbase@0xRAG@Tanker187
, '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: add safe wallet provider (ts) - #330

Open
phdargen wants to merge 9 commits into
coinbase:mainfrom
phdargen:safe-ts
Open

feat: add safe wallet provider (ts)#330
phdargen wants to merge 9 commits into
coinbase:mainfrom
phdargen:safe-ts

Conversation

@phdargen

@phdargenphdargen commented Feb 10, 2025

Copy link
Copy Markdown
Contributor

Why? What?

Every AI Agent deserves a Safe wallet!

Adds new safeActionProvider to handle interactions with Safe multi-signature wallets.
This implementation uses the Safe sdk that is added as new dependency.

What changed?

  • New Wallet Provider: safeWalletProvider

    • connects to an existing safe account
    • or automatically creates a new one with the provided private key as single signer
    • getBalance() returns balance of safe wallet, not private-key of agent
    • nativeTransfer() transfers eth from Safe wallet, may require approval of other signers
  • New Action Provider: safeWalletActionProvider with actions:

    • addSigner: Add a new signer to a Safe wallet
    • removeSigner: Remove an existing signer from a Safe wallet
    • changeThreshold: Modify the number of required signatures
    • approvePending: Approve a pending transaction
    • enableAllowanceModule: Activate the allowance module for a Safe
    • setAllowance: Configure spending allowances for specific addresses
  • New Action Provider: safeApiActionProvider with actions:

    • safeInfo: Retrieve detailed information about a Safe wallet
    • getAllowanceInfo: Get current allowance configurations
    • withdrawAllowance: Withdraw funds from an allowance
  • Adds new getPublicClient() method to EvmWalletProvider, so the publicClient can be passed to safeApiActionProvider

  • Adds new signHash() method to EvmWalletProvider that is needed for withdrawAllowance action

  • Adds new langchain-safe-chatbot example

  • Adds safeApiActionProvider to langchain-cdp-chatbot example

Network support

  • All evm chains

How has it been tested?

  • Unit tests
  • Agent tested, example prompts below (using gpt-4o-mini)

Running langchain-safe-chatbot without setting SAFE_ADDRESS:

Prompt: wallet info
-------------
Your wallet details are as follows:
- **Address:** 0xC099a0dE7533f9F0A20979616376c353F330ed44
- **Network:** - **Protocol Family:** EVM
- **Network ID:** ethereum-sepolia
- **Chain ID:** 11155111
- **Native Balance:** 0 WEI
Prompt: safe info
-------------
Here is the information about your Safe:
- **Safe Address:** 0xC099a0dE7533f9F0A20979616376c353F330ed44
- **Chain:** Sepolia
- **Owners:** 1 owner - 0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B
- **Threshold:** 1
- **Nonce:** 0
- **Modules:** None
- **Balance:** 0 ETH
- **Pending Transactions:** 0
Prompt: enable allowance module
-------------------
The allowance module has been successfully enabled for your Safe wallet at address **0xC099a0dE7533f9F0A20979616376c353F330ed44**. - **Transaction Hash**: 0x3eb5159d9a84324a199614a23ba0abfb1f10d56ac35865b988fd99a7c13fc0a6
Prompt: add 0xb7f17bF94DBa50077be037CDc279a868f82D86C2 as signer with threshold 2
-------------------
The signer **0xb7f17bF94DBa50077be037CDc279a868f82D86C2** has been successfully added to your Safe wallet. - **New Threshold**: 2
- **Transaction Hash**: 0x3f1a4de3647c0ee9d7b6e271f8dde7b096ee84437a21e6dabae7531bbce695e0
Prompt: I sent some eth to the safe, withdraw it all to 0xb7f17bF94DBa50077be037CDc279a868f82D86C2
-------------------
The withdrawal transaction has been proposed to transfer **0.00001 ETH** to **0xb7f17bF94DBa50077be037CDc279a868f82D86C2**.
- **Transaction Hash**: 0xdf0fd67b648f7bac6b1684436dd44f812de8963f66f77423bd654cb2d2b78f83
As the threshold is 2, other owners will need to confirm this transaction before it can be executed.
Prompt: safe info
-------------------
Here is the updated information for your Safe multi-sig wallet:
- **Safe Address**: 0xC099a0dE7533f9F0A20979616376c353F330ed44
- **Chain**: Sepolia
- **Owners**: 2 owners - 1. 0xb7f17bF94DBa50077be037CDc279a868f82D86C2
- 2. 0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B
- **Threshold**: 2 (requires two signatures for execution)
- **Nonce**: 2
- **Modules**: 0xCFbFaC74C26F8647cBDb8c5caf80BB5b32E43134
- **Balance**: 0.00001 ETH
- **Pending Transactions**: 1
- **Transaction** 0xdf0fd67b648f7bac6b1684436dd44f812de8963f66f77423bd654cb2d2b78f83 (1/2 confirmations, confirmed by: 0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B)
Prompt: I approved the tx and proposed a new one to change threshold to 1 with safe tx hash: 0xdbc8583147403145e9ea04f5725b3d998f945abe32ce6de5dc8b80cba0841c86. approve and execute it
-------------------
I successfully approved and executed the transaction:
- **Safe Transaction Hash**: 0xdbc8583147403145e9ea04f5725b3d998f945abe32ce6de5dc8b80cba0841c86
- **Execution Transaction Hash**: 0x4c6b40aceeb670f5e3230a34cc30da6fa26681a4f3446b55e3f5d5858226caed
The threshold has now been changed to 1.
Prompt: add 1 weth (0x7b79995e5f793A07Bc00c21412e50Ecae098E7f9) allowance for 0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B
-------------------
The allowance of **1 WETH** (contract address: **0x7b79995e5f793A07Bc00c21412e50Ecae098E7f9**) has been successfully set for the delegate **0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B** (one-time allowance).
- **Transaction Hash**: 0xd282d081f9cb2a437502eda1963818b0d45f1a5589b5ee0cacb734a5f436b1ff 

Running langchain-cdp-chatbot (could be different agent with other private-key, doesn't need to be owner of safe):

Prompt: wallet info
-------------------
Your wallet details are as follows:
- **Provider:** cdp_wallet_provider
- **Address:** 0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B
- **Network:**
- **Protocol Family:** EVM
- **Network ID:** ethereum-sepolia
- **Chain ID:** 11155111
- **Native Balance:** 0.24262 ETH
Prompt: get info for safe 0xC099a0dE7533f9F0A20979616376c353F330ed44
-------------------
Here's the information for the Safe at address **0xC099a0dE7533f9F0A20979616376c353F330ed44**:
- **Chain:** Sepolia
- **Owners:** - 0xb7f17bF94DBa50077be037CDc279a868f82D86C2
- 0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B
- **Threshold:** 1
- **Nonce:** 5
- **Modules:** 0xCFbFaC74C26F8647cBDb8c5caf80BB5b32E43134
- **Balance:** 0 ETH
- **Pending Transactions:** 0
Prompt: get allowance info for this safe and delegate 0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B
-------------------
Here is the allowance information for the delegate **0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B** from the Safe at address **0xC099a0dE7533f9F0A20979616376c353F330ed44**:
- **Token:** WETH (0x7b79995e5f793A07Bc00c21412e50Ecae098E7f9)
- **Current Safe Balance:** 0.00001 WETH
- **Allowance:** 1 available of 1 total (0 spent)
Prompt: withdraw full safe balance from allowance
-------------------
The full Safe balance of **0.00001 WETH** has been successfully withdrawn from the Safe **0xC099a0dE7533f9F0A20979616376c353F330ed44** to the delegate **0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B**.
- **Transaction Hash:** [0xf039503ffb1d213fb373f62cbb7867d99bd71127c8cab6a3e7cc9f2dc53d99c9](https://sepolia.etherscan.io/tx/0xf039503ffb1d213fb373f62cbb7867d99bd71127c8cab6a3e7cc9f2dc53d99c9)

@cb-heimdall

cb-heimdall commented Feb 10, 2025

Copy link
Copy Markdown

🟡 Heimdall Review Status

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

@phdargen
phdargen marked this pull request as draft February 10, 2025 06:28
@John-peterson-coinbase

Copy link
Copy Markdown
Contributor

@phdargen Thanks for the contribution!

A few initial comments:

  • The Safe smart account wallet likely should be a WalletProvider implementation so that you can easily use any of the existing actions with this new wallet type
  • If you would like actions on top of the wallet provider, you can add a SafeWalletActionProvider implementation similar to how CdpWalletActionProvider allows for CdpWalletProvider specific actions

Let me know if you have any questions or comments

@0xRAG0xRAG added action provider New action provider changes requested PR / Issue has changes requested banana typescript labels Feb 10, 2025
@phdargen

Copy link
Copy Markdown
ContributorAuthor

Thanks for the feedback @John-peterson-coinbase. That’s a good suggestion, I will look into it in the coming days. Only problem I see is that one couldn’t use cdp and safe actions at the same time but I guess thats not too bad in this case.

Before getting started, it would be good to clarify some design choices.
Lets say there is a safewalletprovider that is initialised with the private key of the agent and then creates a new safe account with the agent as signer (or connects to exciting safe). Then should the getAddress(), getBalance(), … return the address/balance of the safe rather than the agent wallet? Similar all tx should be done with the safe account, not the agent wallet, whose sole purpose would be to act as signer.

@phdargen

Copy link
Copy Markdown
ContributorAuthor

Hi @John-peterson-coinbase, I got a minimal example working for the new setup:

  • safeWalletProvider connects to an existing safe account or automatically creates a new one with the provided private key as single signer.

  • Get_wallet_info returns the address and balance of the safe, native_transfer sends eth from the safe (or propose tx).

  • Additional signers can be added with a safeWalletActionProvider.

  • safeApiActionProvider implements read-only actions such as the info of a safe account. The safeApiActionProvider actions can also be used with other EvmWalletProviders.

  • Added new langchain-safe-chatbot example

Please let me know if you agree with this implementation, then I'll go ahead and add the remaining actions and clean up by removing the old safeActionProvider

@phdargen

Copy link
Copy Markdown
ContributorAuthor

BTW, I submitted a related project at the safe agentathon (https://devfolio.co/projects/safegpt-d4c5) with a live demo here: https://safe-gpt.vercel.app/

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation wallet provider New wallet provider example New example agent needs triage labels Feb 24, 2025
@phdargen
phdargenforce-pushed the safe-ts branch 4 times, most recently from 82504f2 to c009f79CompareFebruary 26, 2025 17:38
@phdargen

Copy link
Copy Markdown
ContributorAuthor

This is in principle ready for review, however the actions in safeApiActionProvider rely on the safe api that is currently still down, see safe-global/safe-core-sdk#1155.

@phdargen

Copy link
Copy Markdown
ContributorAuthor

This is in principle ready for review, however the actions in safeApiActionProvider rely on the safe api that is currently still down, see safe-global/safe-core-sdk#1155.

The safe api is back and this is ready for review.
Changed the initial commit description to reflect changes.

error => {
throw new Error("Error initializing Safe wallet: " + error);
},
);

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

@John-peterson-coinbase@0xRAG@CarsonRoscoe any clue why the test-agentkit-typescript (20) units tests are failing? There seems to be a network timeout probably related to the fact that I do not call trackInitialization() immediately but wait until the Safe wallet is created (that requires an onchain tx). Running the test locally is successful and test-agentkit-typescript (18) seems to be fine too

@phdargen
phdargen marked this pull request as ready for review March 8, 2025 12:44
@phdargenphdargen changed the title feat: add safe smart account action (ts)feat: add safe wallet provider (ts)Mar 14, 2025
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

action providerNew action providerbananachanges requestedPR / Issue has changes requesteddocumentationImprovements or additions to documentationexampleNew example agenttypescriptwallet providerNew wallet provider

Development

Successfully merging this pull request may close these issues.

5 participants

@phdargen@cb-heimdall@John-peterson-coinbase@0xRAG@Tanker187
, '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: add safe wallet provider (ts) - #330

Open
phdargen wants to merge 9 commits into
coinbase:mainfrom
phdargen:safe-ts
Open

feat: add safe wallet provider (ts)#330
phdargen wants to merge 9 commits into
coinbase:mainfrom
phdargen:safe-ts

Conversation

@phdargen

@phdargenphdargen commented Feb 10, 2025

Copy link
Copy Markdown
Contributor

Why? What?

Every AI Agent deserves a Safe wallet!

Adds new safeActionProvider to handle interactions with Safe multi-signature wallets.
This implementation uses the Safe sdk that is added as new dependency.

What changed?

  • New Wallet Provider: safeWalletProvider

    • connects to an existing safe account
    • or automatically creates a new one with the provided private key as single signer
    • getBalance() returns balance of safe wallet, not private-key of agent
    • nativeTransfer() transfers eth from Safe wallet, may require approval of other signers
  • New Action Provider: safeWalletActionProvider with actions:

    • addSigner: Add a new signer to a Safe wallet
    • removeSigner: Remove an existing signer from a Safe wallet
    • changeThreshold: Modify the number of required signatures
    • approvePending: Approve a pending transaction
    • enableAllowanceModule: Activate the allowance module for a Safe
    • setAllowance: Configure spending allowances for specific addresses
  • New Action Provider: safeApiActionProvider with actions:

    • safeInfo: Retrieve detailed information about a Safe wallet
    • getAllowanceInfo: Get current allowance configurations
    • withdrawAllowance: Withdraw funds from an allowance
  • Adds new getPublicClient() method to EvmWalletProvider, so the publicClient can be passed to safeApiActionProvider

  • Adds new signHash() method to EvmWalletProvider that is needed for withdrawAllowance action

  • Adds new langchain-safe-chatbot example

  • Adds safeApiActionProvider to langchain-cdp-chatbot example

Network support

  • All evm chains

How has it been tested?

  • Unit tests
  • Agent tested, example prompts below (using gpt-4o-mini)

Running langchain-safe-chatbot without setting SAFE_ADDRESS:

Prompt: wallet info
-------------
Your wallet details are as follows:
- **Address:** 0xC099a0dE7533f9F0A20979616376c353F330ed44
- **Network:** - **Protocol Family:** EVM
- **Network ID:** ethereum-sepolia
- **Chain ID:** 11155111
- **Native Balance:** 0 WEI
Prompt: safe info
-------------
Here is the information about your Safe:
- **Safe Address:** 0xC099a0dE7533f9F0A20979616376c353F330ed44
- **Chain:** Sepolia
- **Owners:** 1 owner - 0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B
- **Threshold:** 1
- **Nonce:** 0
- **Modules:** None
- **Balance:** 0 ETH
- **Pending Transactions:** 0
Prompt: enable allowance module
-------------------
The allowance module has been successfully enabled for your Safe wallet at address **0xC099a0dE7533f9F0A20979616376c353F330ed44**. - **Transaction Hash**: 0x3eb5159d9a84324a199614a23ba0abfb1f10d56ac35865b988fd99a7c13fc0a6
Prompt: add 0xb7f17bF94DBa50077be037CDc279a868f82D86C2 as signer with threshold 2
-------------------
The signer **0xb7f17bF94DBa50077be037CDc279a868f82D86C2** has been successfully added to your Safe wallet. - **New Threshold**: 2
- **Transaction Hash**: 0x3f1a4de3647c0ee9d7b6e271f8dde7b096ee84437a21e6dabae7531bbce695e0
Prompt: I sent some eth to the safe, withdraw it all to 0xb7f17bF94DBa50077be037CDc279a868f82D86C2
-------------------
The withdrawal transaction has been proposed to transfer **0.00001 ETH** to **0xb7f17bF94DBa50077be037CDc279a868f82D86C2**.
- **Transaction Hash**: 0xdf0fd67b648f7bac6b1684436dd44f812de8963f66f77423bd654cb2d2b78f83
As the threshold is 2, other owners will need to confirm this transaction before it can be executed.
Prompt: safe info
-------------------
Here is the updated information for your Safe multi-sig wallet:
- **Safe Address**: 0xC099a0dE7533f9F0A20979616376c353F330ed44
- **Chain**: Sepolia
- **Owners**: 2 owners - 1. 0xb7f17bF94DBa50077be037CDc279a868f82D86C2
- 2. 0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B
- **Threshold**: 2 (requires two signatures for execution)
- **Nonce**: 2
- **Modules**: 0xCFbFaC74C26F8647cBDb8c5caf80BB5b32E43134
- **Balance**: 0.00001 ETH
- **Pending Transactions**: 1
- **Transaction** 0xdf0fd67b648f7bac6b1684436dd44f812de8963f66f77423bd654cb2d2b78f83 (1/2 confirmations, confirmed by: 0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B)
Prompt: I approved the tx and proposed a new one to change threshold to 1 with safe tx hash: 0xdbc8583147403145e9ea04f5725b3d998f945abe32ce6de5dc8b80cba0841c86. approve and execute it
-------------------
I successfully approved and executed the transaction:
- **Safe Transaction Hash**: 0xdbc8583147403145e9ea04f5725b3d998f945abe32ce6de5dc8b80cba0841c86
- **Execution Transaction Hash**: 0x4c6b40aceeb670f5e3230a34cc30da6fa26681a4f3446b55e3f5d5858226caed
The threshold has now been changed to 1.
Prompt: add 1 weth (0x7b79995e5f793A07Bc00c21412e50Ecae098E7f9) allowance for 0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B
-------------------
The allowance of **1 WETH** (contract address: **0x7b79995e5f793A07Bc00c21412e50Ecae098E7f9**) has been successfully set for the delegate **0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B** (one-time allowance).
- **Transaction Hash**: 0xd282d081f9cb2a437502eda1963818b0d45f1a5589b5ee0cacb734a5f436b1ff 

Running langchain-cdp-chatbot (could be different agent with other private-key, doesn't need to be owner of safe):

Prompt: wallet info
-------------------
Your wallet details are as follows:
- **Provider:** cdp_wallet_provider
- **Address:** 0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B
- **Network:**
- **Protocol Family:** EVM
- **Network ID:** ethereum-sepolia
- **Chain ID:** 11155111
- **Native Balance:** 0.24262 ETH
Prompt: get info for safe 0xC099a0dE7533f9F0A20979616376c353F330ed44
-------------------
Here's the information for the Safe at address **0xC099a0dE7533f9F0A20979616376c353F330ed44**:
- **Chain:** Sepolia
- **Owners:** - 0xb7f17bF94DBa50077be037CDc279a868f82D86C2
- 0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B
- **Threshold:** 1
- **Nonce:** 5
- **Modules:** 0xCFbFaC74C26F8647cBDb8c5caf80BB5b32E43134
- **Balance:** 0 ETH
- **Pending Transactions:** 0
Prompt: get allowance info for this safe and delegate 0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B
-------------------
Here is the allowance information for the delegate **0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B** from the Safe at address **0xC099a0dE7533f9F0A20979616376c353F330ed44**:
- **Token:** WETH (0x7b79995e5f793A07Bc00c21412e50Ecae098E7f9)
- **Current Safe Balance:** 0.00001 WETH
- **Allowance:** 1 available of 1 total (0 spent)
Prompt: withdraw full safe balance from allowance
-------------------
The full Safe balance of **0.00001 WETH** has been successfully withdrawn from the Safe **0xC099a0dE7533f9F0A20979616376c353F330ed44** to the delegate **0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B**.
- **Transaction Hash:** [0xf039503ffb1d213fb373f62cbb7867d99bd71127c8cab6a3e7cc9f2dc53d99c9](https://sepolia.etherscan.io/tx/0xf039503ffb1d213fb373f62cbb7867d99bd71127c8cab6a3e7cc9f2dc53d99c9)

@cb-heimdall

cb-heimdall commented Feb 10, 2025

Copy link
Copy Markdown

🟡 Heimdall Review Status

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

@phdargen
phdargen marked this pull request as draft February 10, 2025 06:28
@John-peterson-coinbase

Copy link
Copy Markdown
Contributor

@phdargen Thanks for the contribution!

A few initial comments:

  • The Safe smart account wallet likely should be a WalletProvider implementation so that you can easily use any of the existing actions with this new wallet type
  • If you would like actions on top of the wallet provider, you can add a SafeWalletActionProvider implementation similar to how CdpWalletActionProvider allows for CdpWalletProvider specific actions

Let me know if you have any questions or comments

@0xRAG0xRAG added action provider New action provider changes requested PR / Issue has changes requested banana typescript labels Feb 10, 2025
@phdargen

Copy link
Copy Markdown
ContributorAuthor

Thanks for the feedback @John-peterson-coinbase. That’s a good suggestion, I will look into it in the coming days. Only problem I see is that one couldn’t use cdp and safe actions at the same time but I guess thats not too bad in this case.

Before getting started, it would be good to clarify some design choices.
Lets say there is a safewalletprovider that is initialised with the private key of the agent and then creates a new safe account with the agent as signer (or connects to exciting safe). Then should the getAddress(), getBalance(), … return the address/balance of the safe rather than the agent wallet? Similar all tx should be done with the safe account, not the agent wallet, whose sole purpose would be to act as signer.

@phdargen

Copy link
Copy Markdown
ContributorAuthor

Hi @John-peterson-coinbase, I got a minimal example working for the new setup:

  • safeWalletProvider connects to an existing safe account or automatically creates a new one with the provided private key as single signer.

  • Get_wallet_info returns the address and balance of the safe, native_transfer sends eth from the safe (or propose tx).

  • Additional signers can be added with a safeWalletActionProvider.

  • safeApiActionProvider implements read-only actions such as the info of a safe account. The safeApiActionProvider actions can also be used with other EvmWalletProviders.

  • Added new langchain-safe-chatbot example

Please let me know if you agree with this implementation, then I'll go ahead and add the remaining actions and clean up by removing the old safeActionProvider

@phdargen

Copy link
Copy Markdown
ContributorAuthor

BTW, I submitted a related project at the safe agentathon (https://devfolio.co/projects/safegpt-d4c5) with a live demo here: https://safe-gpt.vercel.app/

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation wallet provider New wallet provider example New example agent needs triage labels Feb 24, 2025
@phdargen
phdargenforce-pushed the safe-ts branch 4 times, most recently from 82504f2 to c009f79CompareFebruary 26, 2025 17:38
@phdargen

Copy link
Copy Markdown
ContributorAuthor

This is in principle ready for review, however the actions in safeApiActionProvider rely on the safe api that is currently still down, see safe-global/safe-core-sdk#1155.

@phdargen

Copy link
Copy Markdown
ContributorAuthor

This is in principle ready for review, however the actions in safeApiActionProvider rely on the safe api that is currently still down, see safe-global/safe-core-sdk#1155.

The safe api is back and this is ready for review.
Changed the initial commit description to reflect changes.

error => {
throw new Error("Error initializing Safe wallet: " + error);
},
);

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

@John-peterson-coinbase@0xRAG@CarsonRoscoe any clue why the test-agentkit-typescript (20) units tests are failing? There seems to be a network timeout probably related to the fact that I do not call trackInitialization() immediately but wait until the Safe wallet is created (that requires an onchain tx). Running the test locally is successful and test-agentkit-typescript (18) seems to be fine too

@phdargen
phdargen marked this pull request as ready for review March 8, 2025 12:44
@phdargenphdargen changed the title feat: add safe smart account action (ts)feat: add safe wallet provider (ts)Mar 14, 2025
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

action providerNew action providerbananachanges requestedPR / Issue has changes requesteddocumentationImprovements or additions to documentationexampleNew example agenttypescriptwallet providerNew wallet provider

Development

Successfully merging this pull request may close these issues.

5 participants

@phdargen@cb-heimdall@John-peterson-coinbase@0xRAG@Tanker187
, '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: add safe wallet provider (ts) - #330

Open
phdargen wants to merge 9 commits into
coinbase:mainfrom
phdargen:safe-ts
Open

feat: add safe wallet provider (ts)#330
phdargen wants to merge 9 commits into
coinbase:mainfrom
phdargen:safe-ts

Conversation

@phdargen

@phdargenphdargen commented Feb 10, 2025

Copy link
Copy Markdown
Contributor

Why? What?

Every AI Agent deserves a Safe wallet!

Adds new safeActionProvider to handle interactions with Safe multi-signature wallets.
This implementation uses the Safe sdk that is added as new dependency.

What changed?

  • New Wallet Provider: safeWalletProvider

    • connects to an existing safe account
    • or automatically creates a new one with the provided private key as single signer
    • getBalance() returns balance of safe wallet, not private-key of agent
    • nativeTransfer() transfers eth from Safe wallet, may require approval of other signers
  • New Action Provider: safeWalletActionProvider with actions:

    • addSigner: Add a new signer to a Safe wallet
    • removeSigner: Remove an existing signer from a Safe wallet
    • changeThreshold: Modify the number of required signatures
    • approvePending: Approve a pending transaction
    • enableAllowanceModule: Activate the allowance module for a Safe
    • setAllowance: Configure spending allowances for specific addresses
  • New Action Provider: safeApiActionProvider with actions:

    • safeInfo: Retrieve detailed information about a Safe wallet
    • getAllowanceInfo: Get current allowance configurations
    • withdrawAllowance: Withdraw funds from an allowance
  • Adds new getPublicClient() method to EvmWalletProvider, so the publicClient can be passed to safeApiActionProvider

  • Adds new signHash() method to EvmWalletProvider that is needed for withdrawAllowance action

  • Adds new langchain-safe-chatbot example

  • Adds safeApiActionProvider to langchain-cdp-chatbot example

Network support

  • All evm chains

How has it been tested?

  • Unit tests
  • Agent tested, example prompts below (using gpt-4o-mini)

Running langchain-safe-chatbot without setting SAFE_ADDRESS:

Prompt: wallet info
-------------
Your wallet details are as follows:
- **Address:** 0xC099a0dE7533f9F0A20979616376c353F330ed44
- **Network:** - **Protocol Family:** EVM
- **Network ID:** ethereum-sepolia
- **Chain ID:** 11155111
- **Native Balance:** 0 WEI
Prompt: safe info
-------------
Here is the information about your Safe:
- **Safe Address:** 0xC099a0dE7533f9F0A20979616376c353F330ed44
- **Chain:** Sepolia
- **Owners:** 1 owner - 0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B
- **Threshold:** 1
- **Nonce:** 0
- **Modules:** None
- **Balance:** 0 ETH
- **Pending Transactions:** 0
Prompt: enable allowance module
-------------------
The allowance module has been successfully enabled for your Safe wallet at address **0xC099a0dE7533f9F0A20979616376c353F330ed44**. - **Transaction Hash**: 0x3eb5159d9a84324a199614a23ba0abfb1f10d56ac35865b988fd99a7c13fc0a6
Prompt: add 0xb7f17bF94DBa50077be037CDc279a868f82D86C2 as signer with threshold 2
-------------------
The signer **0xb7f17bF94DBa50077be037CDc279a868f82D86C2** has been successfully added to your Safe wallet. - **New Threshold**: 2
- **Transaction Hash**: 0x3f1a4de3647c0ee9d7b6e271f8dde7b096ee84437a21e6dabae7531bbce695e0
Prompt: I sent some eth to the safe, withdraw it all to 0xb7f17bF94DBa50077be037CDc279a868f82D86C2
-------------------
The withdrawal transaction has been proposed to transfer **0.00001 ETH** to **0xb7f17bF94DBa50077be037CDc279a868f82D86C2**.
- **Transaction Hash**: 0xdf0fd67b648f7bac6b1684436dd44f812de8963f66f77423bd654cb2d2b78f83
As the threshold is 2, other owners will need to confirm this transaction before it can be executed.
Prompt: safe info
-------------------
Here is the updated information for your Safe multi-sig wallet:
- **Safe Address**: 0xC099a0dE7533f9F0A20979616376c353F330ed44
- **Chain**: Sepolia
- **Owners**: 2 owners - 1. 0xb7f17bF94DBa50077be037CDc279a868f82D86C2
- 2. 0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B
- **Threshold**: 2 (requires two signatures for execution)
- **Nonce**: 2
- **Modules**: 0xCFbFaC74C26F8647cBDb8c5caf80BB5b32E43134
- **Balance**: 0.00001 ETH
- **Pending Transactions**: 1
- **Transaction** 0xdf0fd67b648f7bac6b1684436dd44f812de8963f66f77423bd654cb2d2b78f83 (1/2 confirmations, confirmed by: 0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B)
Prompt: I approved the tx and proposed a new one to change threshold to 1 with safe tx hash: 0xdbc8583147403145e9ea04f5725b3d998f945abe32ce6de5dc8b80cba0841c86. approve and execute it
-------------------
I successfully approved and executed the transaction:
- **Safe Transaction Hash**: 0xdbc8583147403145e9ea04f5725b3d998f945abe32ce6de5dc8b80cba0841c86
- **Execution Transaction Hash**: 0x4c6b40aceeb670f5e3230a34cc30da6fa26681a4f3446b55e3f5d5858226caed
The threshold has now been changed to 1.
Prompt: add 1 weth (0x7b79995e5f793A07Bc00c21412e50Ecae098E7f9) allowance for 0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B
-------------------
The allowance of **1 WETH** (contract address: **0x7b79995e5f793A07Bc00c21412e50Ecae098E7f9**) has been successfully set for the delegate **0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B** (one-time allowance).
- **Transaction Hash**: 0xd282d081f9cb2a437502eda1963818b0d45f1a5589b5ee0cacb734a5f436b1ff 

Running langchain-cdp-chatbot (could be different agent with other private-key, doesn't need to be owner of safe):

Prompt: wallet info
-------------------
Your wallet details are as follows:
- **Provider:** cdp_wallet_provider
- **Address:** 0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B
- **Network:**
- **Protocol Family:** EVM
- **Network ID:** ethereum-sepolia
- **Chain ID:** 11155111
- **Native Balance:** 0.24262 ETH
Prompt: get info for safe 0xC099a0dE7533f9F0A20979616376c353F330ed44
-------------------
Here's the information for the Safe at address **0xC099a0dE7533f9F0A20979616376c353F330ed44**:
- **Chain:** Sepolia
- **Owners:** - 0xb7f17bF94DBa50077be037CDc279a868f82D86C2
- 0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B
- **Threshold:** 1
- **Nonce:** 5
- **Modules:** 0xCFbFaC74C26F8647cBDb8c5caf80BB5b32E43134
- **Balance:** 0 ETH
- **Pending Transactions:** 0
Prompt: get allowance info for this safe and delegate 0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B
-------------------
Here is the allowance information for the delegate **0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B** from the Safe at address **0xC099a0dE7533f9F0A20979616376c353F330ed44**:
- **Token:** WETH (0x7b79995e5f793A07Bc00c21412e50Ecae098E7f9)
- **Current Safe Balance:** 0.00001 WETH
- **Allowance:** 1 available of 1 total (0 spent)
Prompt: withdraw full safe balance from allowance
-------------------
The full Safe balance of **0.00001 WETH** has been successfully withdrawn from the Safe **0xC099a0dE7533f9F0A20979616376c353F330ed44** to the delegate **0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B**.
- **Transaction Hash:** [0xf039503ffb1d213fb373f62cbb7867d99bd71127c8cab6a3e7cc9f2dc53d99c9](https://sepolia.etherscan.io/tx/0xf039503ffb1d213fb373f62cbb7867d99bd71127c8cab6a3e7cc9f2dc53d99c9)

@cb-heimdall

cb-heimdall commented Feb 10, 2025

Copy link
Copy Markdown

🟡 Heimdall Review Status

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

@phdargen
phdargen marked this pull request as draft February 10, 2025 06:28
@John-peterson-coinbase

Copy link
Copy Markdown
Contributor

@phdargen Thanks for the contribution!

A few initial comments:

  • The Safe smart account wallet likely should be a WalletProvider implementation so that you can easily use any of the existing actions with this new wallet type
  • If you would like actions on top of the wallet provider, you can add a SafeWalletActionProvider implementation similar to how CdpWalletActionProvider allows for CdpWalletProvider specific actions

Let me know if you have any questions or comments

@0xRAG0xRAG added action provider New action provider changes requested PR / Issue has changes requested banana typescript labels Feb 10, 2025
@phdargen

Copy link
Copy Markdown
ContributorAuthor

Thanks for the feedback @John-peterson-coinbase. That’s a good suggestion, I will look into it in the coming days. Only problem I see is that one couldn’t use cdp and safe actions at the same time but I guess thats not too bad in this case.

Before getting started, it would be good to clarify some design choices.
Lets say there is a safewalletprovider that is initialised with the private key of the agent and then creates a new safe account with the agent as signer (or connects to exciting safe). Then should the getAddress(), getBalance(), … return the address/balance of the safe rather than the agent wallet? Similar all tx should be done with the safe account, not the agent wallet, whose sole purpose would be to act as signer.

@phdargen

Copy link
Copy Markdown
ContributorAuthor

Hi @John-peterson-coinbase, I got a minimal example working for the new setup:

  • safeWalletProvider connects to an existing safe account or automatically creates a new one with the provided private key as single signer.

  • Get_wallet_info returns the address and balance of the safe, native_transfer sends eth from the safe (or propose tx).

  • Additional signers can be added with a safeWalletActionProvider.

  • safeApiActionProvider implements read-only actions such as the info of a safe account. The safeApiActionProvider actions can also be used with other EvmWalletProviders.

  • Added new langchain-safe-chatbot example

Please let me know if you agree with this implementation, then I'll go ahead and add the remaining actions and clean up by removing the old safeActionProvider

@phdargen

Copy link
Copy Markdown
ContributorAuthor

BTW, I submitted a related project at the safe agentathon (https://devfolio.co/projects/safegpt-d4c5) with a live demo here: https://safe-gpt.vercel.app/

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation wallet provider New wallet provider example New example agent needs triage labels Feb 24, 2025
@phdargen
phdargenforce-pushed the safe-ts branch 4 times, most recently from 82504f2 to c009f79CompareFebruary 26, 2025 17:38
@phdargen

Copy link
Copy Markdown
ContributorAuthor

This is in principle ready for review, however the actions in safeApiActionProvider rely on the safe api that is currently still down, see safe-global/safe-core-sdk#1155.

@phdargen

Copy link
Copy Markdown
ContributorAuthor

This is in principle ready for review, however the actions in safeApiActionProvider rely on the safe api that is currently still down, see safe-global/safe-core-sdk#1155.

The safe api is back and this is ready for review.
Changed the initial commit description to reflect changes.

error => {
throw new Error("Error initializing Safe wallet: " + error);
},
);

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

@John-peterson-coinbase@0xRAG@CarsonRoscoe any clue why the test-agentkit-typescript (20) units tests are failing? There seems to be a network timeout probably related to the fact that I do not call trackInitialization() immediately but wait until the Safe wallet is created (that requires an onchain tx). Running the test locally is successful and test-agentkit-typescript (18) seems to be fine too

@phdargen
phdargen marked this pull request as ready for review March 8, 2025 12:44
@phdargenphdargen changed the title feat: add safe smart account action (ts)feat: add safe wallet provider (ts)Mar 14, 2025
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

action providerNew action providerbananachanges requestedPR / Issue has changes requesteddocumentationImprovements or additions to documentationexampleNew example agenttypescriptwallet providerNew wallet provider

Development

Successfully merging this pull request may close these issues.

5 participants

@phdargen@cb-heimdall@John-peterson-coinbase@0xRAG@Tanker187
, '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: add safe wallet provider (ts) - #330

Open
phdargen wants to merge 9 commits into
coinbase:mainfrom
phdargen:safe-ts
Open

feat: add safe wallet provider (ts)#330
phdargen wants to merge 9 commits into
coinbase:mainfrom
phdargen:safe-ts

Conversation

@phdargen

@phdargenphdargen commented Feb 10, 2025

Copy link
Copy Markdown
Contributor

Why? What?

Every AI Agent deserves a Safe wallet!

Adds new safeActionProvider to handle interactions with Safe multi-signature wallets.
This implementation uses the Safe sdk that is added as new dependency.

What changed?

  • New Wallet Provider: safeWalletProvider

    • connects to an existing safe account
    • or automatically creates a new one with the provided private key as single signer
    • getBalance() returns balance of safe wallet, not private-key of agent
    • nativeTransfer() transfers eth from Safe wallet, may require approval of other signers
  • New Action Provider: safeWalletActionProvider with actions:

    • addSigner: Add a new signer to a Safe wallet
    • removeSigner: Remove an existing signer from a Safe wallet
    • changeThreshold: Modify the number of required signatures
    • approvePending: Approve a pending transaction
    • enableAllowanceModule: Activate the allowance module for a Safe
    • setAllowance: Configure spending allowances for specific addresses
  • New Action Provider: safeApiActionProvider with actions:

    • safeInfo: Retrieve detailed information about a Safe wallet
    • getAllowanceInfo: Get current allowance configurations
    • withdrawAllowance: Withdraw funds from an allowance
  • Adds new getPublicClient() method to EvmWalletProvider, so the publicClient can be passed to safeApiActionProvider

  • Adds new signHash() method to EvmWalletProvider that is needed for withdrawAllowance action

  • Adds new langchain-safe-chatbot example

  • Adds safeApiActionProvider to langchain-cdp-chatbot example

Network support

  • All evm chains

How has it been tested?

  • Unit tests
  • Agent tested, example prompts below (using gpt-4o-mini)

Running langchain-safe-chatbot without setting SAFE_ADDRESS:

Prompt: wallet info
-------------
Your wallet details are as follows:
- **Address:** 0xC099a0dE7533f9F0A20979616376c353F330ed44
- **Network:** - **Protocol Family:** EVM
- **Network ID:** ethereum-sepolia
- **Chain ID:** 11155111
- **Native Balance:** 0 WEI
Prompt: safe info
-------------
Here is the information about your Safe:
- **Safe Address:** 0xC099a0dE7533f9F0A20979616376c353F330ed44
- **Chain:** Sepolia
- **Owners:** 1 owner - 0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B
- **Threshold:** 1
- **Nonce:** 0
- **Modules:** None
- **Balance:** 0 ETH
- **Pending Transactions:** 0
Prompt: enable allowance module
-------------------
The allowance module has been successfully enabled for your Safe wallet at address **0xC099a0dE7533f9F0A20979616376c353F330ed44**. - **Transaction Hash**: 0x3eb5159d9a84324a199614a23ba0abfb1f10d56ac35865b988fd99a7c13fc0a6
Prompt: add 0xb7f17bF94DBa50077be037CDc279a868f82D86C2 as signer with threshold 2
-------------------
The signer **0xb7f17bF94DBa50077be037CDc279a868f82D86C2** has been successfully added to your Safe wallet. - **New Threshold**: 2
- **Transaction Hash**: 0x3f1a4de3647c0ee9d7b6e271f8dde7b096ee84437a21e6dabae7531bbce695e0
Prompt: I sent some eth to the safe, withdraw it all to 0xb7f17bF94DBa50077be037CDc279a868f82D86C2
-------------------
The withdrawal transaction has been proposed to transfer **0.00001 ETH** to **0xb7f17bF94DBa50077be037CDc279a868f82D86C2**.
- **Transaction Hash**: 0xdf0fd67b648f7bac6b1684436dd44f812de8963f66f77423bd654cb2d2b78f83
As the threshold is 2, other owners will need to confirm this transaction before it can be executed.
Prompt: safe info
-------------------
Here is the updated information for your Safe multi-sig wallet:
- **Safe Address**: 0xC099a0dE7533f9F0A20979616376c353F330ed44
- **Chain**: Sepolia
- **Owners**: 2 owners - 1. 0xb7f17bF94DBa50077be037CDc279a868f82D86C2
- 2. 0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B
- **Threshold**: 2 (requires two signatures for execution)
- **Nonce**: 2
- **Modules**: 0xCFbFaC74C26F8647cBDb8c5caf80BB5b32E43134
- **Balance**: 0.00001 ETH
- **Pending Transactions**: 1
- **Transaction** 0xdf0fd67b648f7bac6b1684436dd44f812de8963f66f77423bd654cb2d2b78f83 (1/2 confirmations, confirmed by: 0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B)
Prompt: I approved the tx and proposed a new one to change threshold to 1 with safe tx hash: 0xdbc8583147403145e9ea04f5725b3d998f945abe32ce6de5dc8b80cba0841c86. approve and execute it
-------------------
I successfully approved and executed the transaction:
- **Safe Transaction Hash**: 0xdbc8583147403145e9ea04f5725b3d998f945abe32ce6de5dc8b80cba0841c86
- **Execution Transaction Hash**: 0x4c6b40aceeb670f5e3230a34cc30da6fa26681a4f3446b55e3f5d5858226caed
The threshold has now been changed to 1.
Prompt: add 1 weth (0x7b79995e5f793A07Bc00c21412e50Ecae098E7f9) allowance for 0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B
-------------------
The allowance of **1 WETH** (contract address: **0x7b79995e5f793A07Bc00c21412e50Ecae098E7f9**) has been successfully set for the delegate **0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B** (one-time allowance).
- **Transaction Hash**: 0xd282d081f9cb2a437502eda1963818b0d45f1a5589b5ee0cacb734a5f436b1ff 

Running langchain-cdp-chatbot (could be different agent with other private-key, doesn't need to be owner of safe):

Prompt: wallet info
-------------------
Your wallet details are as follows:
- **Provider:** cdp_wallet_provider
- **Address:** 0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B
- **Network:**
- **Protocol Family:** EVM
- **Network ID:** ethereum-sepolia
- **Chain ID:** 11155111
- **Native Balance:** 0.24262 ETH
Prompt: get info for safe 0xC099a0dE7533f9F0A20979616376c353F330ed44
-------------------
Here's the information for the Safe at address **0xC099a0dE7533f9F0A20979616376c353F330ed44**:
- **Chain:** Sepolia
- **Owners:** - 0xb7f17bF94DBa50077be037CDc279a868f82D86C2
- 0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B
- **Threshold:** 1
- **Nonce:** 5
- **Modules:** 0xCFbFaC74C26F8647cBDb8c5caf80BB5b32E43134
- **Balance:** 0 ETH
- **Pending Transactions:** 0
Prompt: get allowance info for this safe and delegate 0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B
-------------------
Here is the allowance information for the delegate **0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B** from the Safe at address **0xC099a0dE7533f9F0A20979616376c353F330ed44**:
- **Token:** WETH (0x7b79995e5f793A07Bc00c21412e50Ecae098E7f9)
- **Current Safe Balance:** 0.00001 WETH
- **Allowance:** 1 available of 1 total (0 spent)
Prompt: withdraw full safe balance from allowance
-------------------
The full Safe balance of **0.00001 WETH** has been successfully withdrawn from the Safe **0xC099a0dE7533f9F0A20979616376c353F330ed44** to the delegate **0xCe8C96bBF12aa630f439a74A75f86E96c9F50B3B**.
- **Transaction Hash:** [0xf039503ffb1d213fb373f62cbb7867d99bd71127c8cab6a3e7cc9f2dc53d99c9](https://sepolia.etherscan.io/tx/0xf039503ffb1d213fb373f62cbb7867d99bd71127c8cab6a3e7cc9f2dc53d99c9)

@cb-heimdall

cb-heimdall commented Feb 10, 2025

Copy link
Copy Markdown

🟡 Heimdall Review Status

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

@phdargen
phdargen marked this pull request as draft February 10, 2025 06:28
@John-peterson-coinbase

Copy link
Copy Markdown
Contributor

@phdargen Thanks for the contribution!

A few initial comments:

  • The Safe smart account wallet likely should be a WalletProvider implementation so that you can easily use any of the existing actions with this new wallet type
  • If you would like actions on top of the wallet provider, you can add a SafeWalletActionProvider implementation similar to how CdpWalletActionProvider allows for CdpWalletProvider specific actions

Let me know if you have any questions or comments

@0xRAG0xRAG added action provider New action provider changes requested PR / Issue has changes requested banana typescript labels Feb 10, 2025
@phdargen

Copy link
Copy Markdown
ContributorAuthor

Thanks for the feedback @John-peterson-coinbase. That’s a good suggestion, I will look into it in the coming days. Only problem I see is that one couldn’t use cdp and safe actions at the same time but I guess thats not too bad in this case.

Before getting started, it would be good to clarify some design choices.
Lets say there is a safewalletprovider that is initialised with the private key of the agent and then creates a new safe account with the agent as signer (or connects to exciting safe). Then should the getAddress(), getBalance(), … return the address/balance of the safe rather than the agent wallet? Similar all tx should be done with the safe account, not the agent wallet, whose sole purpose would be to act as signer.

@phdargen

Copy link
Copy Markdown
ContributorAuthor

Hi @John-peterson-coinbase, I got a minimal example working for the new setup:

  • safeWalletProvider connects to an existing safe account or automatically creates a new one with the provided private key as single signer.

  • Get_wallet_info returns the address and balance of the safe, native_transfer sends eth from the safe (or propose tx).

  • Additional signers can be added with a safeWalletActionProvider.

  • safeApiActionProvider implements read-only actions such as the info of a safe account. The safeApiActionProvider actions can also be used with other EvmWalletProviders.

  • Added new langchain-safe-chatbot example

Please let me know if you agree with this implementation, then I'll go ahead and add the remaining actions and clean up by removing the old safeActionProvider

@phdargen

Copy link
Copy Markdown
ContributorAuthor

BTW, I submitted a related project at the safe agentathon (https://devfolio.co/projects/safegpt-d4c5) with a live demo here: https://safe-gpt.vercel.app/

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation wallet provider New wallet provider example New example agent needs triage labels Feb 24, 2025
@phdargen
phdargenforce-pushed the safe-ts branch 4 times, most recently from 82504f2 to c009f79CompareFebruary 26, 2025 17:38
@phdargen

Copy link
Copy Markdown
ContributorAuthor

This is in principle ready for review, however the actions in safeApiActionProvider rely on the safe api that is currently still down, see safe-global/safe-core-sdk#1155.

@phdargen

Copy link
Copy Markdown
ContributorAuthor

This is in principle ready for review, however the actions in safeApiActionProvider rely on the safe api that is currently still down, see safe-global/safe-core-sdk#1155.

The safe api is back and this is ready for review.
Changed the initial commit description to reflect changes.

error => {
throw new Error("Error initializing Safe wallet: " + error);
},
);

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

@John-peterson-coinbase@0xRAG@CarsonRoscoe any clue why the test-agentkit-typescript (20) units tests are failing? There seems to be a network timeout probably related to the fact that I do not call trackInitialization() immediately but wait until the Safe wallet is created (that requires an onchain tx). Running the test locally is successful and test-agentkit-typescript (18) seems to be fine too

@phdargen
phdargen marked this pull request as ready for review March 8, 2025 12:44
@phdargenphdargen changed the title feat: add safe smart account action (ts)feat: add safe wallet provider (ts)Mar 14, 2025
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

action providerNew action providerbananachanges requestedPR / Issue has changes requesteddocumentationImprovements or additions to documentationexampleNew example agenttypescriptwallet providerNew wallet provider

Development

Successfully merging this pull request may close these issues.

5 participants

@phdargen@cb-heimdall@John-peterson-coinbase@0xRAG@Tanker187