Beispiel #1
0
 def unlockable_to_aergo(
     self,
     from_chain: str,
     to_chain: str,
     asset_name: str,
     receiver: str,
 ) -> Tuple[int, int]:
     """Check unlockable balance on Aergo."""
     token_origin = self.get_asset_address(asset_name, to_chain)
     bridge_from = self.get_bridge_contract_address(from_chain, to_chain)
     bridge_to = self.get_bridge_contract_address(to_chain, from_chain)
     if not is_aergo_address(token_origin):
         raise InvalidArgumentsError(
             "token_origin {} must be an Aergo address".format(
                 token_origin))
     if not is_aergo_address(receiver):
         raise InvalidArgumentsError(
             "Receiver {} must be an Aergo address".format(receiver))
     hera = self.connect_aergo(to_chain)
     w3 = self.get_web3(from_chain)
     account_ref = receiver + token_origin
     position = b'\x07'  # Burns
     eth_trie_key = keccak(
         account_ref.encode('utf-8') + position.rjust(32, b'\0'))
     aergo_storage_key = ('_sv__unlocks-' + account_ref).encode('utf-8')
     return eth_to_aergo.withdrawable(bridge_from, bridge_to, w3, hera,
                                      eth_trie_key, aergo_storage_key)
Beispiel #2
0
def unlock(aergo_to: herapy.Aergo, receiver: str, burn_proof: AttributeDict,
           token_origin: str, bridge_to: str, gas_limit: int,
           gas_price: int) -> Tuple[str, Transaction]:
    """ Unlock the receiver's deposit balance on aergo_to. """
    if not is_aergo_address(receiver):
        raise InvalidArgumentsError(
            "Receiver {} must be an Aergo address".format(receiver))
    if not is_aergo_address(token_origin):
        raise InvalidArgumentsError(
            "token_origin {} must be an Aergo address".format(token_origin))
    ap = format_proof_for_lua(burn_proof.storageProof[0].proof)
    balance = int.from_bytes(burn_proof.storageProof[0].value, "big")
    ubig_balance = {'_bignum': str(balance)}
    # call unlock on aergo_to with the burn proof from aergo_from
    tx, result = aergo_to.call_sc(
        bridge_to,
        "unlock",
        args=[receiver, ubig_balance, token_origin, ap],
        gas_limit=gas_limit,
        gas_price=gas_price)
    if result.status != herapy.CommitStatus.TX_OK:
        raise TxError("Unlock asset Tx commit failed : {}".format(result))

    result = aergo_to.wait_tx_result(tx.tx_hash)
    if result.status != herapy.TxResultStatus.SUCCESS:
        raise TxError("Unlock asset Tx execution failed : {}".format(result))
    logger.info("\u26fd Aergo gas used: %s", result.gas_used)
    return str(tx.tx_hash), result
Beispiel #3
0
def burn(
    aergo_from: herapy.Aergo,
    bridge_from: str,
    receiver: str,
    value: int,
    token_pegged: str,
    gas_limit: int,
    gas_price: int,
) -> Tuple[int, str, Transaction]:
    """ Burn a minted token on a sidechain. """
    if not is_ethereum_address(receiver):
        raise InvalidArgumentsError(
            "Receiver {} must be an Ethereum address".format(receiver))
    if not is_aergo_address(token_pegged):
        raise InvalidArgumentsError(
            "token_pegged {} must be an Aergo address".format(token_pegged))
    args = (receiver[2:].lower(), {"_bignum": str(value)}, token_pegged)
    tx, result = aergo_from.call_sc(bridge_from,
                                    "burn",
                                    args=args,
                                    gas_limit=gas_limit,
                                    gas_price=gas_price)

    if result.status != herapy.CommitStatus.TX_OK:
        raise TxError("Burn asset Tx commit failed : {}".format(result))

    # Check burn success
    result = aergo_from.wait_tx_result(tx.tx_hash)
    if result.status != herapy.TxResultStatus.SUCCESS:
        raise TxError("Burn asset Tx execution failed : {}".format(result))
    logger.info("\u26fd Aergo gas used: %s", result.gas_used)
    # get precise burn height
    tx_detail = aergo_from.get_tx(tx.tx_hash)
    burn_height = tx_detail.block.height
    return burn_height, str(tx.tx_hash), tx_detail
Beispiel #4
0
def build_lock_proof(
    aergo_from: herapy.Aergo,
    w3: Web3,
    receiver: str,
    bridge_from: str,
    bridge_to: str,
    bridge_to_abi: str,
    lock_height: int,
    token_origin: str,
):
    """ Check the last anchored root includes the lock and build
    a lock proof for that root
    """
    if not is_ethereum_address(receiver):
        raise InvalidArgumentsError(
            "Receiver {} must be an ethereum address".format(receiver)
        )
    if not is_aergo_address(token_origin):
        raise InvalidArgumentsError(
            "token_origin {} must be an Aergo address".format(token_origin)
        )
    trie_key = "_sv__locks-".encode('utf-8') + bytes.fromhex(receiver[2:]) \
        + token_origin.encode('utf-8')
    return _build_deposit_proof(
        aergo_from, w3, bridge_from, bridge_to, bridge_to_abi, lock_height,
        trie_key
    )
Beispiel #5
0
 def mintable_to_eth(
     self,
     from_chain: str,
     to_chain: str,
     asset_name: str,
     receiver: str,
 ) -> Tuple[int, int]:
     """Check mintable balance on Ethereum."""
     token_origin = self.get_asset_address(asset_name, from_chain)
     bridge_from = self.get_bridge_contract_address(from_chain, to_chain)
     bridge_to = self.get_bridge_contract_address(to_chain, from_chain)
     if not is_aergo_address(token_origin):
         raise InvalidArgumentsError(
             "token_origin {} must be an Aergo address".format(
                 token_origin))
     if not is_ethereum_address(receiver):
         raise InvalidArgumentsError(
             "Receiver {} must be an Ethereum address".format(receiver))
     hera = self.connect_aergo(from_chain)
     w3 = self.get_web3(to_chain)
     account_ref_eth = \
         bytes.fromhex(receiver[2:]) + token_origin.encode('utf-8')
     position = b'\x08'  # Mints
     eth_trie_key = keccak(account_ref_eth + position.rjust(32, b'\0'))
     aergo_storage_key = '_sv__locks-'.encode('utf-8') \
         + bytes.fromhex(receiver[2:]) + token_origin.encode('utf-8')
     return aergo_to_eth.withdrawable(bridge_from, bridge_to, hera, w3,
                                      aergo_storage_key, eth_trie_key)
Beispiel #6
0
 def unfreezable(
     self,
     from_chain: str,
     to_chain: str,
     receiver: str,
 ) -> Tuple[int, int]:
     """Check unfreezable balance on Aergo."""
     token_origin = self.get_asset_address('aergo_erc20', from_chain)
     bridge_from = self.get_bridge_contract_address(from_chain, to_chain)
     bridge_to = self.get_bridge_contract_address(to_chain, from_chain)
     if not is_ethereum_address(token_origin):
         raise InvalidArgumentsError(
             "token_origin {} must be an Ethereum address".format(
                 token_origin))
     if not is_aergo_address(receiver):
         raise InvalidArgumentsError(
             "Receiver {} must be an Aergo address".format(receiver))
     hera = self.connect_aergo(to_chain)
     w3 = self.get_web3(from_chain)
     account_ref_eth = \
         receiver.encode('utf-8') + bytes.fromhex(token_origin[2:])
     position = b'\x05'  # Locks
     eth_trie_key = keccak(account_ref_eth + position.rjust(32, b'\0'))
     aergo_storage_key = ('_sv__unfreezes-' + receiver).encode('utf-8') \
         + bytes.fromhex(token_origin[2:])
     return eth_to_aergo.withdrawable(bridge_from, bridge_to, w3, hera,
                                      eth_trie_key, aergo_storage_key)
Beispiel #7
0
def burn(w3: Web3, signer_acct, receiver: str, amount: int, bridge_from: str,
         bridge_from_abi: str, token_pegged: str, gas_limit: int,
         gas_price: int) -> Tuple[int, str, AttributeDict]:
    """ Burn a token that was minted on ethereum. """
    if not is_aergo_address(receiver):
        raise InvalidArgumentsError(
            "Receiver {} must be an Aergo address".format(receiver))
    if not is_ethereum_address(token_pegged):
        raise InvalidArgumentsError(
            "token_pegged {} must be an Ethereum address".format(token_pegged))
    bridge_from = Web3.toChecksumAddress(bridge_from)
    eth_bridge = w3.eth.contract(address=bridge_from, abi=bridge_from_abi)
    construct_txn = eth_bridge.functions.burn(
        receiver, amount, token_pegged).buildTransaction({
            'chainId':
            w3.eth.chainId,
            'from':
            signer_acct.address,
            'nonce':
            w3.eth.getTransactionCount(signer_acct.address),
            'gas':
            gas_limit,
            'gasPrice':
            w3.toWei(gas_price, 'gwei')
        })
    signed = signer_acct.sign_transaction(construct_txn)
    tx_hash = w3.eth.sendRawTransaction(signed.rawTransaction)
    receipt = w3.eth.waitForTransactionReceipt(tx_hash)
    if receipt.status != 1:
        raise TxError("Burn asset Tx execution failed: {}".format(receipt))
    logger.info("\u26fd Eth Gas used: %s", receipt.gasUsed)
    return receipt.blockNumber, tx_hash.hex(), receipt
Beispiel #8
0
    def RequestUnfreeze(self, account_ref, context):
        """
            Create and broadcast unfreeze transactions if conditions are met:
            - the receiver is a valid aergo address
            - the unfreezable amount covers the unfreeze fee
        """
        if not is_aergo_address(account_ref.receiver):
            logger.warning("\"Invalid receiver address %s\"",
                           account_ref.receiver)
            return Status(error="Receiver must be an Aergo address")

        # format account references for Locks and Unfreezes
        account_ref_eth = \
            account_ref.receiver.encode('utf-8') + self.aergo_erc20_bytes
        position = b'\x05'  # Locks
        eth_trie_key = keccak(account_ref_eth + position.rjust(32, b'\0'))
        aergo_storage_key = \
            ('_sv__unfreezes-' + account_ref.receiver).encode('utf-8') \
            + self.aergo_erc20_bytes
        # check unfreezeable is larger that the fee
        unfreezeable, _ = withdrawable(self.bridge_eth, self.bridge_aergo,
                                       self.web3, self.hera, eth_trie_key,
                                       aergo_storage_key)
        if unfreezeable <= self.unfreeze_fee:
            logger.warning(
                "\"Unfreezable (%s aer) doesn't cover fee for: %s\"",
                unfreezeable, account_ref.receiver)
            return Status(
                error="Aergo native to unfreeze doesnt cover the fee")

        # build lock proof and arguments for unfreeze
        lock_proof = _build_deposit_proof(self.web3, self.hera,
                                          self.bridge_eth, self.bridge_aergo,
                                          0, eth_trie_key)
        ap = format_proof_for_lua(lock_proof.storageProof[0].proof)
        balance = int.from_bytes(lock_proof.storageProof[0].value, "big")
        ubig_balance = {'_bignum': str(balance)}

        # call unfreeze tx
        gas_limit = 300000
        gas_price = 0
        tx, result = self.hera.call_sc(
            self.bridge_aergo,
            "unfreeze",
            args=[account_ref.receiver, ubig_balance, ap],
            gas_limit=gas_limit,
            gas_price=gas_price)
        if result.status != herapy.CommitStatus.TX_OK:
            logger.warning("\"Error: tx failed: %s\"", result.json())
            return Status(error="Unfreeze service error: tx failed")

        # all went well
        logger.info("\"Unfreeze success for: %s\"", account_ref.receiver)

        # Test unfreeze fee used
        # result = self.hera.wait_tx_result(tx.tx_hash)
        # logger.info("\"\u26fd Unfreeze tx fee paid: %s\"", result.fee_used)

        return Status(txHash=str(tx.tx_hash))
Beispiel #9
0
    def unfreeze(
        self,
        from_chain: str,
        to_chain: str,
        receiver: str = None,
        lock_height: int = 0,
        privkey_name: str = 'default',
        privkey_pwd: str = None,
    ) -> str:
        """ Finalize ERC20Aergo transfer to Aergo Mainnet by unfreezing
            (aers are already minted and freezed in the bridge contract)
        """
        logger.info(from_chain + ' -> ' + to_chain)
        asset_name = 'aergo_erc20'
        w3 = self.get_web3(from_chain)
        aergo_to = self.get_aergo(to_chain, privkey_name, privkey_pwd)
        tx_sender = str(aergo_to.account.address)
        bridge_to = self.get_bridge_contract_address(to_chain, from_chain)
        bridge_from = self.get_bridge_contract_address(from_chain, to_chain)
        asset_address = self.get_asset_address(asset_name, from_chain)
        if receiver is None:
            receiver = tx_sender
        if not is_aergo_address(receiver):
            raise InvalidArgumentsError(
                "Receiver {} must be an Aergo address".format(receiver))

        balance = aergo_u.get_balance(receiver, 'aergo', aergo_to)
        logger.info("\U0001f4b0 %s balance on destination before transfer: %s",
                    asset_name, balance / 10**18)

        gas_limit = 300000
        if receiver != tx_sender:
            aer_balance = aergo_u.get_balance(tx_sender, 'aergo', aergo_to)
            if aer_balance < gas_limit * self.aergo_gas_price:
                err = "not enough aer balance to pay tx fee"
                raise InsufficientBalanceError(err)

        lock_proof = eth_to_aergo.build_lock_proof(w3, aergo_to, receiver,
                                                   bridge_from, bridge_to,
                                                   lock_height, asset_address)
        logger.info("\u2699 Built lock proof")

        tx_hash, _ = eth_to_aergo.unfreeze(aergo_to, receiver, lock_proof,
                                           bridge_to, gas_limit,
                                           self.aergo_gas_price)
        logger.info('\U0001f4a7 Unfreeze success: %s', tx_hash)
        # new balance on destination
        balance = aergo_u.get_balance(receiver, 'aergo', aergo_to)
        logger.info("\U0001f4b0 %s balance on destination after transfer: %s",
                    asset_name, balance / 10**18)
        aergo_to.disconnect()

        # record mint address in file
        return tx_hash
Beispiel #10
0
    def lock_to_aergo(
        self,
        from_chain: str,
        to_chain: str,
        asset_name: str,
        amount: int,
        receiver: str,
        privkey_name: str = 'default',
        privkey_pwd: str = None,
    ) -> Tuple[int, str]:
        """ Initiate ERC20 token or Ether transfer to Aergo sidechain """
        logger.info(from_chain + ' -> ' + to_chain)
        if not is_aergo_address(receiver):
            raise InvalidArgumentsError(
                "Receiver {} must be an Aergo address".format(receiver))
        bridge_from_abi = self.load_bridge_abi(from_chain, to_chain)
        erc20_abi = self.load_erc20_abi(from_chain, asset_name)
        w3 = self.get_web3(from_chain)
        signer_acct = self.get_signer(w3, privkey_name, privkey_pwd)
        token_owner = signer_acct.address
        bridge_from = self.get_bridge_contract_address(from_chain, to_chain)
        erc20_address = self.get_asset_address(asset_name, from_chain)

        balance = eth_u.get_balance(token_owner, erc20_address, w3, erc20_abi)
        logger.info("\U0001f4b0 %s balance on origin before transfer : %s",
                    asset_name, balance / 10**18)
        if balance < amount:
            raise InsufficientBalanceError("not enough token balance")

        gas_limit = 500000  # estimation
        eth_balance = eth_u.get_balance(token_owner, 'ether', w3)
        if eth_balance * 10**9 < gas_limit * self.eth_gas_price:
            err = "not enough eth balance to pay tx fee"
            raise InsufficientBalanceError(err)

        next_nonce, tx_hash = eth_u.increase_approval(bridge_from,
                                                      erc20_address, amount,
                                                      w3, erc20_abi,
                                                      signer_acct, gas_limit,
                                                      self.eth_gas_price)
        logger.info("\u2b06 Increase approval success: %s", tx_hash)

        lock_height, tx_hash, _ = eth_to_aergo.lock(
            w3, signer_acct, receiver, amount, bridge_from, bridge_from_abi,
            erc20_address, gas_limit, self.eth_gas_price, next_nonce)
        logger.info('\U0001f512 Lock success: %s', tx_hash)

        balance = eth_u.get_balance(token_owner, erc20_address, w3, erc20_abi)
        logger.info(
            "\U0001f4b0 remaining %s balance on origin after transfer: %s",
            asset_name, balance / 10**18)
        return lock_height, tx_hash
Beispiel #11
0
def mint(
    w3: Web3,
    signer_acct,
    receiver: str,
    lock_proof: herapy.obj.sc_state.SCState,
    token_origin: str,
    bridge_to: str,
    bridge_to_abi: str,
    gas_limit: int,
    gas_price: int
) -> Tuple[str, str, AttributeDict]:
    """ Mint the receiver's deposit balance on aergo_to. """
    if not is_ethereum_address(receiver):
        raise InvalidArgumentsError(
            "Receiver {} must be an Ethereum address".format(receiver)
        )
    if not is_aergo_address(token_origin):
        raise InvalidArgumentsError(
            "token_origin {} must be an Aergo address".format(token_origin)
        )
    receiver = Web3.toChecksumAddress(receiver)
    balance = int(lock_proof.var_proofs[0].value.decode('utf-8')[1:-1])
    ap = lock_proof.var_proofs[0].auditPath
    bitmap = lock_proof.var_proofs[0].bitmap
    leaf_height = lock_proof.var_proofs[0].height
    # call mint on ethereum with the lock proof from aergo_from
    eth_bridge = w3.eth.contract(
        address=bridge_to,
        abi=bridge_to_abi
    )
    construct_txn = eth_bridge.functions.mint(
        receiver, balance, token_origin, ap, bitmap, leaf_height
    ).buildTransaction({
        'chainId': w3.eth.chainId,
        'from': signer_acct.address,
        'nonce': w3.eth.getTransactionCount(
            signer_acct.address
        ),
        'gas': gas_limit,
        'gasPrice': w3.toWei(gas_price, 'gwei')
    })
    signed = signer_acct.sign_transaction(construct_txn)
    tx_hash = w3.eth.sendRawTransaction(signed.rawTransaction)
    receipt = w3.eth.waitForTransactionReceipt(tx_hash)
    if receipt.status != 1:
        raise TxError("Mint asset Tx execution failed: {}".format(receipt))
    logger.info("\u26fd Eth Gas used: %s", receipt.gasUsed)
    events = eth_bridge.events.mintEvent().processReceipt(receipt)
    token_pegged = events[0]['args']['tokenAddress']

    return token_pegged, tx_hash.hex(), receipt
Beispiel #12
0
    def burn_to_aergo(
        self,
        from_chain: str,
        to_chain: str,
        asset_name: str,
        amount: int,
        receiver: str,
        privkey_name: str = 'default',
        privkey_pwd: str = None,
    ) -> Tuple[int, str]:
        """ Initiate minted Standard token transfer back to aergo origin"""
        logger.info(from_chain + ' -> ' + to_chain)
        if not is_aergo_address(receiver):
            raise InvalidArgumentsError(
                "Receiver {} must be an Aergo address".format(receiver))
        bridge_from_abi = self.load_bridge_abi(from_chain, to_chain)
        minted_erc20_abi = self.load_minted_erc20_abi(from_chain, to_chain)
        w3 = self.get_web3(from_chain)
        signer_acct = self.get_signer(w3, privkey_name, privkey_pwd)
        token_owner = signer_acct.address
        bridge_from = self.get_bridge_contract_address(from_chain, to_chain)
        token_pegged = self.get_asset_address(asset_name,
                                              from_chain,
                                              asset_origin_chain=to_chain)

        balance = eth_u.get_balance(token_owner, token_pegged, w3,
                                    minted_erc20_abi)
        logger.info("\U0001f4b0 %s balance on origin before transfer : %s",
                    asset_name, balance / 10**18)
        if balance < amount:
            raise InsufficientBalanceError("not enough token balance")

        gas_limit = 200000
        eth_balance = eth_u.get_balance(token_owner, 'ether', w3)
        if eth_balance * 10**9 < gas_limit * self.eth_gas_price:
            err = "not enough aer balance to pay tx fee"
            raise InsufficientBalanceError(err)

        burn_height, tx_hash, _ = eth_to_aergo.burn(w3, signer_acct, receiver,
                                                    amount, bridge_from,
                                                    bridge_from_abi,
                                                    token_pegged, gas_limit,
                                                    self.eth_gas_price)
        logger.info('\U0001f525 Burn success: %s', tx_hash)

        balance = eth_u.get_balance(token_owner, token_pegged, w3,
                                    minted_erc20_abi)
        logger.info(
            "\U0001f4b0 remaining %s balance on origin after transfer: %s",
            asset_name, balance / 10**18)
        return burn_height, tx_hash
Beispiel #13
0
def build_burn_proof(
    w3: Web3,
    aergo_to: herapy.Aergo,
    receiver: str,
    bridge_from: str,
    bridge_to: str,
    burn_height: int,
    token_origin: str,
):
    """ Check the last anchored root includes the lock and build
    a lock proof for that root
    """
    if not is_aergo_address(receiver):
        raise InvalidArgumentsError(
            "Receiver {} must be an Aergo address".format(receiver))
    if not is_aergo_address(token_origin):
        raise InvalidArgumentsError(
            "token_origin {} must be an Aergo address".format(token_origin))
    account_ref = (receiver + token_origin).encode('utf-8')
    # 'Burns is the 8th state var defined in solitity contract
    position = b'\x07'
    trie_key = keccak(account_ref + position.rjust(32, b'\0'))
    return _build_deposit_proof(w3, aergo_to, bridge_from, bridge_to,
                                burn_height, trie_key)
Beispiel #14
0
    def unlock_to_aergo(
        self,
        from_chain: str,
        to_chain: str,
        asset_name: str,
        receiver: str,
        burn_height: int = 0,
        privkey_name: str = 'default',
        privkey_pwd: str = None,
    ) -> str:
        """ Finalize Aergo Standard token transfer back to Aergo Origin"""
        logger.info(from_chain + ' -> ' + to_chain)
        if not is_aergo_address(receiver):
            raise InvalidArgumentsError(
                "Receiver {} must be an Aergo address".format(receiver))
        w3 = self.get_web3(from_chain)
        aergo_to = self.get_aergo(to_chain, privkey_name, privkey_pwd)
        tx_sender = str(aergo_to.account.address)
        bridge_to = self.get_bridge_contract_address(to_chain, from_chain)
        bridge_from = self.get_bridge_contract_address(from_chain, to_chain)
        asset_address = self.get_asset_address(asset_name, to_chain)

        burn_proof = eth_to_aergo.build_burn_proof(w3, aergo_to, receiver,
                                                   bridge_from, bridge_to,
                                                   burn_height, asset_address)
        logger.info("\u2699 Built burn proof")

        balance = aergo_u.get_balance(receiver, asset_address, aergo_to)
        logger.info("\U0001f4b0 %s balance on destination before transfer: %s",
                    asset_name, balance / 10**18)

        gas_limit = 300000
        aer_balance = aergo_u.get_balance(tx_sender, 'aergo', aergo_to)
        if aer_balance < gas_limit * self.aergo_gas_price:
            err = "not enough aer balance to pay tx fee"
            raise InsufficientBalanceError(err)

        tx_hash, _ = eth_to_aergo.unlock(aergo_to, receiver, burn_proof,
                                         asset_address, bridge_to, gas_limit,
                                         self.aergo_gas_price)
        logger.info('\U0001f513 Unlock success: %s', tx_hash)

        # new balance on origin
        balance = aergo_u.get_balance(receiver, asset_address, aergo_to)
        logger.info("\U0001f4b0 %s balance on destination after transfer: %s",
                    asset_name, balance / 10**18)

        return tx_hash
Beispiel #15
0
 def get_balance_aergo(self,
                       asset_name: str,
                       network_name: str,
                       asset_origin_chain: str = None,
                       account_name: str = 'default',
                       account_addr: str = None) -> Tuple[int, str]:
     """ Get account name balance of asset_name on network_name,
     and specify asset_origin_chain for a pegged asset query,
     """
     if account_addr is None:
         account_addr = self.config_data('wallet', account_name, 'addr')
     if not is_aergo_address(account_addr):
         raise InvalidArgumentsError(
             "Account {} must be an Aergo address".format(account_addr))
     aergo = self.connect_aergo(network_name)
     asset_addr = self.get_asset_address(asset_name, network_name,
                                         asset_origin_chain)
     balance = aergo_u.get_balance(account_addr, asset_addr, aergo)
     aergo.disconnect()
     return balance, asset_addr
Beispiel #16
0
def lock(
    aergo_from: herapy.Aergo,
    bridge_from: str,
    receiver: str,
    value: int,
    asset: str,
    gas_limit: int,
    gas_price: int,
) -> Tuple[int, str, Transaction]:
    """ Lock can be called to lock aer or tokens.
        it supports delegated transfers when tx broadcaster is not
        the same as the token owner
    """
    if not is_ethereum_address(receiver):
        raise InvalidArgumentsError(
            "receiver {} must be an Ethereum address".format(receiver))
    if not is_aergo_address(asset):
        raise InvalidArgumentsError(
            "asset {} must be an Aergo address".format(asset))
    args = (bridge_from, {"_bignum": str(value)}, receiver[2:].lower())
    tx, result = aergo_from.call_sc(asset,
                                    "transfer",
                                    args=args,
                                    amount=0,
                                    gas_limit=gas_limit,
                                    gas_price=gas_price)
    if result.status != herapy.CommitStatus.TX_OK:
        raise TxError("Lock asset Tx commit failed : {}".format(result))

    # Check lock success
    result = aergo_from.wait_tx_result(tx.tx_hash)
    if result.status != herapy.TxResultStatus.SUCCESS:
        raise TxError("Lock asset Tx execution failed : {}".format(result))
    logger.info("\u26fd Aergo gas used: %s", result.gas_used)
    # get precise lock height
    tx_detail = aergo_from.get_tx(tx.tx_hash)
    lock_height = tx_detail.block.height

    return lock_height, str(tx.tx_hash), tx_detail
Beispiel #17
0
    def mint_to_aergo(
        self,
        from_chain: str,
        to_chain: str,
        asset_name: str,
        receiver: str = None,
        lock_height: int = 0,
        privkey_name: str = 'default',
        privkey_pwd: str = None,
    ) -> str:
        """ Finalize ERC20 token or Ether transfer to Aergo sidechain """
        logger.info(from_chain + ' -> ' + to_chain)
        w3 = self.get_web3(from_chain)
        aergo_to = self.get_aergo(to_chain, privkey_name, privkey_pwd)
        tx_sender = str(aergo_to.account.address)
        bridge_to = self.get_bridge_contract_address(to_chain, from_chain)
        bridge_from = self.get_bridge_contract_address(from_chain, to_chain)
        asset_address = self.get_asset_address(asset_name, from_chain)
        if receiver is None:
            receiver = tx_sender
        if not is_aergo_address(receiver):
            raise InvalidArgumentsError(
                "Receiver {} must be an Aergo address".format(receiver))

        save_pegged_token_address = False
        try:
            token_pegged = self.get_asset_address(
                asset_name, to_chain, asset_origin_chain=from_chain)
            balance = aergo_u.get_balance(receiver, token_pegged, aergo_to)
            logger.info(
                "\U0001f4b0 %s balance on destination before transfer: %s",
                asset_name, balance / 10**18)
        except KeyError:
            logger.info("Pegged token unknow by wallet")
            save_pegged_token_address = True

        gas_limit = 300000
        aer_balance = aergo_u.get_balance(tx_sender, 'aergo', aergo_to)
        if aer_balance < gas_limit * self.aergo_gas_price:
            err = "not enough aer balance to pay tx fee"
            raise InsufficientBalanceError(err)

        lock_proof = eth_to_aergo.build_lock_proof(w3, aergo_to, receiver,
                                                   bridge_from, bridge_to,
                                                   lock_height, asset_address)
        logger.info("\u2699 Built lock proof")

        token_pegged, tx_hash, _ = eth_to_aergo.mint(aergo_to, receiver,
                                                     lock_proof, asset_address,
                                                     bridge_to, gas_limit,
                                                     self.aergo_gas_price)
        logger.info('\u26cf Mint success: %s', tx_hash)
        # new balance on destination
        balance = aergo_u.get_balance(receiver, token_pegged, aergo_to)
        logger.info("\U0001f4b0 %s balance on destination after transfer: %s",
                    asset_name, balance / 10**18)
        aergo_to.disconnect()

        # record mint address in file
        if save_pegged_token_address:
            logger.info("------ Store mint address in config.json -----------")
            self.config_data('networks',
                             from_chain,
                             'tokens',
                             asset_name,
                             'pegs',
                             to_chain,
                             value=token_pegged)
            self.save_config()
        return tx_hash