1
- import pytest
2
1
from decimal import Decimal
3
2
from unittest .mock import MagicMock , patch
4
3
4
+ import pytest
5
5
from aleph_message .models import Chain
6
- from web3 .types import TxParams
7
6
from web3 .exceptions import ContractCustomError
7
+ from web3 .types import TxParams
8
8
9
9
from aleph .sdk .chains .ethereum import ETHAccount
10
- from aleph .sdk .exceptions import InsufficientFundsError
11
- from aleph .sdk .evm_utils import MIN_ETH_BALANCE_WEI
12
10
from aleph .sdk .connectors .superfluid import Superfluid
11
+ from aleph .sdk .exceptions import InsufficientFundsError
13
12
from aleph .sdk .types import TokenType
14
13
15
14
@@ -23,11 +22,13 @@ def mock_eth_account():
23
22
account ._provider = MagicMock ()
24
23
account ._provider .eth = MagicMock ()
25
24
account ._provider .eth .gas_price = 20_000_000_000 # 20 Gwei
26
- account ._provider .eth .estimate_gas = MagicMock (return_value = 100_000 ) # 100k gas units
27
-
25
+ account ._provider .eth .estimate_gas = MagicMock (
26
+ return_value = 100_000
27
+ ) # 100k gas units
28
+
28
29
# Mock get_eth_balance to return a specific balance
29
30
account .get_eth_balance = MagicMock (return_value = 10 ** 18 ) # 1 ETH
30
-
31
+
31
32
return account
32
33
33
34
@@ -38,120 +39,132 @@ def mock_superfluid(mock_eth_account):
38
39
superfluid .cfaV1Instance .create_flow = MagicMock ()
39
40
superfluid .super_token = "0xsupertokenaddress"
40
41
superfluid .normalized_address = "0xsenderaddress"
41
-
42
+
42
43
# Mock the operation
43
44
operation = MagicMock ()
44
45
operation ._get_populated_transaction_request = MagicMock (
45
46
return_value = {"value" : 0 , "gas" : 100000 , "gasPrice" : 20_000_000_000 }
46
47
)
47
48
superfluid .cfaV1Instance .create_flow .return_value = operation
48
-
49
+
49
50
return superfluid
50
51
51
52
52
53
class TestGasEstimation :
53
54
def test_can_transact_with_sufficient_funds (self , mock_eth_account ):
54
55
tx = TxParams ({"to" : "0xreceiver" , "value" : 0 })
55
-
56
+
56
57
# Should pass with 1 ETH balance against ~0.002 ETH gas cost
57
58
assert mock_eth_account .can_transact (tx = tx , block = True ) is True
58
-
59
+
59
60
def test_can_transact_with_insufficient_funds (self , mock_eth_account ):
60
61
tx = TxParams ({"to" : "0xreceiver" , "value" : 0 })
61
-
62
+
62
63
# Set balance to almost zero
63
64
mock_eth_account .get_eth_balance = MagicMock (return_value = 1000 )
64
-
65
+
65
66
# Should raise InsufficientFundsError
66
67
with pytest .raises (InsufficientFundsError ) as exc_info :
67
68
mock_eth_account .can_transact (tx = tx , block = True )
68
-
69
+
69
70
assert exc_info .value .token_type == TokenType .GAS
70
-
71
+
71
72
def test_can_transact_with_legacy_gas_price (self , mock_eth_account ):
72
- tx = TxParams ({
73
- "to" : "0xreceiver" ,
74
- "value" : 0 ,
75
- "gasPrice" : 30_000_000_000 # 30 Gwei
76
- })
77
-
73
+ tx = TxParams (
74
+ {"to" : "0xreceiver" , "value" : 0 , "gasPrice" : 30_000_000_000 } # 30 Gwei
75
+ )
76
+
78
77
# Should use the tx's gasPrice instead of default
79
78
mock_eth_account .can_transact (tx = tx , block = True )
80
-
79
+
81
80
# It should have used the tx's gasPrice for calculation
82
81
mock_eth_account ._provider .eth .estimate_gas .assert_called_once ()
83
-
82
+
84
83
def test_can_transact_with_eip1559_gas (self , mock_eth_account ):
85
- tx = TxParams ({
86
- "to" : "0xreceiver" ,
87
- "value" : 0 ,
88
- "maxFeePerGas" : 40_000_000_000 # 40 Gwei
89
- })
90
-
84
+ tx = TxParams (
85
+ {"to" : "0xreceiver" , "value" : 0 , "maxFeePerGas" : 40_000_000_000 } # 40 Gwei
86
+ )
87
+
91
88
# Should use the tx's maxFeePerGas
92
89
mock_eth_account .can_transact (tx = tx , block = True )
93
-
90
+
94
91
# It should have used the tx's maxFeePerGas for calculation
95
92
mock_eth_account ._provider .eth .estimate_gas .assert_called_once ()
96
-
93
+
97
94
def test_can_transact_with_contract_error (self , mock_eth_account ):
98
95
tx = TxParams ({"to" : "0xreceiver" , "value" : 0 })
99
-
96
+
100
97
# Make estimate_gas throw a ContractCustomError
101
- mock_eth_account ._provider .eth .estimate_gas .side_effect = ContractCustomError ("error" )
102
-
98
+ mock_eth_account ._provider .eth .estimate_gas .side_effect = ContractCustomError (
99
+ "error"
100
+ )
101
+
103
102
# Should fallback to MIN_ETH_BALANCE_WEI
104
103
mock_eth_account .can_transact (tx = tx , block = True )
105
-
104
+
106
105
# It should have called estimate_gas
107
106
mock_eth_account ._provider .eth .estimate_gas .assert_called_once ()
108
107
109
108
110
109
class TestSuperfluidFlowEstimation :
111
110
@pytest .mark .asyncio
112
- async def test_simulate_create_tx_flow_success (self , mock_superfluid , mock_eth_account ):
111
+ async def test_simulate_create_tx_flow_success (
112
+ self , mock_superfluid , mock_eth_account
113
+ ):
113
114
# Patch the can_transact method to simulate a successful transaction
114
- with patch .object (mock_eth_account , ' can_transact' , return_value = True ):
115
+ with patch .object (mock_eth_account , " can_transact" , return_value = True ):
115
116
result = mock_superfluid ._simulate_create_tx_flow (Decimal ("0.00000005" ))
116
117
assert result is True
117
-
118
+
118
119
# Verify the flow was correctly simulated but not executed
119
120
mock_superfluid .cfaV1Instance .create_flow .assert_called_once ()
120
- assert "0x0000000000000000000000000000000000000001" in str (mock_superfluid .cfaV1Instance .create_flow .call_args )
121
-
121
+ assert "0x0000000000000000000000000000000000000001" in str (
122
+ mock_superfluid .cfaV1Instance .create_flow .call_args
123
+ )
124
+
122
125
@pytest .mark .asyncio
123
- async def test_simulate_create_tx_flow_contract_error (self , mock_superfluid , mock_eth_account ):
126
+ async def test_simulate_create_tx_flow_contract_error (
127
+ self , mock_superfluid , mock_eth_account
128
+ ):
124
129
# Setup a contract error code for insufficient deposit
125
130
error = ContractCustomError ("Insufficient deposit" )
126
131
error .data = "0xea76c9b3" # This is the specific error code checked in the code
127
-
132
+
128
133
# Mock can_transact to throw the error
129
- with patch .object (mock_eth_account , ' can_transact' , side_effect = error ):
134
+ with patch .object (mock_eth_account , " can_transact" , side_effect = error ):
130
135
# Also mock get_super_token_balance for the error case
131
- with patch .object (mock_eth_account , 'get_super_token_balance' , return_value = 0 ):
136
+ with patch .object (
137
+ mock_eth_account , "get_super_token_balance" , return_value = 0
138
+ ):
132
139
# Should raise InsufficientFundsError for ALEPH token
133
140
with pytest .raises (InsufficientFundsError ) as exc_info :
134
141
mock_superfluid ._simulate_create_tx_flow (Decimal ("0.00000005" ))
135
-
142
+
136
143
assert exc_info .value .token_type == TokenType .ALEPH
137
-
144
+
138
145
@pytest .mark .asyncio
139
- async def test_simulate_create_tx_flow_other_error (self , mock_superfluid , mock_eth_account ):
146
+ async def test_simulate_create_tx_flow_other_error (
147
+ self , mock_superfluid , mock_eth_account
148
+ ):
140
149
# Setup a different contract error code
141
150
error = ContractCustomError ("Other error" )
142
151
error .data = "0xsomeothercode"
143
-
152
+
144
153
# Mock can_transact to throw the error
145
- with patch .object (mock_eth_account , ' can_transact' , side_effect = error ):
154
+ with patch .object (mock_eth_account , " can_transact" , side_effect = error ):
146
155
# Should return False for other errors
147
156
result = mock_superfluid ._simulate_create_tx_flow (Decimal ("0.00000005" ))
148
157
assert result is False
149
-
158
+
150
159
@pytest .mark .asyncio
151
160
async def test_can_start_flow_uses_simulation (self , mock_superfluid ):
152
161
# Mock _simulate_create_tx_flow to verify it's called
153
- with patch .object (mock_superfluid , '_simulate_create_tx_flow' , return_value = True ) as mock_simulate :
162
+ with patch .object (
163
+ mock_superfluid , "_simulate_create_tx_flow" , return_value = True
164
+ ) as mock_simulate :
154
165
result = mock_superfluid .can_start_flow (Decimal ("0.00000005" ))
155
-
166
+
156
167
assert result is True
157
- mock_simulate .assert_called_once_with (flow = Decimal ("0.00000005" ), block = True )
168
+ mock_simulate .assert_called_once_with (
169
+ flow = Decimal ("0.00000005" ), block = True
170
+ )
0 commit comments