def test_safe_creation_estimate(self): url = reverse('v2:safe-creation-estimates') number_owners = 4 data = { 'numberOwners': number_owners } response = self.client.post(url, data=data, format='json') self.assertEqual(response.status_code, status.HTTP_200_OK) safe_creation_estimates = response.json() self.assertEqual(len(safe_creation_estimates), 1) safe_creation_estimate = safe_creation_estimates[0] self.assertEqual(safe_creation_estimate['paymentToken'], NULL_ADDRESS) token = TokenFactory(gas=True, fixed_eth_conversion=None) response = self.client.post(url, data=data, format='json') self.assertEqual(response.status_code, status.HTTP_200_OK) safe_creation_estimates = response.json() # No price oracles, so no estimation self.assertEqual(len(safe_creation_estimates), 1) fixed_price_token = TokenFactory(gas=True, fixed_eth_conversion=1.0) response = self.client.post(url, data=data, format='json') self.assertEqual(response.status_code, status.HTTP_200_OK) safe_creation_estimates = response.json() # Fixed price oracle, so estimation will work self.assertEqual(len(safe_creation_estimates), 2) safe_creation_estimate = safe_creation_estimates[1] self.assertEqual(safe_creation_estimate['paymentToken'], fixed_price_token.address) self.assertGreater(int(safe_creation_estimate['payment']), 0) self.assertGreater(int(safe_creation_estimate['gasPrice']), 0) self.assertGreater(int(safe_creation_estimate['gas']), 0)
def test_estimate_safe_creation_for_all_tokens(self): number_owners = 4 safe_creation_estimates = self.safe_creation_service.estimate_safe_creation_for_all_tokens( number_owners) self.assertEqual(len(safe_creation_estimates), 1) safe_creation_estimate = safe_creation_estimates[0] self.assertEqual(safe_creation_estimate.payment_token, NULL_ADDRESS) token = TokenFactory(gas=True, fixed_eth_conversion=None) safe_creation_estimates = self.safe_creation_service.estimate_safe_creation_for_all_tokens( number_owners) # No price oracles, so no estimation self.assertEqual(len(safe_creation_estimates), 1) fixed_price_token = TokenFactory(gas=True, fixed_eth_conversion=1.0) safe_creation_estimates = self.safe_creation_service.estimate_safe_creation_for_all_tokens( number_owners) # Fixed price oracle, so estimation will work self.assertEqual(len(safe_creation_estimates), 2) safe_creation_estimate = safe_creation_estimates[1] self.assertEqual(safe_creation_estimate.payment_token, fixed_price_token.address) self.assertGreater(safe_creation_estimate.payment, 0) self.assertGreater(safe_creation_estimate.gas_price, 0) self.assertGreater(safe_creation_estimate.gas, 0)
def test_estimate_tx_for_all_tokent(self): safe_address = self.deploy_test_safe().safe_address to = Account.create().address value = 0 data = b'' operation = SafeOperation.CALL.value # TokenFactory(address=gas_token, gas=True) transaction_estimations = self.transaction_service.estimate_tx_for_all_tokens( safe_address, to, value, data, operation) self.assertEqual(transaction_estimations.last_used_nonce, None) self.assertGreater(transaction_estimations.safe_tx_gas, 0) self.assertEqual(transaction_estimations.operational_gas, 0) # Operational gas must be `0` for new Safes self.assertEqual(len(transaction_estimations.estimations), 1) # Just ether estimation = transaction_estimations.estimations[0] self.assertGreater(estimation.gas_price, 0) self.assertGreater(estimation.base_gas, 0) self.assertEqual(estimation.gas_token, NULL_ADDRESS) TokenFactory(address=Account.create().address, gas=True, fixed_eth_conversion=None) transaction_estimations = self.transaction_service.estimate_tx_for_all_tokens( safe_address, to, value, data, operation) self.assertEqual(len(transaction_estimations.estimations), 1) # Just ether as no price was configured valid_token = TokenFactory(address=Account.create().address, gas=True, fixed_eth_conversion=2) transaction_estimations = self.transaction_service.estimate_tx_for_all_tokens( safe_address, to, value, data, operation) self.assertEqual(transaction_estimations.last_used_nonce, None) self.assertGreater(transaction_estimations.safe_tx_gas, 0) self.assertEqual(transaction_estimations.operational_gas, 0) # Operational gas must be `0` for new Safes self.assertEqual(len(transaction_estimations.estimations), 2) # Just ether estimation_ether = transaction_estimations.estimations[0] self.assertGreater(estimation_ether.gas_price, 0) self.assertGreater(estimation_ether.base_gas, 0) self.assertEqual(estimation_ether.gas_token, NULL_ADDRESS) estimation_token = transaction_estimations.estimations[1] self.assertAlmostEqual(estimation_token.gas_price, estimation_ether.gas_price // 2, delta=1.0) self.assertGreater(estimation_token.base_gas, estimation_ether.base_gas) self.assertEqual(estimation_token.gas_token, valid_token.address)
def test_estimate_tx(self): safe_address = Account.create().address to = Account.create().address value = 0 data = b'' operation = SafeOperation.CALL.value gas_token = Account().create().address with self.assertRaises(InvalidGasToken): self.transaction_service.estimate_tx(safe_address, to, value, data, operation, gas_token) TokenFactory(address=gas_token, gas=True) with self.assertRaises(SafeDoesNotExist): self.transaction_service.estimate_tx(safe_address, to, value, data, operation, gas_token) # We need a real safe deployed for this method to work gas_token = NULL_ADDRESS safe_address = self.deploy_test_safe().safe_address transaction_estimation = self.transaction_service.estimate_tx( safe_address, to, value, data, operation, gas_token) self.assertEqual(transaction_estimation.last_used_nonce, None) self.assertGreater(transaction_estimation.safe_tx_gas, 0) self.assertGreater(transaction_estimation.base_gas, 0) self.assertGreater(transaction_estimation.data_gas, 0) self.assertGreater(transaction_estimation.gas_price, 0) self.assertEqual(transaction_estimation.operational_gas, 0) self.assertEqual(transaction_estimation.gas_token, NULL_ADDRESS)
def test_safe_creation_estimate(self): data = { 'number_owners': 4, 'payment_token': None, } response = self.client.post(reverse('v1:safe-creation-estimate'), data=data, format='json') response_json = response.json() for field in ['payment', 'gasPrice', 'gas']: self.assertIn(field, response_json) self.assertGreater(int(response_json[field]), 0) estimated_payment = response_json['payment'] # With payment token erc20_contract = self.deploy_example_erc20(10000, NULL_ADDRESS) payment_token = erc20_contract.address token_model = TokenFactory(address=payment_token, gas=True, fixed_eth_conversion=0.1) data = { 'number_owners': 4, 'payment_token': payment_token, } response = self.client.post(reverse('v1:safe-creation-estimate'), data=data, format='json') response_json = response.json() for field in ['payment', 'gasPrice', 'gas']: self.assertIn(field, response_json) self.assertGreater(int(response_json[field]), 0) self.assertGreater(response_json['payment'], estimated_payment)
def test_safe_multisig_tx_estimates(self): my_safe_address = get_eth_address_with_invalid_checksum() response = self.client.post(reverse('v1:safe-multisig-tx-estimates', args=(my_safe_address,)), data={}, format='json') self.assertEqual(response.status_code, status.HTTP_422_UNPROCESSABLE_ENTITY) my_safe_address, _ = get_eth_address_with_key() response = self.client.post(reverse('v1:safe-multisig-tx-estimates', args=(my_safe_address,)), data={}, format='json') self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) initial_funding = self.w3.toWei(0.0001, 'ether') safe_creation = self.deploy_test_safe(number_owners=3, threshold=2, initial_funding_wei=initial_funding) my_safe_address = safe_creation.safe_address SafeContractFactory(address=my_safe_address) to = Account.create().address tx = { 'to': to, 'value': initial_funding // 2, 'data': '0x', 'operation': 1 } response = self.client.post(reverse('v1:safe-multisig-tx-estimates', args=(my_safe_address,)), data=tx, format='json') self.assertEqual(response.status_code, status.HTTP_200_OK) response = response.json() self.assertGreater(int(response['safeTxGas']), 0) self.assertEqual(response['operationalGas'], '0') self.assertIsNone(response['lastUsedNonce']) self.assertEqual(len(response['estimations']), 1) estimation = response['estimations'][0] self.assertGreater(int(estimation['baseGas']), 0) self.assertGreater(int(estimation['gasPrice']), 0) self.assertEqual(estimation['gasToken'], NULL_ADDRESS) valid_token = TokenFactory(address=Account.create().address, gas=True, fixed_eth_conversion=2) response = self.client.post(reverse('v1:safe-multisig-tx-estimates', args=(my_safe_address,)), data=tx, format='json') self.assertEqual(response.status_code, status.HTTP_200_OK) response = response.json() self.assertGreater(int(response['safeTxGas']), 0) self.assertEqual(response['operationalGas'], '0') self.assertIsNone(response['lastUsedNonce']) self.assertEqual(len(response['estimations']), 2) estimation_ether = response['estimations'][0] self.assertGreater(int(estimation_ether['baseGas']), 0) self.assertGreater(int(estimation_ether['gasPrice']), 0) self.assertEqual(estimation_ether['gasToken'], NULL_ADDRESS) estimation_token = response['estimations'][1] self.assertGreater(int(estimation_token['baseGas']), int(estimation_ether['baseGas'])) self.assertAlmostEqual(int(estimation_token['gasPrice']), int(estimation_ether['gasPrice']) // 2, delta=1.0) self.assertEqual(estimation_token['gasToken'], valid_token.address)
def test_estimate_safe_creation2(self): gas_price = self.safe_creation_service._get_configured_gas_price() number_owners = 4 payment_token = None safe_creation_estimate = self.safe_creation_service.estimate_safe_creation2( number_owners, payment_token) self.assertGreater(safe_creation_estimate.gas, 0) self.assertEqual(safe_creation_estimate.gas_price, gas_price) self.assertGreater(safe_creation_estimate.payment, 0) self.assertEqual(safe_creation_estimate.payment_token, NULL_ADDRESS) estimated_payment = safe_creation_estimate.payment # Compare with safe_creation salt_nonce = 4815162342 owners = [Account.create().address for _ in range(number_owners)] threshold = 1 safe_creation_2 = self.safe_creation_service.create2_safe_tx( salt_nonce, owners, threshold, payment_token=payment_token) self.assertAlmostEqual(safe_creation_2.gas_estimated, safe_creation_estimate.gas, delta=1000) # Compare with estimation for all tokens (position 0 is ether) self.assertAlmostEqual( self.safe_creation_service.estimate_safe_creation_for_all_tokens( number_owners)[0].gas, safe_creation_estimate.gas, delta=1000, ) number_owners = 8 payment_token = None safe_creation_estimate = self.safe_creation_service.estimate_safe_creation2( number_owners, payment_token) self.assertGreater(safe_creation_estimate.gas, 0) self.assertEqual(safe_creation_estimate.gas_price, gas_price) self.assertGreater(safe_creation_estimate.payment, estimated_payment) self.assertEqual(safe_creation_estimate.payment_token, NULL_ADDRESS) payment_token = get_eth_address_with_key()[0] with self.assertRaisesMessage(InvalidPaymentToken, payment_token): self.safe_creation_service.estimate_safe_creation2( number_owners, payment_token) number_tokens = 1000 owner = Account.create() erc20 = self.deploy_example_erc20(number_tokens, owner.address) number_owners = 4 payment_token = erc20.address payment_token_db = TokenFactory(address=payment_token, fixed_eth_conversion=0.1) safe_creation_estimate = self.safe_creation_service.estimate_safe_creation2( number_owners, payment_token) self.assertGreater(safe_creation_estimate.gas, 0) self.assertEqual(safe_creation_estimate.gas_price, gas_price) self.assertGreater(safe_creation_estimate.payment, estimated_payment) self.assertEqual(safe_creation_estimate.payment_token, payment_token)
def test_safe_creation_with_payment_token(self): salt_nonce = generate_salt_nonce() owners = [Account.create().address for _ in range(2)] payment_token = Account.create().address data = { "saltNonce": salt_nonce, "owners": owners, "threshold": len(owners), "paymentToken": payment_token, } response = self.client.post(reverse("v3:safe-creation"), data=data, format="json") self.assertEqual(response.status_code, status.HTTP_422_UNPROCESSABLE_ENTITY) response_json = response.json() self.assertIn("InvalidPaymentToken", response_json["exception"]) self.assertIn(payment_token, response_json["exception"]) fixed_eth_conversion = 0.1 token_model = TokenFactory(address=payment_token, fixed_eth_conversion=fixed_eth_conversion) response = self.client.post(reverse("v3:safe-creation"), data, format="json") self.assertEqual(response.status_code, status.HTTP_201_CREATED) response_json = response.json() safe_address = response_json["safe"] self.assertTrue(check_checksum(safe_address)) self.assertTrue(check_checksum(response_json["paymentReceiver"])) self.assertEqual(response_json["paymentToken"], payment_token) self.assertEqual( int(response_json["payment"]), int(response_json["gasEstimated"]) * int(response_json["gasPriceEstimated"]) * (1 / fixed_eth_conversion), ) self.assertGreater(int(response_json["gasEstimated"]), 0) self.assertGreater(int(response_json["gasPriceEstimated"]), 0) self.assertGreater(len(response_json["setupData"]), 2) self.assertTrue(SafeContract.objects.filter(address=safe_address)) self.assertTrue( SafeCreation2.objects.filter(owners__contains=[owners[0]])) safe_creation = SafeCreation2.objects.get(safe=safe_address) self.assertEqual(safe_creation.payment_token, payment_token) # Payment includes deployment gas + gas to send eth to the deployer self.assertEqual( safe_creation.payment, safe_creation.wei_estimated_deploy_cost() * (1 / fixed_eth_conversion), )
def test_estimate_safe_creation2(self): gas_price = self.safe_creation_service._get_configured_gas_price() number_owners = 4 payment_token = None safe_creation_estimate = self.safe_creation_service.estimate_safe_creation2( number_owners, payment_token) self.assertGreater(safe_creation_estimate.gas, 0) self.assertEqual(safe_creation_estimate.gas_price, gas_price) self.assertGreater(safe_creation_estimate.payment, 0) self.assertEqual(safe_creation_estimate.payment_token, NULL_ADDRESS) estimated_payment = safe_creation_estimate.payment number_owners = 8 payment_token = None safe_creation_estimate = self.safe_creation_service.estimate_safe_creation2( number_owners, payment_token) self.assertGreater(safe_creation_estimate.gas, 0) self.assertEqual(safe_creation_estimate.gas_price, gas_price) self.assertGreater(safe_creation_estimate.payment, estimated_payment) self.assertEqual(safe_creation_estimate.payment_token, NULL_ADDRESS) payment_token = get_eth_address_with_key()[0] with self.assertRaisesMessage(InvalidPaymentToken, payment_token): self.safe_creation_service.estimate_safe_creation2( number_owners, payment_token) number_tokens = 1000 owner = Account.create() erc20 = self.deploy_example_erc20(number_tokens, owner.address) number_owners = 4 payment_token = erc20.address payment_token_db = TokenFactory(address=payment_token, fixed_eth_conversion=0.1) safe_creation_estimate = self.safe_creation_service.estimate_safe_creation2( number_owners, payment_token) self.assertGreater(safe_creation_estimate.gas, 0) self.assertEqual(safe_creation_estimate.gas_price, gas_price) self.assertGreater(safe_creation_estimate.payment, estimated_payment) self.assertEqual(safe_creation_estimate.payment_token, payment_token)
def test_safe_multisig_tx_post_gas_token(self): # Create Safe ------------------------------------------------ w3 = self.ethereum_client.w3 safe_balance = w3.toWei(0.01, 'ether') owner_account = self.create_account() owner = owner_account.address threshold = 1 safe_creation = self.deploy_test_safe(owners=[owner], threshold=threshold, initial_funding_wei=safe_balance) my_safe_address = safe_creation.safe_address self.assertEqual(self.w3.eth.getBalance(my_safe_address), safe_balance) SafeContractFactory(address=my_safe_address) # Get tokens for the safe safe_token_balance = int(1e18) erc20_contract = self.deploy_example_erc20(safe_token_balance, my_safe_address) # Safe prepared -------------------------------------------- to = Account.create().address value = safe_balance tx_data = None operation = SafeOperation.CALL.value refund_receiver = None nonce = 0 gas_token = erc20_contract.address data = { "to": to, "value": value, "data": tx_data, "operation": operation, "gasToken": gas_token } # Get estimation for gas. Token does not exist response = self.client.post(reverse('v1:safe-multisig-tx-estimate', args=(my_safe_address, )), data=data, format='json') self.assertEqual(response.status_code, status.HTTP_422_UNPROCESSABLE_ENTITY) self.assertEqual('InvalidGasToken: %s' % gas_token, response.json()['exception']) # Create token token_model = TokenFactory(address=gas_token) response = self.client.post(reverse('v1:safe-multisig-tx-estimate', args=(my_safe_address, )), data=data, format='json') self.assertEqual(response.status_code, status.HTTP_200_OK) estimation_json = response.json() safe_tx_gas = estimation_json['safeTxGas'] + estimation_json[ 'operationalGas'] data_gas = estimation_json['dataGas'] gas_price = estimation_json['gasPrice'] gas_token = estimation_json['gasToken'] multisig_tx_hash = SafeTx(None, my_safe_address, to, value, tx_data, operation, safe_tx_gas, data_gas, gas_price, gas_token, refund_receiver, safe_nonce=nonce).safe_tx_hash signatures = [ w3.eth.account.signHash(multisig_tx_hash, private_key) for private_key in [owner_account.key] ] signatures_json = [{ 'v': s['v'], 'r': s['r'], 's': s['s'] } for s in signatures] data = { "to": to, "value": value, "data": tx_data, "operation": operation, "safe_tx_gas": safe_tx_gas, "data_gas": data_gas, "gas_price": gas_price, "gas_token": gas_token, "nonce": nonce, "signatures": signatures_json } response = self.client.post(reverse('v1:safe-multisig-txs', args=(my_safe_address, )), data=data, format='json') self.assertEqual(response.status_code, status.HTTP_201_CREATED) tx_hash = response.json()['transactionHash'][2:] # Remove leading 0x safe_multisig_tx = SafeMultisigTx.objects.get( ethereum_tx__tx_hash=tx_hash) self.assertEqual(safe_multisig_tx.to, to) self.assertEqual(safe_multisig_tx.value, value) self.assertEqual(safe_multisig_tx.data, tx_data) self.assertEqual(safe_multisig_tx.operation, operation) self.assertEqual(safe_multisig_tx.safe_tx_gas, safe_tx_gas) self.assertEqual(safe_multisig_tx.data_gas, data_gas) self.assertEqual(safe_multisig_tx.gas_price, gas_price) self.assertEqual(safe_multisig_tx.gas_token, gas_token) self.assertEqual(safe_multisig_tx.nonce, nonce)
def test_safe_creation_with_payment_token(self): s = generate_valid_s() owner1, _ = get_eth_address_with_key() owner2, _ = get_eth_address_with_key() payment_token, _ = get_eth_address_with_key() serializer = SafeCreationSerializer( data={ 's': s, 'owners': [owner1, owner2], 'threshold': 2, 'payment_token': payment_token, }) self.assertTrue(serializer.is_valid()) response = self.client.post(reverse('v1:safe-creation'), data=serializer.data, format='json') self.assertEqual(response.status_code, status.HTTP_422_UNPROCESSABLE_ENTITY) response_json = response.json() self.assertIn('InvalidPaymentToken', response_json['exception']) self.assertIn(payment_token, response_json['exception']) # With previous versions of ganache it failed, because token was on DB but not in blockchain, # so gas cannot be estimated. With new versions of ganache estimation is working token_model = TokenFactory(address=payment_token, fixed_eth_conversion=0.1) response = self.client.post(reverse('v1:safe-creation'), data=serializer.data, format='json') self.assertEqual(response.status_code, status.HTTP_201_CREATED) erc20_contract = self.deploy_example_erc20(10000, NULL_ADDRESS) payment_token = erc20_contract.address serializer = SafeCreationSerializer( data={ 's': s, 'owners': [owner1, owner2], 'threshold': 2, 'payment_token': payment_token, }) self.assertTrue(serializer.is_valid()) token_model = TokenFactory(address=payment_token, fixed_eth_conversion=0.1) response = self.client.post(reverse('v1:safe-creation'), data=serializer.data, format='json') self.assertEqual(response.status_code, status.HTTP_201_CREATED) response_json = response.json() deployer = response_json['deployer'] self.assertTrue(check_checksum(deployer)) self.assertTrue(check_checksum(response_json['safe'])) self.assertEqual(response_json['paymentToken'], payment_token) self.assertTrue( SafeContract.objects.filter(address=response.data['safe'])) safe_creation = SafeCreation.objects.get(deployer=deployer) self.assertIn(owner1, safe_creation.owners) self.assertEqual(safe_creation.payment_token, payment_token) self.assertGreater(safe_creation.payment, safe_creation.wei_deploy_cost()) # Check that payment is more than with ether token_payment = response_json['payment'] serializer = SafeCreationSerializer(data={ 's': s, 'owners': [owner1, owner2], 'threshold': 2, }) self.assertTrue(serializer.is_valid()) response = self.client.post(reverse('v1:safe-creation'), data=serializer.data, format='json') self.assertEqual(response.status_code, status.HTTP_201_CREATED) response_json = response.json() payment_using_ether = response_json['payment'] self.assertGreater(token_payment, payment_using_ether) # Check that token with fixed conversion price to 1 is a little higher than with ether # (We need to pay for storage for token transfer, as funder does not own any token yet) erc20_contract = self.deploy_example_erc20(10000, NULL_ADDRESS) payment_token = erc20_contract.address token_model = TokenFactory(address=payment_token, fixed_eth_conversion=1) serializer = SafeCreationSerializer( data={ 's': s, 'owners': [owner1, owner2], 'threshold': 2, 'payment_token': payment_token }) self.assertTrue(serializer.is_valid()) response = self.client.post(reverse('v1:safe-creation'), data=serializer.data, format='json') self.assertEqual(response.status_code, status.HTTP_201_CREATED) response_json = response.json() deployer = response_json['deployer'] payment_using_token = response_json['payment'] self.assertGreater(payment_using_token, payment_using_ether) safe_creation = SafeCreation.objects.get(deployer=deployer) # Payment includes also the gas to send ether to the safe deployer self.assertGreater(safe_creation.payment, safe_creation.wei_deploy_cost())
def test_safe_multisig_tx_estimates(self): my_safe_address = get_eth_address_with_invalid_checksum() response = self.client.post( reverse("v1:safe-multisig-tx-estimates", args=(my_safe_address, )), data={}, format="json", ) self.assertEqual(response.status_code, status.HTTP_422_UNPROCESSABLE_ENTITY) my_safe_address, _ = get_eth_address_with_key() response = self.client.post( reverse("v1:safe-multisig-tx-estimates", args=(my_safe_address, )), data={}, format="json", ) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) initial_funding = self.w3.toWei(0.0001, "ether") safe = self.deploy_test_safe(number_owners=3, threshold=2, initial_funding_wei=initial_funding) my_safe_address = safe.address to = Account.create().address tx = { "to": to, "value": initial_funding // 2, "data": "0x", "operation": 1 } response = self.client.post( reverse("v1:safe-multisig-tx-estimates", args=(my_safe_address, )), data=tx, format="json", ) self.assertEqual(response.status_code, status.HTTP_200_OK) # Use not existing Safe non_existing_safe_address = Account.create().address response = self.client.post( reverse("v1:safe-multisig-tx-estimates", args=(non_existing_safe_address, )), data=tx, format="json", ) self.assertEqual(response.status_code, status.HTTP_422_UNPROCESSABLE_ENTITY) self.assertIn("SafeDoesNotExist", response.data["exception"]) # Add to database and test SafeContractFactory(address=my_safe_address) response = self.client.post( reverse("v1:safe-multisig-tx-estimates", args=(my_safe_address, )), data=tx, format="json", ) self.assertEqual(response.status_code, status.HTTP_200_OK) response = response.json() self.assertGreater(int(response["safeTxGas"]), 0) self.assertEqual(response["operationalGas"], "0") self.assertIsNone(response["lastUsedNonce"]) self.assertEqual(len(response["estimations"]), 1) estimation = response["estimations"][0] self.assertGreater(int(estimation["baseGas"]), 0) self.assertGreater(int(estimation["gasPrice"]), 0) self.assertEqual(estimation["gasToken"], NULL_ADDRESS) valid_token = TokenFactory(address=Account.create().address, gas=True, fixed_eth_conversion=2) response = self.client.post( reverse("v1:safe-multisig-tx-estimates", args=(my_safe_address, )), data=tx, format="json", ) self.assertEqual(response.status_code, status.HTTP_200_OK) response = response.json() self.assertGreater(int(response["safeTxGas"]), 0) self.assertEqual(response["operationalGas"], "0") self.assertIsNone(response["lastUsedNonce"]) self.assertEqual(len(response["estimations"]), 2) estimation_ether = response["estimations"][0] self.assertGreater(int(estimation_ether["baseGas"]), 0) self.assertGreater(int(estimation_ether["gasPrice"]), 0) self.assertEqual(estimation_ether["gasToken"], NULL_ADDRESS) estimation_token = response["estimations"][1] self.assertGreater(int(estimation_token["baseGas"]), int(estimation_ether["baseGas"])) self.assertAlmostEqual( int(estimation_token["gasPrice"]), int(estimation_ether["gasPrice"]) // 2, delta=1.0, ) self.assertEqual(estimation_token["gasToken"], valid_token.address)