def get_keeper_params(oracles_contract: Contract, multicall_contract: Contract) -> Parameters: """Returns keeper params for checking whether to submit the votes.""" calls = [ { "target": oracles_contract.address, "callData": oracles_contract.encodeABI(fn_name="paused"), }, { "target": oracles_contract.address, "callData": oracles_contract.encodeABI(fn_name="currentRewardsNonce"), }, { "target": oracles_contract.address, "callData": oracles_contract.encodeABI(fn_name="currentValidatorsNonce"), }, { "target": oracles_contract.address, "callData": oracles_contract.encodeABI(fn_name="getRoleMemberCount", args=[ORACLE_ROLE]), }, ] response = multicall_contract.functions.aggregate(calls).call()[1] paused = bool(Web3.toInt(primitive=response[0])) rewards_nonce = Web3.toInt(primitive=response[1]) validators_nonce = Web3.toInt(primitive=response[2]) total_oracles = Web3.toInt(primitive=response[3]) calls = [] for i in range(total_oracles): calls.append({ "target": oracles_contract.address, "callData": oracles_contract.encodeABI(fn_name="getRoleMember", args=[ORACLE_ROLE, i]), }) response = multicall_contract.functions.aggregate(calls).call()[1] oracles: List[ChecksumAddress] = [] for addr in response: oracles.append(Web3.toChecksumAddress(Web3.toBytes(Web3.toInt(addr)))) return Parameters( paused=paused, rewards_nonce=rewards_nonce, validators_nonce=validators_nonce, oracles=oracles, )
def get_constructor_arguments(contract: Contract, args: Optional[list] = None, kwargs: Optional[dict] = None): """Get constructor arguments for Etherscan verify. https://etherscanio.freshdesk.com/support/solutions/articles/16000053599-contract-verification-constructor-arguments """ # return contract._encode_constructor_data(args=args, kwargs=kwargs) constructor_abi = get_constructor_abi(contract.abi) # constructor_abi can be none in case of libraries if constructor_abi is None: return to_hex(contract.bytecode) if args is not None: return contract.encodeABI(constructor_abi['name'], args)[2:] # No 0x else: constructor_abi = get_constructor_abi(contract.abi) kwargs = kwargs or {} arguments = merge_args_and_kwargs(constructor_abi, [], kwargs) # deploy_data = add_0x_prefix( # contract._encode_abi(constructor_abi, arguments) # ) # TODO: Looks like recent Web3.py ABI change deploy_data = encode_abi(contract.web3, constructor_abi, arguments) return deploy_data
def execute(self, to_contract: Contract, func: str, args=None, amount_in_eth: Optional[Decimal] = None, max_gas=300000): """Calls a smart contract from the hosted wallet. Creates a transaction that is proxyed through hosted wallet execute method. We need to have ABI as Populus Contract instance. :param wallet_address: Wallet address :param contract: Contract to called as address bound Populus Contract class :param func: Method name to be called :param args: Arguments passed to the method :param value: Additional value carried in the call in ETH :param gas: The max amount of gas the coinbase account is allowed to pay for this transaction. :return: txid of the execution as hex string """ assert isinstance(to_contract, Contract) if amount_in_eth: assert isinstance(amount_in_eth, Decimal) # Don't let floats slip through value = to_wei(amount_in_eth) else: value = 0 # Encode function arguments # function_abi = to_contract._find_matching_fn_abi(func, args) # 4 byte function hash # function_selector = function_abi_to_4byte_selector(function_abi) # data payload passed to the function arg_data = to_contract.encodeABI(func, args=args) call_data = arg_data # Latest web3 behavior, no manual function selector needed # test_event_execute() call data should look like # function selector + random int as 256-bit # 0x5093dc7d000000000000000000000000000000000000000000000000000000002a3f58fe # web3 takes bytes argument as actual bytes, not hex call_data = binascii.unhexlify(call_data[2:]) tx_info = { # The Ethereum account that pays the gas for this operation "from": self.contract.web3.eth.coinbase, "gas": max_gas, } txid = self.contract.transact(tx_info).execute(to_contract.address, value, max_gas, call_data) return txid
def execute( self, to_contract: Contract, func: str, args=None, amount_in_eth: Optional[Decimal] = None, max_gas=100000 ): """Calls a smart contract from the hosted wallet. Creates a transaction that is proxyed through hosted wallet execute method. We need to have ABI as Populus Contract instance. :param wallet_address: Wallet address :param contract: Contract to called as address bound Populus Contract class :param func: Method name to be called :param args: Arguments passed to the method :param value: Additional value carried in the call in ETH :param gas: The max amount of gas the coinbase account is allowed to pay for this transaction. :return: txid of the execution as hex string """ assert isinstance(to_contract, Contract) if amount_in_eth: assert isinstance(amount_in_eth, Decimal) # Don't let floats slip through value = to_wei(amount_in_eth) else: value = 0 # Encode function arguments function_abi = to_contract._find_matching_fn_abi(func, args) # 4 byte function hash function_selector = function_abi_to_4byte_selector(function_abi) # data payload passed to the function arg_data = to_contract.encodeABI(func, args=args) call_data = function_selector + arg_data[2:] # test_event_execute() call data should look like # function selector + random int as 256-bit # 0x5093dc7d000000000000000000000000000000000000000000000000000000002a3f58fe # web3 takes bytes argument as actual bytes, not hex call_data = binascii.unhexlify(call_data[2:]) tx_info = { # The Ethereum account that pays the gas for this operation "from": self.contract.web3.eth.coinbase, "gas": max_gas, } txid = self.contract.transact(tx_info).execute(to_contract.address, value, max_gas, call_data) return txid