diff --git a/src/opengradient/client/opg_token.py b/src/opengradient/client/opg_token.py index 864dd64..acf59c7 100644 --- a/src/opengradient/client/opg_token.py +++ b/src/opengradient/client/opg_token.py @@ -95,12 +95,26 @@ def _send_approve_tx( nonce = w3.eth.get_transaction_count(owner, "pending") estimated_gas = approve_fn.estimate_gas({"from": owner}) + # Check native (ETH) balance to cover gas before building/sending tx. + gas_price = w3.eth.gas_price + gas_limit = int(estimated_gas * 1.2) + total_cost = gas_limit * gas_price + native_balance = w3.eth.get_balance(owner) + if native_balance < int (total_cost * 1.1): # Add a 10% buffer to avoid underestimating + needed_eth = Web3.from_wei(total_cost, "ether") + have_eth = Web3.from_wei(native_balance, "ether") + raise RuntimeError( + f"Insufficient native balance for gas on {w3.eth.chain_id}. " + f"Required: {needed_eth:.6f} ETH, Available: {have_eth:.6f} ETH. " + f"Please fund your wallet at the Base Sepolia faucet." + ) + tx = approve_fn.build_transaction( { "from": owner, "nonce": nonce, - "gas": int(estimated_gas * 1.2), - "gasPrice": w3.eth.gas_price, + "gas": gas_limit, + "gasPrice": gas_price, "chainId": w3.eth.chain_id, } ) diff --git a/tests/opg_token_test.py b/tests/opg_token_test.py index b237ccd..abaa2db 100644 --- a/tests/opg_token_test.py +++ b/tests/opg_token_test.py @@ -50,12 +50,26 @@ def _setup_approval_mocks(mock_web3, mock_wallet, contract): approve_fn = MagicMock() contract.functions.approve.return_value = approve_fn approve_fn.estimate_gas.return_value = 50_000 - approve_fn.build_transaction.return_value = {"mock": "tx"} + + # 1. Update build_transaction to include required fields for the signer + approve_fn.build_transaction.return_value = { + "gas": 50_000, + "gasPrice": 1_000_000_000, + "nonce": 7, + "chainId": 84532, + "to": SPENDER_ADDRESS, + "data": "0x", + "from": OWNER_ADDRESS + } mock_web3.eth.get_transaction_count.return_value = 7 mock_web3.eth.gas_price = 1_000_000_000 mock_web3.eth.chain_id = 84532 + # 2. Add the ETH balance mock so your new pre-flight check passes + # We give it 10 ETH by default so existing tests don't "starve" for gas + mock_web3.eth.get_balance.return_value = 10 * 10**18 + signed = MagicMock() signed.raw_transaction = b"\x00" mock_wallet.sign_transaction.return_value = signed @@ -215,3 +229,4 @@ def test_large_amount(self, mock_wallet, mock_web3): assert result.allowance_before == expected_base assert result.tx_hash is None +# --- Add this at the very end of opg_token_test.py --- \ No newline at end of file