-
Notifications
You must be signed in to change notification settings - Fork 161
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
9126e73
commit fae9162
Showing
10 changed files
with
366 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
190 changes: 190 additions & 0 deletions
190
cdp-agentkit-core/cdp_agentkit_core/actions/wow/constants.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,190 @@ | ||
WOW_FACTORY_ABI = [ | ||
{ | ||
"type": "constructor", | ||
"inputs": [ | ||
{"name": "_tokenImplementation", "type": "address", "internalType": "address"}, | ||
{"name": "_bondingCurve", "type": "address", "internalType": "address"}, | ||
], | ||
"stateMutability": "nonpayable", | ||
}, | ||
{ | ||
"type": "function", | ||
"name": "UPGRADE_INTERFACE_VERSION", | ||
"inputs": [], | ||
"outputs": [{"name": "", "type": "string", "internalType": "string"}], | ||
"stateMutability": "view", | ||
}, | ||
{ | ||
"type": "function", | ||
"name": "bondingCurve", | ||
"inputs": [], | ||
"outputs": [{"name": "", "type": "address", "internalType": "address"}], | ||
"stateMutability": "view", | ||
}, | ||
{ | ||
"type": "function", | ||
"name": "deploy", | ||
"inputs": [ | ||
{"name": "_tokenCreator", "type": "address", "internalType": "address"}, | ||
{"name": "_platformReferrer", "type": "address", "internalType": "address"}, | ||
{"name": "_tokenURI", "type": "string", "internalType": "string"}, | ||
{"name": "_name", "type": "string", "internalType": "string"}, | ||
{"name": "_symbol", "type": "string", "internalType": "string"}, | ||
], | ||
"outputs": [{"name": "", "type": "address", "internalType": "address"}], | ||
"stateMutability": "payable", | ||
}, | ||
{ | ||
"type": "function", | ||
"name": "implementation", | ||
"inputs": [], | ||
"outputs": [{"name": "", "type": "address", "internalType": "address"}], | ||
"stateMutability": "view", | ||
}, | ||
{ | ||
"type": "function", | ||
"name": "initialize", | ||
"inputs": [{"name": "_owner", "type": "address", "internalType": "address"}], | ||
"outputs": [], | ||
"stateMutability": "nonpayable", | ||
}, | ||
{ | ||
"type": "function", | ||
"name": "owner", | ||
"inputs": [], | ||
"outputs": [{"name": "", "type": "address", "internalType": "address"}], | ||
"stateMutability": "view", | ||
}, | ||
{ | ||
"type": "function", | ||
"name": "proxiableUUID", | ||
"inputs": [], | ||
"outputs": [{"name": "", "type": "bytes32", "internalType": "bytes32"}], | ||
"stateMutability": "view", | ||
}, | ||
{ | ||
"type": "function", | ||
"name": "renounceOwnership", | ||
"inputs": [], | ||
"outputs": [], | ||
"stateMutability": "nonpayable", | ||
}, | ||
{ | ||
"type": "function", | ||
"name": "tokenImplementation", | ||
"inputs": [], | ||
"outputs": [{"name": "", "type": "address", "internalType": "address"}], | ||
"stateMutability": "view", | ||
}, | ||
{ | ||
"type": "function", | ||
"name": "transferOwnership", | ||
"inputs": [{"name": "newOwner", "type": "address", "internalType": "address"}], | ||
"outputs": [], | ||
"stateMutability": "nonpayable", | ||
}, | ||
{ | ||
"type": "function", | ||
"name": "upgradeToAndCall", | ||
"inputs": [ | ||
{"name": "newImplementation", "type": "address", "internalType": "address"}, | ||
{"name": "data", "type": "bytes", "internalType": "bytes"}, | ||
], | ||
"outputs": [], | ||
"stateMutability": "payable", | ||
}, | ||
{ | ||
"type": "event", | ||
"name": "Initialized", | ||
"inputs": [ | ||
{"name": "version", "type": "uint64", "indexed": False, "internalType": "uint64"} | ||
], | ||
"anonymous": False, | ||
}, | ||
{ | ||
"type": "event", | ||
"name": "OwnershipTransferred", | ||
"inputs": [ | ||
{ | ||
"name": "previousOwner", | ||
"type": "address", | ||
"indexed": True, | ||
"internalType": "address", | ||
}, | ||
{"name": "newOwner", "type": "address", "indexed": True, "internalType": "address"}, | ||
], | ||
"anonymous": False, | ||
}, | ||
{ | ||
"type": "event", | ||
"name": "Upgraded", | ||
"inputs": [ | ||
{ | ||
"name": "implementation", | ||
"type": "address", | ||
"indexed": True, | ||
"internalType": "address", | ||
} | ||
], | ||
"anonymous": False, | ||
}, | ||
{ | ||
"type": "error", | ||
"name": "AddressEmptyCode", | ||
"inputs": [{"name": "target", "type": "address", "internalType": "address"}], | ||
}, | ||
{"type": "error", "name": "ERC1167FailedCreateClone", "inputs": []}, | ||
{ | ||
"type": "error", | ||
"name": "ERC1967InvalidImplementation", | ||
"inputs": [{"name": "implementation", "type": "address", "internalType": "address"}], | ||
}, | ||
{"type": "error", "name": "ERC1967NonPayable", "inputs": []}, | ||
{"type": "error", "name": "FailedInnerCall", "inputs": []}, | ||
{"type": "error", "name": "InvalidInitialization", "inputs": []}, | ||
{"type": "error", "name": "NotInitializing", "inputs": []}, | ||
{ | ||
"type": "error", | ||
"name": "OwnableInvalidOwner", | ||
"inputs": [{"name": "owner", "type": "address", "internalType": "address"}], | ||
}, | ||
{ | ||
"type": "error", | ||
"name": "OwnableUnauthorizedAccount", | ||
"inputs": [{"name": "account", "type": "address", "internalType": "address"}], | ||
}, | ||
{"type": "error", "name": "ReentrancyGuardReentrantCall", "inputs": []}, | ||
{"type": "error", "name": "UUPSUnauthorizedCallContext", "inputs": []}, | ||
{ | ||
"type": "error", | ||
"name": "UUPSUnsupportedProxiableUUID", | ||
"inputs": [{"name": "slot", "type": "bytes32", "internalType": "bytes32"}], | ||
}, | ||
] | ||
|
||
WOW_FACTORY_CONTRACT_ADDRESSES = { | ||
"base-sepolia": "0x04870e22fa217Cb16aa00501D7D5253B8838C1eA", | ||
"base-mainnet": "0x997020E5F59cCB79C74D527Be492Cc610CB9fA2B", | ||
} | ||
|
||
|
||
def get_factory_address(network: str) -> str: | ||
"""Get the Zora Wow ERC20 Factory contract address for the specified network. | ||
Args: | ||
network (str): The network ID to get the contract address for. | ||
Valid networks are: base-sepolia, base-mainnet. | ||
Returns: | ||
str: The contract address for the specified network. | ||
Raises: | ||
ValueError: If the specified network is not supported. | ||
""" | ||
network = network.lower() | ||
if network not in WOW_FACTORY_CONTRACT_ADDRESSES: | ||
raise ValueError( | ||
f"Invalid network: {network}. Valid networks are: {', '.join(WOW_FACTORY_CONTRACT_ADDRESSES.keys())}" | ||
) | ||
return WOW_FACTORY_CONTRACT_ADDRESSES[network] |
66 changes: 66 additions & 0 deletions
66
cdp-agentkit-core/cdp_agentkit_core/actions/wow/create_token.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
from collections.abc import Callable | ||
|
||
from cdp import Wallet | ||
from pydantic import BaseModel, Field | ||
|
||
from cdp_agentkit_core.actions import CdpAction | ||
from cdp_agentkit_core.actions.wow.constants import WOW_FACTORY_ABI, get_factory_address | ||
|
||
WOW_CREATE_TOKEN_PROMPT = """ | ||
This tool will create a Zora Wow ERC20 memecoin using the WoW factory. This tool takes the token name and token symbol. It is only supported on Base Sepolia and Base Mainnet. | ||
""" | ||
|
||
|
||
class WowCreateTokenInput(BaseModel): | ||
"""Input argument schema for create token action.""" | ||
|
||
name: str = Field( | ||
..., | ||
description="The name of the token to create.", | ||
) | ||
symbol: str = Field( | ||
..., | ||
description="The symbol of the token to create.", | ||
) | ||
|
||
|
||
def wow_create_token(wallet: Wallet, name: str, symbol: str) -> str: | ||
"""Create a Zora Wow ERC20 memecoin. | ||
Args: | ||
wallet (Wallet): The wallet to create the token from. | ||
name (str): The name of the token to create. | ||
symbol (str): The symbol of the token to create. | ||
Returns: | ||
str: A message containing the token creation details. | ||
""" | ||
factory_address = get_factory_address(wallet.network_id) | ||
|
||
# Generic JSON metadata for all tokens | ||
token_uri = "ipfs://QmY1GqprFYvojCcUEKgqHeDj9uhZD9jmYGrQTfA9vAE78J" | ||
|
||
invocation = wallet.invoke_contract( | ||
contract_address=factory_address, | ||
method="deploy", | ||
abi=WOW_FACTORY_ABI, | ||
args={ | ||
"_tokenCreator": wallet.default_address.address_id, | ||
"_platformReferrer": "0x0000000000000000000000000000000000000000", | ||
"_tokenURI": token_uri, | ||
"_name": name, | ||
"_symbol": symbol, | ||
}, | ||
).wait() | ||
|
||
return f"Created WoW ERC20 memecoin {name} with symbol {symbol} on network {wallet.network_id}.\nTransaction hash for the token creation: {invocation.transaction.transaction_hash}\nTransaction link for the token creation: {invocation.transaction.transaction_link}" | ||
|
||
|
||
class WowCreateTokenAction(CdpAction): | ||
"""Zora Wow create token action.""" | ||
|
||
name: str = "wow_create_token" | ||
description: str = WOW_CREATE_TOKEN_PROMPT | ||
args_schema: type[BaseModel] | None = WowCreateTokenInput | ||
func: Callable[..., str] = wow_create_token |
101 changes: 101 additions & 0 deletions
101
cdp-agentkit-core/tests/actions/wow/test_create_token.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
from unittest.mock import patch | ||
|
||
import pytest | ||
|
||
from cdp_agentkit_core.actions.wow.constants import WOW_FACTORY_ABI, get_factory_address | ||
from cdp_agentkit_core.actions.wow.create_token import ( | ||
WowCreateTokenInput, | ||
wow_create_token, | ||
) | ||
|
||
MOCK_NAME = "Test Token" | ||
MOCK_SYMBOL = "TEST" | ||
MOCK_NETWORK_ID = "base-sepolia" | ||
MOCK_WALLET_ADDRESS = "0x1234567890123456789012345678901234567890" | ||
MOCK_TOKEN_URI = "ipfs://QmY1GqprFYvojCcUEKgqHeDj9uhZD9jmYGrQTfA9vAE78J" | ||
|
||
|
||
def test_create_token_input_model_valid(): | ||
"""Test that CreateTokenInput accepts valid parameters.""" | ||
input_model = WowCreateTokenInput( | ||
name=MOCK_NAME, | ||
symbol=MOCK_SYMBOL, | ||
) | ||
|
||
assert input_model.name == MOCK_NAME | ||
assert input_model.symbol == MOCK_SYMBOL | ||
|
||
|
||
def test_create_token_input_model_missing_params(): | ||
"""Test that CreateTokenInput raises error when params are missing.""" | ||
with pytest.raises(ValueError): | ||
WowCreateTokenInput() | ||
|
||
|
||
def test_create_token_success(wallet_factory, contract_invocation_factory): | ||
"""Test successful token creation with valid parameters.""" | ||
mock_wallet = wallet_factory() | ||
mock_contract_instance = contract_invocation_factory() | ||
mock_wallet.default_address.address_id = MOCK_WALLET_ADDRESS | ||
mock_wallet.network_id = MOCK_NETWORK_ID | ||
|
||
with ( | ||
patch.object( | ||
mock_wallet, "invoke_contract", return_value=mock_contract_instance | ||
) as mock_invoke, | ||
patch.object( | ||
mock_contract_instance, "wait", return_value=mock_contract_instance | ||
) as mock_contract_wait, | ||
): | ||
action_response = wow_create_token( | ||
mock_wallet, | ||
MOCK_NAME, | ||
MOCK_SYMBOL, | ||
) | ||
|
||
expected_response = f"Created WoW ERC20 memecoin {MOCK_NAME} with symbol {MOCK_SYMBOL} on network {MOCK_NETWORK_ID}.\nTransaction hash for the token creation: {mock_contract_instance.transaction.transaction_hash}\nTransaction link for the token creation: {mock_contract_instance.transaction.transaction_link}" | ||
assert action_response == expected_response | ||
|
||
mock_invoke.assert_called_once_with( | ||
contract_address=get_factory_address(MOCK_NETWORK_ID), | ||
method="deploy", | ||
abi=WOW_FACTORY_ABI, | ||
args={ | ||
"_tokenCreator": MOCK_WALLET_ADDRESS, | ||
"_platformReferrer": "0x0000000000000000000000000000000000000000", | ||
"_tokenURI": MOCK_TOKEN_URI, | ||
"_name": MOCK_NAME, | ||
"_symbol": MOCK_SYMBOL, | ||
}, | ||
) | ||
mock_contract_wait.assert_called_once_with() | ||
|
||
|
||
def test_create_token_api_error(wallet_factory): | ||
"""Test create_token when API error occurs.""" | ||
mock_wallet = wallet_factory() | ||
mock_wallet.default_address.address_id = MOCK_WALLET_ADDRESS | ||
mock_wallet.network_id = MOCK_NETWORK_ID | ||
|
||
with patch.object( | ||
mock_wallet, "invoke_contract", side_effect=Exception("API error") | ||
) as mock_invoke: | ||
with pytest.raises(Exception, match="API error"): | ||
wow_create_token( | ||
mock_wallet, | ||
MOCK_NAME, | ||
MOCK_SYMBOL, | ||
) | ||
|
||
mock_invoke.assert_called_once_with( | ||
contract_address=get_factory_address(MOCK_NETWORK_ID), | ||
method="deploy", | ||
abi=WOW_FACTORY_ABI, | ||
args={ | ||
"_tokenCreator": MOCK_WALLET_ADDRESS, | ||
"_platformReferrer": "0x0000000000000000000000000000000000000000", | ||
"_tokenURI": MOCK_TOKEN_URI, | ||
"_name": MOCK_NAME, | ||
"_symbol": MOCK_SYMBOL, | ||
}, | ||
) |
Oops, something went wrong.