From 19888d460583a6f3c56d416e5fbc6c305a54b1a4 Mon Sep 17 00:00:00 2001 From: Eugene Voronov Date: Sat, 30 Sep 2023 09:49:01 +0300 Subject: [PATCH 1/4] Updated cancel method --- packages/sdk/python/human-protocol-sdk/example.py | 1 - .../human-protocol-sdk/human_protocol_sdk/escrow.py | 8 +++++--- .../test/human_protocol_sdk/agreement/test_measures.py | 1 - 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/sdk/python/human-protocol-sdk/example.py b/packages/sdk/python/human-protocol-sdk/example.py index a07a4bd9d3..506686554f 100644 --- a/packages/sdk/python/human-protocol-sdk/example.py +++ b/packages/sdk/python/human-protocol-sdk/example.py @@ -66,7 +66,6 @@ ) ) - # process annotation data and get quality estimates url = "https://raw.githubusercontent.com/humanprotocol/human-protocol/efa8d3789ac35915b42435011cd0a8d36507564c/packages/sdk/python/human-protocol-sdk/example_annotations.json" annotations = json.loads(StorageClient.download_file_from_url(url)) diff --git a/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/escrow.py b/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/escrow.py index 64ebd6c27a..98d29433c2 100644 --- a/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/escrow.py +++ b/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/escrow.py @@ -447,14 +447,14 @@ def bulk_payout( EscrowClientError, ) - def cancel(self, escrow_address: str) -> None: + def cancel(self, escrow_address: str) -> str: """Cancels the specified escrow and sends the balance to the canceler. Args: escrow_address (str): Address of the escrow to cancel Returns: - None + str Raises: EscrowClientError: If an error occurs while checking the parameters @@ -463,13 +463,15 @@ def cancel(self, escrow_address: str) -> None: if not Web3.is_address(escrow_address): raise EscrowClientError(f"Invalid escrow address: {escrow_address}") - handle_transaction( + transaction_receipt = handle_transaction( self.w3, "Cancel", self._get_escrow_contract(escrow_address).functions.cancel(), EscrowClientError, ) + return transaction_receipt.transactionHash + def abort(self, escrow_address: str) -> None: """Cancels the specified escrow, sends the balance to the canceler and selfdestructs the escrow contract. diff --git a/packages/sdk/python/human-protocol-sdk/test/human_protocol_sdk/agreement/test_measures.py b/packages/sdk/python/human-protocol-sdk/test/human_protocol_sdk/agreement/test_measures.py index 366c06a8c7..35e81c765e 100644 --- a/packages/sdk/python/human-protocol-sdk/test/human_protocol_sdk/agreement/test_measures.py +++ b/packages/sdk/python/human-protocol-sdk/test/human_protocol_sdk/agreement/test_measures.py @@ -19,7 +19,6 @@ def test_agreement(annotations, labels): - # test if both interfaces match k_agree = agreement(annotations, measure="fleiss_kappa", labels=labels)["results"][ "score" From c860cf34392ad344686b7fc181cfa0539d8f84e3 Mon Sep 17 00:00:00 2001 From: Eugene Voronov Date: Tue, 10 Oct 2023 14:46:29 +0300 Subject: [PATCH 2/4] Updated cancel method logic --- .../human_protocol_sdk/escrow.py | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/escrow.py b/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/escrow.py index 905b095453..bf8ba25925 100644 --- a/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/escrow.py +++ b/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/escrow.py @@ -24,6 +24,19 @@ LOG = logging.getLogger("human_protocol_sdk.escrow") +class EscrowCancel: + def __init__(self, tx_hash: str, amount_refunded: any): + """ + Represents the result of an escrow cancellation transaction. + + Args: + tx_hash (str): The hash of the transaction that cancelled the escrow. + amount_refunded (Any): The amount refunded during the escrow cancellation. + """ + self.txHash = tx_hash + self.amountRefunded = amount_refunded + + class EscrowClientError(Exception): """ Raises when some error happens when interacting with escrow. @@ -415,6 +428,65 @@ def cancel(self, escrow_address: str) -> str: return transaction_receipt.transactionHash + def cancel(self, escrow_address: str) -> EscrowCancel: + """ + Cancels the specified escrow and sends the balance to the canceler. + + This method initiates the cancellation of an escrow by interacting with the Ethereum blockchain. + It verifies the validity of the escrow address, initiates the cancellation transaction, and + retrieves information about the refunded amount. + + Args: + escrow_address (str): The Ethereum address of the escrow contract to be cancelled. + + Returns: + EscrowCancel: An instance of the EscrowCancel class containing details of the cancellation transaction, + including the transaction hash and the amount refunded. + + Raises: + EscrowClientError: If an error occurs while checking the parameters + EscrowClientError: If the transfer event associated with the cancellation is not found in the transaction logs + """ + + if not Web3.is_address(escrow_address): + raise EscrowClientError(f"Invalid escrow address: {escrow_address}") + + transaction_receipt = handle_transaction( + self.w3, + "Cancel", + self._get_escrow_contract(escrow_address).functions.cancel(), + EscrowClientError, + self.gas_limit, + ) + + amount_transferred = None + token_address = self.get_token_address(escrow_address) + + erc20_interface = get_erc20_interface() + token_contract = self.w3.eth.contract(token_address, abi=erc20_interface["abi"]) + + for log in transaction_receipt["logs"]: + if log["address"] == token_address: + parsed_log = token_contract.events.Transfer().processLog(log) + + from_address = parsed_log[0]["args"]["from"] + if ( + parsed_log[0]["event"] == "Transfer" + and from_address == escrow_address + ): + amount_transferred = parsed_log[0]["args"]["value"] + break + + if amount_transferred is None: + raise EscrowClientError("Transfer Event Not Found in Transaction Logs") + + escrow_cancel_data = EscrowCancel( + tx_hash=transaction_receipt["transactionHash"], + amount_refunded=amount_transferred, + ) + + return escrow_cancel_data + def abort(self, escrow_address: str) -> None: """Cancels the specified escrow, sends the balance to the canceler and selfdestructs the escrow contract. From 8069df202d117feb17aa1da84014e726ff47f935 Mon Sep 17 00:00:00 2001 From: Eugene Voronov Date: Tue, 10 Oct 2023 14:48:01 +0300 Subject: [PATCH 3/4] Removed old implementation of cancel method --- .../human_protocol_sdk/escrow.py | 26 ------------------- 1 file changed, 26 deletions(-) diff --git a/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/escrow.py b/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/escrow.py index bf8ba25925..c09f88398d 100644 --- a/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/escrow.py +++ b/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/escrow.py @@ -402,32 +402,6 @@ def bulk_payout( self.gas_limit, ) - def cancel(self, escrow_address: str) -> str: - """Cancels the specified escrow and sends the balance to the canceler. - - Args: - escrow_address (str): Address of the escrow to cancel - - Returns: - str - - Raises: - EscrowClientError: If an error occurs while checking the parameters - """ - - if not Web3.is_address(escrow_address): - raise EscrowClientError(f"Invalid escrow address: {escrow_address}") - - transaction_receipt = handle_transaction( - self.w3, - "Cancel", - self._get_escrow_contract(escrow_address).functions.cancel(), - EscrowClientError, - self.gas_limit, - ) - - return transaction_receipt.transactionHash - def cancel(self, escrow_address: str) -> EscrowCancel: """ Cancels the specified escrow and sends the balance to the canceler. From eab8a5d0aa9f7a4c022aca0740210414ffe8db79 Mon Sep 17 00:00:00 2001 From: Eric Lee Date: Mon, 8 Jan 2024 17:22:06 -0500 Subject: [PATCH 4/4] fix test and generate doc --- ...human_protocol_sdk.escrow.escrow_client.md | 24 ++++- docs/sdk/python/human_protocol_sdk.escrow.md | 2 + docs/sdk/python/human_protocol_sdk.md | 1 + .../escrow/escrow_client.py | 20 ++--- .../escrow/test_escrow_client.py | 90 ++++++++++++++++++- 5 files changed, 119 insertions(+), 18 deletions(-) diff --git a/docs/sdk/python/human_protocol_sdk.escrow.escrow_client.md b/docs/sdk/python/human_protocol_sdk.escrow.escrow_client.md index 76879c9f8a..c876b9468b 100644 --- a/docs/sdk/python/human_protocol_sdk.escrow.escrow_client.md +++ b/docs/sdk/python/human_protocol_sdk.escrow.escrow_client.md @@ -48,6 +48,18 @@ escrow_client = EscrowClient(w3) ## Module +### *class* human_protocol_sdk.escrow.escrow_client.EscrowCancel(tx_hash, amount_refunded) + +Bases: `object` + +#### \_\_init_\_(tx_hash, amount_refunded) + +Represents the result of an escrow cancellation transaction. +Args: + +> tx_hash (str): The hash of the transaction that cancelled the escrow. +> amount_refunded (Any): The amount refunded during the escrow cancellation. + ### *class* human_protocol_sdk.escrow.escrow_client.EscrowClient(web3) Bases: `object` @@ -215,11 +227,15 @@ Cancels the specified escrow and sends the balance to the canceler. * **escrow_address** (`str`) – Address of the escrow to cancel * **tx_options** (`Optional`[`TxParams`]) – (Optional) Additional transaction parameters * **Return type:** - `None` + [`EscrowCancel`](#human_protocol_sdk.escrow.escrow_client.EscrowCancel) * **Returns:** - None + EscrowCancel: + An instance of the EscrowCancel class containing details of the cancellation transaction, + including the transaction hash and the amount refunded. * **Raises:** - [**EscrowClientError**](#human_protocol_sdk.escrow.escrow_client.EscrowClientError) – If an error occurs while checking the parameters + * [**EscrowClientError**](#human_protocol_sdk.escrow.escrow_client.EscrowClientError) – If an error occurs while checking the parameters + * [**EscrowClientError**](#human_protocol_sdk.escrow.escrow_client.EscrowClientError) – If the transfer event associated with the cancellation + is not found in the transaction logs * **Example:** ```python from eth_typing import URI @@ -242,7 +258,7 @@ Cancels the specified escrow and sends the balance to the canceler. (w3, gas_payer) = get_w3_with_priv_key('YOUR_PRIVATE_KEY') escrow_client = EscrowClient(w3) - transaction_hash = escrow_client.cancel( + escrow_cancel_data = escrow_client.cancel( "0x62dD51230A30401C455c8398d06F85e4EaB6309f" ) ``` diff --git a/docs/sdk/python/human_protocol_sdk.escrow.md b/docs/sdk/python/human_protocol_sdk.escrow.md index fd20e4b026..d5f4da28c0 100644 --- a/docs/sdk/python/human_protocol_sdk.escrow.md +++ b/docs/sdk/python/human_protocol_sdk.escrow.md @@ -8,6 +8,8 @@ obtain information from both the contracts and subgraph. * [human_protocol_sdk.escrow.escrow_client module](human_protocol_sdk.escrow.escrow_client.md) * [Code Example](human_protocol_sdk.escrow.escrow_client.md#code-example) * [Module](human_protocol_sdk.escrow.escrow_client.md#module) + * [`EscrowCancel`](human_protocol_sdk.escrow.escrow_client.md#human_protocol_sdk.escrow.escrow_client.EscrowCancel) + * [`EscrowCancel.__init__()`](human_protocol_sdk.escrow.escrow_client.md#human_protocol_sdk.escrow.escrow_client.EscrowCancel.__init__) * [`EscrowClient`](human_protocol_sdk.escrow.escrow_client.md#human_protocol_sdk.escrow.escrow_client.EscrowClient) * [`EscrowClient.__init__()`](human_protocol_sdk.escrow.escrow_client.md#human_protocol_sdk.escrow.escrow_client.EscrowClient.__init__) * [`EscrowClient.abort()`](human_protocol_sdk.escrow.escrow_client.md#human_protocol_sdk.escrow.escrow_client.EscrowClient.abort) diff --git a/docs/sdk/python/human_protocol_sdk.md b/docs/sdk/python/human_protocol_sdk.md index b42a16d2be..c7ee11f32c 100644 --- a/docs/sdk/python/human_protocol_sdk.md +++ b/docs/sdk/python/human_protocol_sdk.md @@ -36,6 +36,7 @@ * [human_protocol_sdk.escrow.escrow_client module](human_protocol_sdk.escrow.escrow_client.md) * [Code Example](human_protocol_sdk.escrow.escrow_client.md#code-example) * [Module](human_protocol_sdk.escrow.escrow_client.md#module) + * [`EscrowCancel`](human_protocol_sdk.escrow.escrow_client.md#human_protocol_sdk.escrow.escrow_client.EscrowCancel) * [`EscrowClient`](human_protocol_sdk.escrow.escrow_client.md#human_protocol_sdk.escrow.escrow_client.EscrowClient) * [`EscrowClientError`](human_protocol_sdk.escrow.escrow_client.md#human_protocol_sdk.escrow.escrow_client.EscrowClientError) * [`EscrowConfig`](human_protocol_sdk.escrow.escrow_client.md#human_protocol_sdk.escrow.escrow_client.EscrowConfig) diff --git a/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/escrow/escrow_client.py b/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/escrow/escrow_client.py index e7c1e9481b..0cbef4fa60 100644 --- a/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/escrow/escrow_client.py +++ b/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/escrow/escrow_client.py @@ -63,8 +63,10 @@ def get_w3_with_priv_key(priv_key: str): handle_transaction, ) from web3 import Web3, contract +from web3 import eth from web3.middleware import geth_poa_middleware from web3.types import TxParams +from eth_utils import abi from human_protocol_sdk.utils import validate_url @@ -703,9 +705,8 @@ def cancel( An instance of the EscrowCancel class containing details of the cancellation transaction, including the transaction hash and the amount refunded. - :raise - EscrowClientError: If an error occurs while checking the parameters - EscrowClientError: If the transfer event associated with the cancellation + :raise EscrowClientError: If an error occurs while checking the parameters + :raise EscrowClientError: If the transfer event associated with the cancellation is not found in the transaction logs @@ -732,7 +733,7 @@ def get_w3_with_priv_key(priv_key: str): (w3, gas_payer) = get_w3_with_priv_key('YOUR_PRIVATE_KEY') escrow_client = EscrowClient(w3) - transaction_hash = escrow_client.cancel( + escrow_cancel_data = escrow_client.cancel( "0x62dD51230A30401C455c8398d06F85e4EaB6309f" ) """ @@ -756,21 +757,20 @@ def get_w3_with_priv_key(priv_key: str): for log in transaction_receipt["logs"]: if log["address"] == token_address: - parsed_log = token_contract.events.Transfer().processLog(log) + processed_log = token_contract.events.Transfer().process_log(log) - from_address = parsed_log[0]["args"]["from"] if ( - parsed_log[0]["event"] == "Transfer" - and from_address == escrow_address + processed_log["event"] == "Transfer" + and processed_log["args"]["from"] == escrow_address ): - amount_transferred = parsed_log[0]["args"]["value"] + amount_transferred = processed_log["args"]["value"] break if amount_transferred is None: raise EscrowClientError("Transfer Event Not Found in Transaction Logs") escrow_cancel_data = EscrowCancel( - tx_hash=transaction_receipt["transactionHash"], + tx_hash=transaction_receipt["transactionHash"].hex(), amount_refunded=amount_transferred, ) diff --git a/packages/sdk/python/human-protocol-sdk/test/human_protocol_sdk/escrow/test_escrow_client.py b/packages/sdk/python/human-protocol-sdk/test/human_protocol_sdk/escrow/test_escrow_client.py index 34b302e8c3..14d3be5587 100644 --- a/packages/sdk/python/human-protocol-sdk/test/human_protocol_sdk/escrow/test_escrow_client.py +++ b/packages/sdk/python/human-protocol-sdk/test/human_protocol_sdk/escrow/test_escrow_client.py @@ -8,6 +8,7 @@ from human_protocol_sdk.escrow import EscrowClient, EscrowClientError, EscrowConfig from human_protocol_sdk.filter import EscrowFilter, FilterError from web3 import Web3 +from web3.constants import ADDRESS_ZERO from web3.middleware import construct_sign_and_send_raw_middleware from web3.providers.rpc import HTTPProvider @@ -1478,12 +1479,49 @@ def test_cancel(self): mock_contract = MagicMock() mock_contract.functions.cancel = MagicMock() self.escrow._get_escrow_contract = MagicMock(return_value=mock_contract) - escrow_address = "0x1234567890123456789012345678901234567890" + escrow_address = "0xa76507AbFE3B67cB25F16DbC75a883D4190B7e46" + token_address = "0x0376D26246Eb35FF4F9924cF13E6C05fd0bD7Fb4" + + self.escrow.get_token_address = MagicMock(return_value=token_address) with patch( "human_protocol_sdk.escrow.escrow_client.handle_transaction" ) as mock_function: - self.escrow.cancel(escrow_address) + tx_hash = bytes.fromhex( + "01682095d5abb0270d11a31139b9a1f410b363c84add467004e728ec831bd529" + ) + amount_refunded = 187744067287473730 + mock_function.return_value = { + "transactionHash": tx_hash, + "logs": [ + { + "logIndex": 0, + "transactionIndex": 0, + "transactionHash": tx_hash, + "blockHash": bytes.fromhex( + "92abf9325a3959a911a2581e9ea36cba3060d8b293b50e5738ff959feb95258a" + ), + "blockNumber": 5, + "address": token_address, + "data": bytes.fromhex( + "000000000000000000000000000000000000000000000000029b003c075b5e42" + ), + "topics": [ + bytes.fromhex( + "ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef" + ), + bytes.fromhex( + "000000000000000000000000a76507abfe3b67cb25f16dbc75a883d4190b7e46" + ), + bytes.fromhex( + "0000000000000000000000005607acf0828e238099aa1784541a5abd7f975c76" + ), + ], + } + ], + } + + escrow_cancel_data = self.escrow.cancel(escrow_address) self.escrow._get_escrow_contract.assert_called_once_with(escrow_address) mock_contract.functions.cancel.assert_called_once_with() @@ -1495,6 +1533,9 @@ def test_cancel(self): None, ) + self.assertEqual(escrow_cancel_data.txHash, tx_hash.hex()) + self.assertEqual(escrow_cancel_data.amountRefunded, amount_refunded) + def test_cancel_invalid_address(self): escrow_address = "invalid_address" @@ -1560,13 +1601,51 @@ def test_cancel_with_tx_options(self): mock_contract = MagicMock() mock_contract.functions.cancel = MagicMock() self.escrow._get_escrow_contract = MagicMock(return_value=mock_contract) - escrow_address = "0x1234567890123456789012345678901234567890" tx_options = {"gas": 50000} + escrow_address = "0xa76507AbFE3B67cB25F16DbC75a883D4190B7e46" + token_address = "0x0376D26246Eb35FF4F9924cF13E6C05fd0bD7Fb4" + + self.escrow.get_token_address = MagicMock(return_value=token_address) + with patch( "human_protocol_sdk.escrow.escrow_client.handle_transaction" ) as mock_function: - self.escrow.cancel(escrow_address, tx_options) + tx_hash = bytes.fromhex( + "01682095d5abb0270d11a31139b9a1f410b363c84add467004e728ec831bd529" + ) + amount_refunded = 187744067287473730 + mock_function.return_value = { + "transactionHash": tx_hash, + "logs": [ + { + "logIndex": 0, + "transactionIndex": 0, + "transactionHash": tx_hash, + "blockHash": bytes.fromhex( + "92abf9325a3959a911a2581e9ea36cba3060d8b293b50e5738ff959feb95258a" + ), + "blockNumber": 5, + "address": token_address, + "data": bytes.fromhex( + "000000000000000000000000000000000000000000000000029b003c075b5e42" + ), + "topics": [ + bytes.fromhex( + "ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef" + ), + bytes.fromhex( + "000000000000000000000000a76507abfe3b67cb25f16dbc75a883d4190b7e46" + ), + bytes.fromhex( + "0000000000000000000000005607acf0828e238099aa1784541a5abd7f975c76" + ), + ], + } + ], + } + + escrow_cancel_data = self.escrow.cancel(escrow_address, tx_options) self.escrow._get_escrow_contract.assert_called_once_with(escrow_address) mock_contract.functions.cancel.assert_called_once_with() @@ -1578,6 +1657,9 @@ def test_cancel_with_tx_options(self): tx_options, ) + self.assertEqual(escrow_cancel_data.txHash, tx_hash.hex()) + self.assertEqual(escrow_cancel_data.amountRefunded, amount_refunded) + def test_abort(self): mock_contract = MagicMock() mock_contract.functions.abort = MagicMock()