Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ The **Coinbase Developer Platform (CDP) Agentkit for Python** simplifies bringin
- Deploying [ERC-721](https://www.coinbase.com/learn/crypto-glossary/what-is-erc-721) tokens and minting NFTs
- Buying and selling [Zora Wow](https://wow.xyz/) ERC-20 coins
- Deploying tokens on [Zora's Wow Launcher](https://wow.xyz/mechanics) (Bonding Curve)
- Wrapping ETH to WETH on Base

Or [add your own](./CONTRIBUTING.md#adding-an-action-to-agentkit-core)!

Expand Down Expand Up @@ -50,4 +51,4 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for more information.
## Documentation
- [CDP Agentkit Documentation](https://docs.cdp.coinbase.com/agentkit/docs/welcome)
- [API Reference: CDP Agentkit Core](https://coinbase.github.io/cdp-agentkit/cdp-agentkit-core/index.html)
- [API Reference: CDP Agentkit LangChain Extension](https://coinbase.github.io/cdp-agentkit/cdp-langchain/index.html)
- [API Reference: CDP Agentkit LangChain Extension](https://coinbase.github.io/cdp-agentkit/cdp-langchain/index.html)
4 changes: 4 additions & 0 deletions cdp-agentkit-core/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

### Added

- Added `wrap_eth` action to wrap ETH to WETH on Base.

## [0.0.7] - 2025-01-08

### Added
Expand Down
8 changes: 4 additions & 4 deletions cdp-agentkit-core/Makefile
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
.PHONY: format
format:
ruff format .
poetry run ruff format .

.PHONY: lint
lint:
ruff check .
poetry run ruff check .

.PHONY: lint-fix
lint-fix:
ruff check . --fix
poetry run ruff check . --fix

.PHONY: docs
docs:
sphinx-apidoc -f -o ./docs ./cdp_agentkit_core
poetry run sphinx-apidoc -f -o ./docs ./cdp_agentkit_core

.PHONY: local-docs
local-docs: docs
Expand Down
2 changes: 2 additions & 0 deletions cdp-agentkit-core/cdp_agentkit_core/actions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from cdp_agentkit_core.actions.wow.buy_token import WowBuyTokenAction
from cdp_agentkit_core.actions.wow.create_token import WowCreateTokenAction
from cdp_agentkit_core.actions.wow.sell_token import WowSellTokenAction
from cdp_agentkit_core.actions.wrap_eth import WrapEthAction


# WARNING: All new CdpAction subclasses must be imported above, otherwise they will not be discovered
Expand Down Expand Up @@ -40,4 +41,5 @@ def get_all_cdp_actions() -> list[type[CdpAction]]:
"WowBuyTokenAction",
"WowCreateTokenAction",
"WowSellTokenAction",
"WrapEthAction",
]
1 change: 1 addition & 0 deletions cdp-agentkit-core/cdp_agentkit_core/actions/deploy_nft.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
It takes the name of the NFT collection, the symbol of the NFT collection, and the base URI for the token metadata as inputs.
"""


class DeployNftInput(BaseModel):
"""Input argument schema for deploy NFT action."""

Expand Down
1 change: 1 addition & 0 deletions cdp-agentkit-core/cdp_agentkit_core/actions/get_balance.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
It takes the asset ID as input. Always use 'eth' for the native asset ETH and 'usdc' for USDC.
"""


class GetBalanceInput(BaseModel):
"""Input argument schema for get balance action."""

Expand Down
1 change: 1 addition & 0 deletions cdp-agentkit-core/cdp_agentkit_core/actions/mint_nft.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
Do not use the contract address as the destination address. If you are unsure of the destination address, please ask the user before proceeding.
"""


class MintNftInput(BaseModel):
"""Input argument schema for mint NFT action."""

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,12 @@ def account_details(client: tweepy.Client) -> str:

try:
response = client.get_me()
data = response['data']
data['url'] = f"https://x.com/{data['username']}"
data = response["data"]
data["url"] = f"https://x.com/{data['username']}"

message = f"""Successfully retrieved authenticated user account details:\n{dumps(response)}"""
message = (
f"""Successfully retrieved authenticated user account details:\n{dumps(response)}"""
)
except tweepy.errors.TweepyException as e:
message = f"Error retrieving authenticated user account details:\n{e}"

Expand Down
1 change: 1 addition & 0 deletions cdp-agentkit-core/cdp_agentkit_core/actions/transfer.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
- When sending native assets (e.g. 'eth' on base-mainnet), ensure there is sufficient balance for the transfer itself AND the gas cost of this transfer
"""


class TransferInput(BaseModel):
"""Input argument schema for transfer action."""

Expand Down
34 changes: 16 additions & 18 deletions cdp-agentkit-core/cdp_agentkit_core/actions/wow/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,14 @@ def get_buy_quote(network_id: str, token_address: str, amount_eth_in_wei: str):
"""
has_graduated = get_has_graduated(network_id, token_address)
token_quote = (
(has_graduated
and (get_uniswap_quote(network_id, token_address, amount_eth_in_wei, "buy")).amount_out)
or SmartContract.read(
network_id,
token_address,
"getEthBuyQuote",
abi=WOW_ABI,
args={"ethOrderSize": str(amount_eth_in_wei)},
)
has_graduated
and (get_uniswap_quote(network_id, token_address, amount_eth_in_wei, "buy")).amount_out
) or SmartContract.read(
network_id,
token_address,
"getEthBuyQuote",
abi=WOW_ABI,
args={"ethOrderSize": str(amount_eth_in_wei)},
)
return token_quote

Expand All @@ -56,14 +55,13 @@ def get_sell_quote(network_id: str, token_address: str, amount_tokens_in_wei: st
"""
has_graduated = get_has_graduated(network_id, token_address)
token_quote = (
(has_graduated
and (get_uniswap_quote(network_id, token_address, amount_tokens_in_wei, "sell")).amount_out)
or SmartContract.read(
network_id,
token_address,
"getTokenSellQuote",
WOW_ABI,
args={"tokenOrderSize": str(amount_tokens_in_wei)},
)
has_graduated
and (get_uniswap_quote(network_id, token_address, amount_tokens_in_wei, "sell")).amount_out
) or SmartContract.read(
network_id,
token_address,
"getTokenSellQuote",
WOW_ABI,
args={"tokenOrderSize": str(amount_tokens_in_wei)},
)
return token_quote
93 changes: 93 additions & 0 deletions cdp-agentkit-core/cdp_agentkit_core/actions/wrap_eth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
from collections.abc import Callable

from cdp import Wallet
from pydantic import BaseModel, Field

from cdp_agentkit_core.actions import CdpAction

WETH_ADDRESS = "0x4200000000000000000000000000000000000006"

WETH_ABI = [
{
"inputs": [],
"name": "deposit",
"outputs": [],
"stateMutability": "payable",
"type": "function",
},
{
"inputs": [
{
"name": "account",
"type": "address",
},
],
"name": "balanceOf",
"outputs": [
{
"type": "uint256",
},
],
"stateMutability": "view",
"type": "function",
},
]

WRAP_ETH_PROMPT = """
This tool can only be used to wrap ETH to WETH.
Do not use this tool for any other purpose, or trading other assets.
Inputs:
- Amount of ETH to wrap.
Important notes:
- The amount is a string and cannot have any decimal points, since the unit of measurement is wei.
- Make sure to use the exact amount provided, and if there's any doubt, check by getting more information before continuing with the action.
- 1 wei = 0.000000000000000001 WETH
- Minimum purchase amount is 100000000000000 wei (0.0000001 WETH)
- Only supported on the following networks:
- Base Sepolia (ie, 'base-sepolia')
- Base Mainnet (ie, 'base', 'base-mainnnet')
"""


class WrapEthInput(BaseModel):
"""Input argument schema for wrapping ETH to WETH."""

amount_to_wrap: str = Field(
...,
description="Amount of ETH to wrap in wei",
)


def wrap_eth(wallet: Wallet, amount_to_wrap: str) -> str:
"""Wrap ETH to WETH.

Args:
wallet (Wallet): The wallet to wrap ETH from.
amount_to_wrap (str): The amount of ETH to wrap in wei.

Returns:
str: A message containing the wrapped ETH details.

"""
try:
invocation = wallet.invoke_contract(
contract_address=WETH_ADDRESS,
method="deposit",
abi=WETH_ABI,
args={},
amount=amount_to_wrap,
asset_id="wei",
)
result = invocation.wait()
return f"Wrapped ETH with transaction hash: {result.transaction.transaction_hash}"
except Exception as e:
return f"Unexpected error wrapping ETH: {e!s}"


class WrapEthAction(CdpAction):
"""Wrap ETH to WETH action."""

name: str = "wrap_eth"
description: str = WRAP_ETH_PROMPT
args_schema: type[BaseModel] | None = WrapEthInput
func: Callable[..., str] = wrap_eth
74 changes: 74 additions & 0 deletions cdp-agentkit-core/tests/actions/test_wrap_eth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
from unittest.mock import patch

import pytest

from cdp_agentkit_core.actions.wrap_eth import (
WETH_ABI,
WETH_ADDRESS,
WrapEthAction,
WrapEthInput,
wrap_eth,
)


def test_wrap_eth_success(wallet_factory, contract_invocation_factory):
"""Test successful ETH wrapping."""
mock_wallet = wallet_factory()
mock_invocation = contract_invocation_factory()

amount = "1000000000000000000" # 1 ETH in wei

with (
patch.object(
mock_wallet, "invoke_contract", return_value=mock_invocation
) as mock_invoke_contract,
patch.object(mock_invocation, "wait", return_value=mock_invocation) as mock_invocation_wait,
):
result = wrap_eth(mock_wallet, amount)

mock_invoke_contract.assert_called_once_with(
contract_address=WETH_ADDRESS,
method="deposit",
abi=WETH_ABI,
args={},
amount=amount,
asset_id="wei",
)
mock_invocation_wait.assert_called_once_with()

assert (
result
== f"Wrapped ETH with transaction hash: {mock_invocation.transaction.transaction_hash}"
)


def test_wrap_eth_failure(wallet_factory):
"""Test ETH wrapping failure."""
mock_wallet = wallet_factory()
mock_wallet.invoke_contract.side_effect = Exception("Test error")

amount = "1000000000000000000"
result = wrap_eth(mock_wallet, amount)

assert result == "Unexpected error wrapping ETH: Test error"


def test_wrap_eth_action_initialization():
"""Test WrapEthAction initialization and attributes."""
action = WrapEthAction()

assert action.name == "wrap_eth"
assert action.args_schema == WrapEthInput
assert callable(action.func)


def test_wrap_eth_input_model_valid():
"""Test WrapEthInput accepts valid parameters."""
valid_input = WrapEthInput(amount_to_wrap="1000000000000000000")
assert valid_input.amount_to_wrap == "1000000000000000000"


def test_wrap_eth_input_model_missing_params():
"""Test WrapEthInput raises error when params are missing."""
with pytest.raises(ValueError):
WrapEthInput()
8 changes: 4 additions & 4 deletions cdp-langchain/Makefile
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
.PHONY: format
format:
ruff format .
poetry run ruff format .

.PHONY: lint
lint:
ruff check .
poetry run ruff check .

.PHONY: lint-fix
lint-fix:
ruff check . --fix
poetry run ruff check . --fix

.PHONY: docs
docs:
sphinx-apidoc -f -o ./docs ./cdp_langchain
poetry run sphinx-apidoc -f -o ./docs ./cdp_langchain

.PHONY: local-docs
local-docs: docs
Expand Down
1 change: 1 addition & 0 deletions cdp-langchain/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ The toolkit provides the following tools:
10. **wow_create_token** - Deploy a token using Zora's Wow Launcher (Bonding Curve)
11. **wow_buy_token** - Buy Zora Wow ERC20 memecoin with ETH
12. **wow_sell_token** - Sell Zora Wow ERC20 memecoin for ETH
13. **wrap_eth** - Wrap ETH to WETH

### Using with an Agent

Expand Down
1 change: 1 addition & 0 deletions cdp-langchain/cdp_langchain/agent_toolkits/cdp_toolkit.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ class CdpToolkit(BaseToolkit):
wow_create_token
wow_buy_token
wow_sell_token
wrap_eth
Use within an agent:
.. code-block:: python

Expand Down
1 change: 0 additions & 1 deletion cdp-langchain/examples/chatbot/chatbot.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ def initialize_agent():
"recommend they go to docs.cdp.coinbase.com for more information. Be concise and helpful with your "
"responses. Refrain from restating your tools' descriptions unless it is explicitly requested."
),

), config


Expand Down
Loading