コード例 #1
0
def contract_call(ctx, deploy_descriptor, function, params, return_type):
    try:
        with open(deploy_descriptor) as fp:
            contract = json.load(fp)
            source = contract.get('source')
            bytecode = contract.get('bytecode')
            address = contract.get('address')

            kp, _ = _keypair()
            contract = Contract(source,
                                bytecode=bytecode,
                                address=address,
                                client=_epoch_cli())
            result = contract.tx_call(kp, function, params, gas=40000000)
            _pp([
                ('Contract address', contract.address),
                ('Gas price', result.gas_price),
                ('Gas used', result.gas_used),
                ('Return value (encoded)', result.return_value),
            ])
            if result.return_type == 'ok':
                value, remote_type = contract.decode_data(
                    result.return_value, return_type)
                _pp([
                    ('Return value', value),
                    ('Return remote type', remote_type),
                ])

            pass
    except Exception as e:
        print(e)
コード例 #2
0
def contract_compile(contract_file):
    try:
        with open(contract_file) as fp:
            code = fp.read()
            contract = Contract(Contract.SOPHIA, client=_epoch_cli())
            result = contract.compile(code)
            _pp([("bytecode", result)])
    except Exception as e:
        print(e)
コード例 #3
0
def test_sophia_contract_tx_call():
    contract = Contract(aer_identity_contract, Contract.SOPHIA)
    address, tx = contract.tx_create_wait(KEYPAIR, gas=1000)
    print("contract: ", address)
    print("tx contract: ", tx)

    result = contract.tx_call(address, KEYPAIR, 'main', '42')
    assert result is not None
    assert result.return_type == 'ok'
    assert result.return_value.lower() == f'0x{hex(42)[2:].zfill(64).lower()}'
コード例 #4
0
def contract_compile(contract_file):
    try:
        with open(contract_file) as fp:
            c = fp.read()
            print(c)
            contract = Contract(fp.read(),
                                Contract.SOPHIA,
                                client=_epoch_cli())
            result = contract.compile('')
            _pp([("contract", result)])
    except Exception as e:
        print(e)
コード例 #5
0
def contract_deploy(contract_file, gas):
    try:
        with open(contract_file) as fp:
            contract = Contract(fp.read(),
                                Contract.SOPHIA,
                                client=_epoch_cli())
            kp, _ = _keypair()
            address, tx = contract.tx_create(kp, gas=gas)
            _pp([
                ("Contract address", address),
                ("Transaction hash", tx.tx_hash),
            ])
    except Exception as e:
        print(e)
コード例 #6
0
    def __init__(self, **kwargs):
        """
        Initialize a ContractNative object

        :param client: an instance of NodeClient
        :param source: the source code of the contract
        :param compiler: an instance of the CompilerClient
        :param address: address of the currently deployed contract (optional)
        :param gas: Gas to be used for all contract interactions (optional)
        :param fee: fee to be used for all contract interactions (optional)
        :param gas_price: Gas price to be used for all contract interactions (optional)
        :param account: Account to be used for contract deploy and contract calls (optional)
        :param use_dry_run: use dry run for all method calls except for payable and stateful methods (default: True)
        """
        if 'client' in kwargs:
            self.contract = Contract(kwargs.get('client'))
        else:
            raise ValueError("client is not provided")
        self.compiler = kwargs.get('compiler', None)
        if self.compiler is None:
            raise ValueError("Compiler is not provided")
        else:
            if isinstance(self.compiler, str):
                self.compiler = compiler.CompilerClient(self.compiler)
        self.source = kwargs.get('source', None)
        if self.source is not None:
            self.bytecode = self.compiler.compile(self.source).bytecode
            self.aci = self.compiler.aci(self.source)
        else:
            raise ValueError("contract source not provided")

        self.contract_name = self.aci.encoded_aci.contract.name
        self.gas = kwargs.get('gas', defaults.CONTRACT_GAS)
        self.gas_price = kwargs.get('gas_price', defaults.CONTRACT_GAS_PRICE)
        self.fee = kwargs.get('fee', defaults.FEE)
        self.contract_amount = kwargs.get('amount', defaults.CONTRACT_AMOUNT)
        self.use_dry_run = kwargs.get('use_dry_run', True)

        address = kwargs.get('address', None)
        if address:
            self.at(address)

        self.account = kwargs.get('account', None)
        if self.account and type(self.account) is not signing.Account:
            raise TypeError(
                "Invalid account type. Use `class Account` for creating an account"
            )
        self.sophia_transformer = SophiaTransformation()
        self.__generate_methods()
コード例 #7
0
def contract_deploy(contract_file, gas):
    """
    Deploy a contract to the chain and create a deploy descriptor
    with the contract informations that can be use to invoke the contract
    later on.

    The generated descriptor will be created in the same folde of the contract
    source file. Multiple deploy of the same contract file will generate different
    deploy descriptor
    """
    try:
        with open(contract_file) as fp:
            code = fp.read()
            contract = Contract(code, client=_epoch_cli())
            kp, _ = _keypair()
            tx = contract.tx_create(kp, gas=gas)

            # save the contract data
            contract_data = {
                'source': contract.source_code,
                'bytecode': contract.bytecode,
                'address': contract.address,
                'transaction': tx.tx_hash,
                'owner': kp.get_address(),
                'created_at': datetime.now().isoformat('T')
            }
            # write the contract data to a file
            deploy_descriptor = f"{contract_file}.deploy.{contract.address[3:]}.json"
            with open(deploy_descriptor, 'w') as fw:
                json.dump(contract_data, fw, indent=2)
            _pp([
                ("Contract address", contract.address),
                ("Transaction hash", tx.tx_hash),
                ("Deploy descriptor", deploy_descriptor),
            ])
    except Exception as e:
        print(e)
コード例 #8
0
def test_evm_broken_contract_call():
    contract = Contract(broken_contract, Contract.EVM)
    with raises(AException):
        result = contract.call('IdentityBroken.main', '1')
        print(result)
コード例 #9
0
def test_evm_broken_contract_compile():
    contract = Contract(broken_contract, Contract.EVM)
    with raises(AException):
        result = contract.compile('')
コード例 #10
0
def test_evm_encode_calldata():
    contract = Contract(aer_identity_contract, Contract.EVM)
    result = contract.encode_calldata('main', '1')
    assert result is not None
    assert result == 'main1'
コード例 #11
0
def test_evm_contract_call():
    contract = Contract(aer_identity_contract, Contract.EVM)
    result = contract.call('main', '1')
    assert result is not None
    assert result.get('out')
コード例 #12
0
def test_evm_contract_compile():
    contract = Contract(aer_identity_contract, Contract.EVM)
    result = contract.compile()
    assert result is not None
    assert result.startswith('0x')
コード例 #13
0
def test_sophia_contract_compile():
    contract = Contract(aer_identity_contract, Contract.SOPHIA)
    result = contract.compile('')
    assert result is not None
    assert result.startswith('0x')
コード例 #14
0
def test_sophia_broken_encode_calldata():
    contract = Contract(broken_contract, Contract.SOPHIA)
    with raises(ContractError):
        result = contract.encode_calldata('IdentityBroken.main', '1')
        print(result)
コード例 #15
0
class ContractNative(object):
    def __init__(self, **kwargs):
        """
        Initialize a ContractNative object

        :param client: an instance of NodeClient
        :param source: the source code of the contract
        :param compiler: an instance of the CompilerClient
        :param address: address of the currently deployed contract (optional)
        :param gas: Gas to be used for all contract interactions (optional)
        :param fee: fee to be used for all contract interactions (optional)
        :param gas_price: Gas price to be used for all contract interactions (optional)
        :param account: Account to be used for contract deploy and contract calls (optional)
        :param use_dry_run: use dry run for all method calls except for payable and stateful methods (default: True)
        """
        if 'client' in kwargs:
            self.contract = Contract(kwargs.get('client'))
        else:
            raise ValueError("client is not provided")
        self.compiler = kwargs.get('compiler', None)
        if self.compiler is None:
            raise ValueError("Compiler is not provided")
        else:
            if isinstance(self.compiler, str):
                self.compiler = compiler.CompilerClient(self.compiler)
        self.source = kwargs.get('source', None)
        if self.source is not None:
            self.bytecode = self.compiler.compile(self.source).bytecode
            self.aci = self.compiler.aci(self.source)
        else:
            raise ValueError("contract source not provided")

        self.contract_name = self.aci.encoded_aci.contract.name
        self.gas = kwargs.get('gas', defaults.CONTRACT_GAS)
        self.gas_price = kwargs.get('gas_price', defaults.CONTRACT_GAS_PRICE)
        self.fee = kwargs.get('fee', defaults.FEE)
        self.contract_amount = kwargs.get('amount', defaults.CONTRACT_AMOUNT)
        self.use_dry_run = kwargs.get('use_dry_run', True)

        address = kwargs.get('address', None)
        if address:
            self.at(address)

        self.account = kwargs.get('account', None)
        if self.account and type(self.account) is not signing.Account:
            raise TypeError(
                "Invalid account type. Use `class Account` for creating an account"
            )
        self.sophia_transformer = SophiaTransformation()
        self.__generate_methods()

    def __generate_methods(self):
        if self.aci:
            for f in self.aci.encoded_aci.contract.functions:
                self.__add_contract_method(
                    namedtupled.map(
                        {
                            "name": f.name,
                            "doc": f"Contract Method {f.name}",
                            "arguments": f.arguments,
                            "returns": f.returns,
                            "stateful": f.stateful,
                            "payable": f.payable
                        },
                        _nt_name="ContractMethod"))

    def __encode_method_args(self, method, *args):
        if len(args) != len(method.arguments):
            raise ValueError(
                f"Invalid number of arguments. Expected {len(method.arguments)}, Provided {len(args)}"
            )
        transformed_args = []
        for i, val in enumerate(args):
            transformed_args.append(
                self.sophia_transformer.convert_to_sophia(
                    val, namedtupled.reduce(method.arguments[i].type),
                    self.aci.encoded_aci))
        return self.compiler.encode_calldata(self.source, method.name,
                                             *transformed_args).calldata

    def __decode_method_args(self, method, args):
        return self.sophia_transformer.convert_to_py(
            namedtupled.reduce(args), namedtupled.reduce(method.returns),
            self.aci.encoded_aci)

    def __add_contract_method(self, method):
        def contract_method(*args, **kwargs):
            calldata = self.__encode_method_args(method, *args)
            use_dry_run = kwargs.get('use_dry_run', self.use_dry_run)
            call_info = None
            if method.stateful or method.payable or not use_dry_run:
                tx_hash = self.call(method.name, calldata, **kwargs).hash
                call_info = namedtupled.reduce(
                    self.contract.get_call_object(tx_hash))
                call_info['tx_hash'] = tx_hash
                call_info = namedtupled.map(call_info)
            else:
                call_info = self.call_static(method.name, calldata, **kwargs)
                if call_info.result == 'error':
                    raise ValueError(call_info.reason)
                call_info = call_info.call_obj
            decoded_call_result = self.compiler.decode_call_result(
                self.source, method.name, call_info.return_value,
                call_info.return_type)
            return call_info, self.__decode_method_args(
                method, decoded_call_result)

        contract_method.__name__ = method.name
        contract_method.__doc__ = method.doc
        setattr(self, contract_method.__name__, contract_method)

    def __process_options(self, **kwargs):
        gas = self.gas if kwargs.get('gas') is None else kwargs.get('gas')
        gas_price = self.gas_price if kwargs.get(
            'gas_price') is None else kwargs.get('gas_price')
        amount = self.contract_amount if kwargs.get(
            'amount') is None else kwargs.get('amount')
        fee = self.fee if kwargs.get('fee') is None else kwargs.get('fee')
        account = self.account if kwargs.get(
            'account') is None else kwargs.get('account')
        if account is None:
            raise ValueError(
                "Please provide an account to sign contract call transactions. You can set a default account using 'set_account' method"
            )
        if account and type(account) is not signing.Account:
            raise TypeError(
                "Invalid account type. Use `class Account` for creating an account"
            )
        return namedtupled.map(
            {
                "gas": gas,
                "gas_price": gas_price,
                "amount": amount,
                "fee": fee,
                "account": account
            },
            _nt_name="ContractOptions")

    def at(self, address):
        """
        Set contract address
        """
        if not address or not utils.is_valid_hash(
                address, prefix=identifiers.CONTRACT_ID):
            raise ValueError(f"Invalid contract address {address}")
        if not self.contract.is_deployed(address):
            raise ValueError("Contract not deployed")
        self.address = address
        self.deployed = True

    def set_account(self, account):
        if account is None:
            raise ValueError("Account can not be of None type")
        if type(account) is not signing.Account:
            raise TypeError(
                "Invalid account type. Use `class Account` for creating an account"
            )
        self.account = account

    def deploy(self,
               *arguments,
               entrypoint="init",
               deposit=defaults.CONTRACT_DEPOSIT,
               vm_version=None,
               abi_version=None,
               tx_ttl=defaults.TX_TTL,
               **kwargs):
        """
        Create a contract and deploy it to the chain
        :return: the transaction
        """
        method_list = list(
            filter(lambda f: f.name == entrypoint,
                   self.aci.encoded_aci.contract.functions))
        calldata = None
        if len(method_list) == 1 and method_list[0].name == entrypoint:
            calldata = self.__encode_method_args(method_list[0], *arguments)
        else:
            calldata = self.compiler.encode_calldata(self.source, entrypoint,
                                                     *arguments).calldata
        opts = self.__process_options(**kwargs)
        tx = self.contract.create(opts.account, self.bytecode, calldata,
                                  opts.amount, deposit, opts.gas,
                                  opts.gas_price, opts.fee, vm_version,
                                  abi_version, tx_ttl)
        self.at(tx.metadata.contract_id)
        return tx

    def call(self,
             function,
             calldata,
             abi_version=None,
             tx_ttl=defaults.TX_TTL,
             **kwargs):
        """
        call a contract method
        :return: the transaction
        """
        opts = self.__process_options(**kwargs)
        return self.contract.call(self.address, opts.account, function,
                                  calldata, opts.amount, opts.gas,
                                  opts.gas_price, opts.fee, abi_version,
                                  tx_ttl)

    def call_static(self,
                    function,
                    calldata,
                    abi_version=None,
                    tx_ttl=defaults.TX_TTL,
                    top=None,
                    **kwargs):
        """
        call-static a contract method
        :return: the call object
        """
        opts = self.__process_options(**kwargs)
        return self.contract.call_static(self.address, function, calldata,
                                         opts.account.get_address(),
                                         opts.amount, opts.gas, opts.gas_price,
                                         opts.fee, abi_version, tx_ttl, top)
コード例 #16
0
def test_sophia_broken_contract_compile():
    contract = Contract(broken_contract, Contract.SOPHIA)
    with raises(ContractError):
        result = contract.compile('')
        print(result)
コード例 #17
0
def test_sophia_encode_calldata():
    contract = Contract(aer_identity_contract, Contract.SOPHIA)
    result = contract.encode_calldata('main', '1')
    assert result is not None
    assert result == 'main1'
コード例 #18
0
def test_sophia_contract_call():
    contract = Contract(aer_identity_contract, Contract.SOPHIA)
    result = contract.call('main', '1')
    assert result is not None
    assert result.out
コード例 #19
0
def test_evm_broken_encode_calldata():
    contract = Contract(broken_contract, Contract.EVM)
    #with raises(AException):
    result = contract.encode_calldata('IdentityBroken.main', '1')
コード例 #20
0
def test_ring_broken_contract_compile():
    contract = Contract(broken_contract, Contract.RING)
    with raises(AException):
        result = contract.compile('')
コード例 #21
0
def test_evm_broken_contract_compile():
    contract = Contract(broken_contract, Contract.EVM)
    with raises(ContractError):
        result = contract.compile('')
        print(result)
コード例 #22
0
def test_ring_broken_contract_call():
    contract = Contract(broken_contract, Contract.RING)
    with raises(AException):
        result = contract.call('IdentityBroken.main', '1')
コード例 #23
0
def test_ring_contract_compile():
    contract = Contract(aer_identity_contract, Contract.RING)
    result = contract.compile('')
    assert result is not None
    assert result.startswith('0x')
コード例 #24
0
def test_sophia_contract_tx_create():
    contract = Contract(aer_identity_contract, Contract.SOPHIA)
    address, tx = contract.tx_create(KEYPAIR, gas=1000)
    assert address is not None
    assert len(address) > 0