diff --git a/crypto/enums/abi_function.py b/crypto/enums/abi_function.py index ae447df..8352e20 100644 --- a/crypto/enums/abi_function.py +++ b/crypto/enums/abi_function.py @@ -10,3 +10,4 @@ class AbiFunction(Enum): VALIDATOR_RESIGNATION = 'resignValidator' UPDATE_VALIDATOR = 'updateValidator' TRANSFER = 'transfer' + APPROVE = 'approve' diff --git a/crypto/transactions/builder/token_approve_builder.py b/crypto/transactions/builder/token_approve_builder.py new file mode 100644 index 0000000..37b50c1 --- /dev/null +++ b/crypto/transactions/builder/token_approve_builder.py @@ -0,0 +1,27 @@ +from crypto.enums.abi_function import AbiFunction +from crypto.enums.contract_abi_type import ContractAbiType +from crypto.transactions.builder.abstract_transaction_builder import ( + AbstractTransactionBuilder, +) +from crypto.transactions.types.evm_call import EvmCall +from crypto.utils.abi_encoder import AbiEncoder +from crypto.utils.transaction_utils import TransactionUtils + + +class TokenApproveBuilder(AbstractTransactionBuilder): + def contract_address(self, address): + self.transaction.data['to'] = address + return self + + def spender(self, address, amount): + encoder = AbiEncoder(ContractAbiType.TOKEN) + payload = encoder.encode_function_call( + AbiFunction.APPROVE.value, [address, amount] + ) + self.transaction.data['data'] = TransactionUtils.parse_hex_from_str( + payload + ) + return self + + def get_transaction_instance(self, data): + return EvmCall(data) diff --git a/tests/fixtures/transactions/token-approve.json b/tests/fixtures/transactions/token-approve.json new file mode 100644 index 0000000..36efcec --- /dev/null +++ b/tests/fixtures/transactions/token-approve.json @@ -0,0 +1,16 @@ +{ + "data": { + "value": "0", + "senderPublicKey": "0243333347c8cbf4e3cbc7a96964181d02a2b0c854faa2fef86b4b8d92afcf473d", + "gasPrice": "5000000000", + "gasLimit": "21000", + "nonce": "1", + "data": "095ea7b30000000000000000000000006f0182a0cc707b055322ccf6d4cb6a5aff1aeb220000000000000000000000000000000000000000000000000000000000000000", + "to": "0x6F0182a0cc707b055322CcF6d4CB6a5Aff1aEb22", + "v": 0, + "r": "f9120b04d549fe3ac691d8ce0cb26a7f104f0686d03434d47528894c0cad1eea", + "s": "3a8960d16a4903b52045b1b40b0f81912bc834985fbd80dc75bf7e60b07bb3e1", + "hash": "2472dc84d8d4b6fb1d843b1417a14ae8de1865bd86f9cd8b444b7c4ff7c6f28c" + }, + "serialized": "f8ab0185012a05f200825208946f0182a0cc707b055322ccf6d4cb6a5aff1aeb2280b844095ea7b30000000000000000000000006f0182a0cc707b055322ccf6d4cb6a5aff1aeb220000000000000000000000000000000000000000000000000000000000000000825c6ba0f9120b04d549fe3ac691d8ce0cb26a7f104f0686d03434d47528894c0cad1eeaa03a8960d16a4903b52045b1b40b0f81912bc834985fbd80dc75bf7e60b07bb3e1" +} diff --git a/tests/transactions/builder/test_token_approve_builder.py b/tests/transactions/builder/test_token_approve_builder.py new file mode 100644 index 0000000..7586537 --- /dev/null +++ b/tests/transactions/builder/test_token_approve_builder.py @@ -0,0 +1,53 @@ +from crypto.transactions.builder.token_approve_builder import ( + TokenApproveBuilder, +) + + +def test_token_approve_transaction(passphrase, load_transaction_fixture): + fixture = load_transaction_fixture('transactions/token-approve') + + builder = ( + TokenApproveBuilder + .new() + .spender(fixture['data']['to'], int(fixture['data']['value'])) + .to(fixture['data']['to']) + .nonce(fixture['data']['nonce']) + .gas_price(fixture['data']['gasPrice']) + .gas_limit(fixture['data']['gasLimit']) + .sign(passphrase) + ) + + assert builder.transaction.data['gasPrice'] == int( + fixture['data']['gasPrice'] + ) + assert builder.transaction.data['gasLimit'] == int( + fixture['data']['gasLimit'] + ) + assert builder.transaction.data['nonce'] == fixture['data']['nonce'] + assert builder.transaction.data['to'].lower() == ( + fixture['data']['to'].lower() + ) + assert builder.transaction.data['v'] == fixture['data']['v'] + assert builder.transaction.data['r'] == fixture['data']['r'] + assert builder.transaction.data['s'] == fixture['data']['s'] + assert builder.transaction.data['hash'] == fixture['data']['hash'] + + assert builder.verify() + + +def test_token_approve_serialization(passphrase, load_transaction_fixture): + fixture = load_transaction_fixture('transactions/token-approve') + + builder = ( + TokenApproveBuilder + .new() + .spender(fixture['data']['to'], int(fixture['data']['value'])) + .to(fixture['data']['to']) + .nonce(fixture['data']['nonce']) + .gas_price(fixture['data']['gasPrice']) + .gas_limit(fixture['data']['gasLimit']) + .sign(passphrase) + ) + + serialized = builder.transaction.serialize().hex() + assert serialized == fixture['serialized']