11from unittest .mock import Mock , patch
22
3+ import pytest
34from eth_account import Account
45
56from cdp .client .models .call import Call
67from cdp .client .models .create_smart_wallet_request import CreateSmartWalletRequest
7- from cdp .evm_call_types import EVMCallDict
8- from cdp .smart_wallet import SmartWallet
8+ from cdp .evm_call_types import EVMAbiCallDict , EVMCallDict
9+ from cdp .smart_wallet import SmartWallet , to_smart_wallet
910from cdp .user_operation import UserOperation
1011
1112
@@ -51,15 +52,14 @@ def test_smart_wallet_use_network(smart_wallet_factory):
5152
5253
5354@patch ("cdp.Cdp.api_clients" )
54- def test_smart_wallet_send_user_operation (
55+ def test_smart_wallet_send_user_operation_with_encoded_call (
5556 mock_api_clients , smart_wallet_factory , user_operation_model_factory , account_factory
5657):
5758 """Test SmartWallet send_user_operation method."""
5859 account = account_factory ()
5960 smart_wallet_address = "0x1234567890123456789012345678901234567890"
6061 smart_wallet = smart_wallet_factory (smart_wallet_address , account )
6162
62- # Create a mock user operation using UserOperationModel
6363 mock_user_operation = user_operation_model_factory (
6464 id = "test-user-operation-id" ,
6565 network_id = "base-sepolia" ,
@@ -70,7 +70,6 @@ def test_smart_wallet_send_user_operation(
7070 status = "pending" ,
7171 )
7272
73- # Setup the mock chain
7473 mock_create_user_operation = Mock (return_value = mock_user_operation )
7574 mock_api_clients .smart_wallets .create_user_operation = mock_create_user_operation
7675
@@ -80,14 +79,170 @@ def test_smart_wallet_send_user_operation(
8079 calls = calls , chain_id = 84532 , paymaster_url = "https://paymaster.com"
8180 )
8281
83- # Verify the mock was called with correct arguments
8482 mock_create_user_operation .assert_called_once ()
8583
86- # Verify the user operation properties match expected values
8784 assert user_operation .user_operation_id == mock_user_operation .id
8885 assert user_operation .smart_wallet_address == smart_wallet_address
8986 assert user_operation .unsigned_payload == mock_user_operation .unsigned_payload
9087 assert user_operation .signature == mock_user_operation .signature
9188 assert user_operation .transaction_hash == mock_user_operation .transaction_hash
9289 assert user_operation .status == UserOperation .Status (mock_user_operation .status )
9390 assert not user_operation .terminal_state
91+
92+
93+ @patch ("cdp.Cdp.api_clients" )
94+ def test_send_user_operation_with_abi_call (
95+ mock_api_clients , smart_wallet_factory , user_operation_model_factory , account_factory
96+ ):
97+ """Test sending user operation with EVMAbiCallDict."""
98+ account = account_factory ()
99+ smart_wallet_address = "0x1234567890123456789012345678901234567890"
100+ smart_wallet = smart_wallet_factory (smart_wallet_address , account )
101+
102+ mock_user_operation = user_operation_model_factory (
103+ id = "test-op-id" ,
104+ network_id = "base-sepolia" ,
105+ calls = [Call (to = account .address , data = "0x123" , value = "0" )],
106+ unsigned_payload = "0x" + "0" * 64 ,
107+ signature = "0xe0a180fdd0fe38037cc878c03832861b40a29d32bd7b40b10c9e1efc8c1468a05ae06d1624896d0d29f4b31e32772ea3cb1b4d7ed4e077e5da28dcc33c0e78121c" ,
108+ transaction_hash = "0x4567890123456789012345678901234567890123" ,
109+ status = "pending" ,
110+ )
111+
112+ mock_create_user_operation = Mock (return_value = mock_user_operation )
113+ mock_api_clients .smart_wallets .create_user_operation = mock_create_user_operation
114+
115+ abi_call = EVMAbiCallDict (
116+ to = account .address ,
117+ abi = [{"inputs" : [], "name" : "transfer" , "type" : "function" }],
118+ function_name = "transfer" ,
119+ args = [],
120+ value = None ,
121+ )
122+
123+ user_operation = smart_wallet .send_user_operation (calls = [abi_call ], chain_id = 84532 )
124+
125+ assert user_operation .user_operation_id == mock_user_operation .id
126+ assert user_operation .smart_wallet_address == smart_wallet_address
127+ assert user_operation .unsigned_payload == mock_user_operation .unsigned_payload
128+ assert user_operation .signature == mock_user_operation .signature
129+ assert user_operation .transaction_hash == mock_user_operation .transaction_hash
130+ assert user_operation .status == UserOperation .Status (mock_user_operation .status )
131+ assert not user_operation .terminal_state
132+ mock_create_user_operation .assert_called_once ()
133+
134+
135+ def test_smart_wallet_multiple_calls (smart_wallet_factory , account_factory ):
136+ """Test sending multiple calls in one operation."""
137+ account = account_factory ()
138+ smart_wallet_address = "0x1234567890123456789012345678901234567890"
139+ smart_wallet = smart_wallet_factory (smart_wallet_address , account )
140+
141+ calls = [
142+ EVMCallDict (to = account .address , value = 1000000000000000000 , data = "0x" ),
143+ EVMCallDict (to = account .address , value = 0 , data = "0x123" ),
144+ EVMCallDict (to = account .address , value = None , data = None ),
145+ ]
146+
147+ with patch ("cdp.Cdp.api_clients" ) as mock_api_clients :
148+ mock_user_operation = Mock (
149+ id = "test-op-id" ,
150+ network_id = "base-sepolia" ,
151+ calls = [Call (to = c .to , value = str (c .value or 0 ), data = c .data or "0x" ) for c in calls ],
152+ unsigned_payload = "0x" + "0" * 64 ,
153+ signature = "0x" + "0" * 130 ,
154+ transaction_hash = "0x" + "0" * 64 ,
155+ status = "pending" ,
156+ )
157+ mock_api_clients .smart_wallets .create_user_operation .return_value = mock_user_operation
158+
159+ user_operation = smart_wallet .send_user_operation (calls = calls , chain_id = 84532 )
160+ assert user_operation .user_operation_id == "test-op-id"
161+
162+
163+ def test_network_scoped_wallet_initialization (smart_wallet_factory , account_factory ):
164+ """Test NetworkScopedSmartWallet initialization."""
165+ account = account_factory ()
166+ smart_wallet_address = "0x1234567890123456789012345678901234567890"
167+
168+ network_wallet = smart_wallet_factory (smart_wallet_address , account ).use_network (
169+ 84532 , "https://paymaster.com"
170+ )
171+ assert network_wallet .chain_id == 84532
172+ assert network_wallet .paymaster_url == "https://paymaster.com"
173+
174+ network_wallet = smart_wallet_factory (smart_wallet_address , account ).use_network (84532 )
175+ assert network_wallet .chain_id == 84532
176+ assert network_wallet .paymaster_url is None
177+
178+
179+ @patch ("cdp.Cdp.api_clients" )
180+ def test_network_scoped_wallet_send_operation (
181+ mock_api_clients , smart_wallet_factory , user_operation_model_factory , account_factory
182+ ):
183+ """Test NetworkScopedSmartWallet send_user_operation method."""
184+ account = account_factory ()
185+ smart_wallet_address = "0x1234567890123456789012345678901234567890"
186+ network_wallet = smart_wallet_factory (smart_wallet_address , account ).use_network (84532 )
187+
188+ mock_user_operation = user_operation_model_factory (
189+ id = "test-op-id" ,
190+ network_id = "base-sepolia" ,
191+ calls = [Call (to = account .address , value = "1000000000000000000" , data = "0x" )],
192+ unsigned_payload = "0x" + "0" * 64 ,
193+ signature = "0xe0a180fdd0fe38037cc878c03832861b40a29d32bd7b40b10c9e1efc8c1468a05ae06d1624896d0d29f4b31e32772ea3cb1b4d7ed4e077e5da28dcc33c0e78121c" ,
194+ transaction_hash = "0x4567890123456789012345678901234567890123" ,
195+ status = "pending" ,
196+ )
197+
198+ mock_create_user_operation = Mock (return_value = mock_user_operation )
199+ mock_api_clients .smart_wallets .create_user_operation = mock_create_user_operation
200+
201+ calls = [EVMCallDict (to = account .address , value = 1000000000000000000 , data = "0x" )]
202+ user_operation = network_wallet .send_user_operation (calls = calls )
203+
204+ assert user_operation .user_operation_id == mock_user_operation .id
205+ assert user_operation .smart_wallet_address == smart_wallet_address
206+ assert user_operation .unsigned_payload == mock_user_operation .unsigned_payload
207+ assert user_operation .signature == mock_user_operation .signature
208+ assert user_operation .transaction_hash == mock_user_operation .transaction_hash
209+ assert user_operation .status == UserOperation .Status (mock_user_operation .status )
210+ assert not user_operation .terminal_state
211+
212+
213+ def test_network_scoped_wallet_string_representations (smart_wallet_factory , account_factory ):
214+ """Test NetworkScopedSmartWallet string representations."""
215+ account = account_factory ()
216+ address = "0x1234567890123456789012345678901234567890"
217+ network_wallet = smart_wallet_factory (address , account ).use_network (84532 )
218+
219+ assert str (network_wallet ) == f"Network Scoped Smart Wallet: { address } (Chain ID: 84532)"
220+ assert (
221+ repr (network_wallet )
222+ == f"Network Scoped Smart Wallet: (model=SmartWalletModel(address='{ address } '), network=Network(chain_id=84532, paymaster_url=None))"
223+ )
224+
225+
226+ def test_to_smart_wallet_creation ():
227+ """Test to_smart_wallet function."""
228+ signer = Account .create ()
229+ address = "0x1234567890123456789012345678901234567890"
230+
231+ wallet = to_smart_wallet (address , signer )
232+ assert wallet .address == address
233+ assert wallet .owners == [signer ]
234+
235+
236+ @patch ("cdp.Cdp.api_clients" )
237+ def test_send_user_operation_with_empty_calls (
238+ mock_api_clients , smart_wallet_factory , account_factory
239+ ):
240+ """Test that sending empty calls list raises ValueError."""
241+ account = account_factory ()
242+ smart_wallet_address = "0x1234567890123456789012345678901234567890"
243+ smart_wallet = smart_wallet_factory (smart_wallet_address , account )
244+
245+ with pytest .raises (ValueError , match = "Calls list cannot be empty" ):
246+ smart_wallet .send_user_operation (calls = [], chain_id = 84532 )
247+
248+ mock_api_clients .smart_wallets .create_user_operation .assert_not_called ()
0 commit comments