Esempio n. 1
0
 def unlockable_to_eth(
     self,
     from_chain: str,
     to_chain: str,
     asset_name: str,
     receiver: str,
 ) -> Tuple[int, int]:
     """Check unlockable balance on Ethereum."""
     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_ethereum_address(token_origin):
         raise InvalidArgumentsError(
             "token_origin {} must be an Ethereum 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 = receiver[2:] + token_origin[2:]
     position = b'\x06'  # Unlocks
     eth_trie_key = keccak(
         bytes.fromhex(account_ref) + position.rjust(32, b'\0'))
     aergo_storage_key = '_sv__burns-'.encode('utf-8') \
         + bytes.fromhex(account_ref)
     return aergo_to_eth.withdrawable(bridge_from, bridge_to, hera, w3,
                                      aergo_storage_key, eth_trie_key)
Esempio n. 2
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
    )
Esempio n. 3
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)
Esempio n. 4
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)
Esempio n. 5
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
Esempio n. 6
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
Esempio n. 7
0
def get_balance(
    account_addr: str,
    asset_addr: str,
    aergo: herapy.Aergo,
) -> int:
    """ Get an account or the default wallet balance of Aer
    or any token on a given network.
    """
    if not is_aergo_address(account_addr):
        raise InvalidArgumentsError(
            "Account {} must be an Aergo address".format(account_addr))
    balance = 0
    if asset_addr == "aergo":
        # query aergo bits on network_name
        ret_account = aergo.get_account(address=account_addr)
        balance = ret_account.balance
    else:
        balance_q = aergo.query_sc_state(asset_addr,
                                         ["_sv__balances-" + account_addr])
        if not balance_q.account.state_proof.inclusion:
            raise InvalidArgumentsError(
                "Contract doesnt exist in state, check contract deployed and "
                "chain synced {}".format(balance_q))
        if balance_q.var_proofs[0].inclusion:
            balance = json.loads(balance_q.var_proofs[0].value)['_bignum']
    return int(balance)
Esempio n. 8
0
def mint(aergo_to: herapy.Aergo, receiver: str, lock_proof: AttributeDict,
         token_origin: str, bridge_to: str, gas_limit: int,
         gas_price: int) -> Tuple[str, 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_ethereum_address(token_origin):
        raise InvalidArgumentsError(
            "token_origin {} must be an Ethereum address".format(token_origin))
    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 unlock on aergo_to with the burn proof from aergo_from
    tx, result = aergo_to.call_sc(
        bridge_to,
        "mint",
        args=[receiver, ubig_balance, token_origin[2:].lower(), ap],
        gas_limit=gas_limit,
        gas_price=gas_price)
    if result.status != herapy.CommitStatus.TX_OK:
        raise TxError("Mint asset Tx commit failed : {}".format(result))

    result = aergo_to.wait_tx_result(tx.tx_hash)
    if result.status != herapy.TxResultStatus.SUCCESS:
        raise TxError("Mint asset Tx execution failed : {}".format(result))
    logger.info("\u26fd Aergo gas used: %s", result.gas_used)
    token_pegged = json.loads(result.detail)[0]
    return token_pegged, str(tx.tx_hash), result
Esempio n. 9
0
def withdrawable(
    bridge_from: str,
    bridge_to: str,
    hera: herapy.Aergo,
    w3: Web3,
    aergo_storage_key: bytes,
    eth_trie_key: bytes,
) -> Tuple[int, int]:
    # total_deposit : total latest deposit including pending
    _, block_height = hera.get_blockchain_status()
    block_from = hera.get_block_headers(
        block_height=block_height, list_size=1)
    root_from = block_from[0].blocks_root_hash
    deposit_proof = hera.query_sc_state(
        bridge_from, [aergo_storage_key],
        root=root_from, compressed=False
    )
    if not deposit_proof.account.state_proof.inclusion:
        raise InvalidArgumentsError(
            "Contract doesnt exist in state, check contract deployed and "
            "chain synced {}".format(deposit_proof))
    total_deposit = 0
    if deposit_proof.var_proofs[0].inclusion:
        total_deposit = int(deposit_proof.var_proofs[0].value
                            .decode('utf-8')[1:-1])

    # get total withdrawn and last anchor height
    bridge_to = Web3.toChecksumAddress(bridge_to)
    storage_value = w3.eth.getStorageAt(bridge_to, eth_trie_key, 'latest')
    total_withdrawn = int.from_bytes(storage_value, "big")
    # Height is at position 1 in solidity contract
    storage_value = w3.eth.getStorageAt(bridge_to, 1, 'latest')
    last_anchor_height = int.from_bytes(storage_value, "big")

    # get anchored deposit : total deposit before the last anchor
    block_from = hera.get_block_headers(
        block_height=last_anchor_height, list_size=1)
    root_from = block_from[0].blocks_root_hash
    deposit_proof = hera.query_sc_state(
        bridge_from, [aergo_storage_key],
        root=root_from, compressed=False
    )
    if not deposit_proof.account.state_proof.inclusion:
        raise InvalidArgumentsError(
            "Contract doesnt exist in state, check contract deployed and "
            "chain synced {}".format(deposit_proof))
    anchored_deposit = 0
    if deposit_proof.var_proofs[0].inclusion:
        anchored_deposit = int(deposit_proof.var_proofs[0].value
                               .decode('utf-8')[1:-1])

    withdrawable_balance = anchored_deposit - total_withdrawn
    pending = total_deposit - anchored_deposit
    return withdrawable_balance, pending
Esempio n. 10
0
 def get_bridge_tempo(self,
                      from_chain: str,
                      to_chain: str,
                      aergo: herapy.Aergo = None,
                      bridge_address: str = None,
                      sync: bool = False) -> Tuple[int, int]:
     """ Return the anchoring periode of from_chain onto to_chain
     and minimum finality time of from_chain. This information is
     queried from bridge_to.
     """
     if not sync:
         t_anchor = self.config_data('networks', to_chain, 'bridges',
                                     from_chain, "t_anchor")
         t_final = self.config_data('networks', to_chain, 'bridges',
                                    from_chain, "t_final")
         return t_anchor, t_final
     logger.info(
         "getting latest t_anchor and t_final from bridge contract...")
     if aergo is None:
         aergo = self._connect_aergo(to_chain)
     if bridge_address is None:
         bridge_address = self.config_data('networks', to_chain, 'bridges',
                                           from_chain, 'addr')
     # Get bridge information
     bridge_info = aergo.query_sc_state(bridge_address, [
         "_sv__tAnchor",
         "_sv__tFinal",
     ])
     if not bridge_info.account.state_proof.inclusion:
         raise InvalidArgumentsError(
             "Contract doesnt exist in state, check contract deployed and "
             "chain synced {}".format(bridge_info))
     if not bridge_info.var_proofs[0].inclusion:
         raise InvalidArgumentsError("Cannot query T_anchor", bridge_info)
     if not bridge_info.var_proofs[1].inclusion:
         raise InvalidArgumentsError("Cannot query T_final", bridge_info)
     t_anchor, t_final = [
         int(item.value) for item in bridge_info.var_proofs
     ]
     aergo.disconnect()
     self.config_data('networks',
                      to_chain,
                      'bridges',
                      from_chain,
                      "t_anchor",
                      value=t_anchor)
     self.config_data('networks',
                      to_chain,
                      'bridges',
                      from_chain,
                      "t_final",
                      value=t_final)
     self.save_config()
     return t_anchor, t_final
Esempio n. 11
0
def build_deposit_proof(aergo_from: herapy.Aergo, aergo_to: herapy.Aergo,
                        receiver: str, bridge_from: str, bridge_to: str,
                        deposit_height: int, token_origin: str,
                        key_word: str) -> herapy.obj.sc_state.SCState:
    """ 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))
    # check last merged height
    anchor_info = aergo_to.query_sc_state(bridge_to, ["_sv__anchorHeight"])
    if not anchor_info.account.state_proof.inclusion:
        raise InvalidArgumentsError(
            "Contract doesnt exist in state, check contract deployed and "
            "chain synced {}".format(anchor_info))
    if not anchor_info.var_proofs[0].inclusion:
        raise InvalidArgumentsError("Cannot query last anchored height",
                                    anchor_info)
    last_merged_height_to = int(anchor_info.var_proofs[0].value)
    _, current_height = aergo_to.get_blockchain_status()
    # waite for anchor containing our transfer
    stream = aergo_to.receive_event_stream(bridge_to,
                                           "newAnchor",
                                           start_block_no=current_height)
    while last_merged_height_to < deposit_height:
        logger.info(
            "deposit not recorded in current anchor, waiting new anchor "
            "event... / deposit height : %s / last anchor height : %s ",
            deposit_height, last_merged_height_to)
        new_anchor_event = next(stream)
        last_merged_height_to = new_anchor_event.arguments[1]
    stream.stop()
    # get inclusion proof of lock in last merged block
    merge_block_from = aergo_from.get_block_headers(
        block_height=last_merged_height_to, list_size=1)
    root_from = merge_block_from[0].blocks_root_hash
    account_ref = receiver + token_origin
    proof = aergo_from.query_sc_state(bridge_from, [key_word + account_ref],
                                      root=root_from,
                                      compressed=False)
    if not proof.verify_proof(root_from):
        raise InvalidMerkleProofError(
            "Unable to verify {} proof".format(key_word))
    if not proof.account.state_proof.inclusion:
        raise InvalidMerkleProofError(
            "Contract doesnt exist in state, check contract deployed and "
            "chain synced {}".format(proof))
    if not proof.var_proofs[0].inclusion:
        raise InvalidMerkleProofError(
            "No tokens deposited for this account reference: {}".format(proof))
    return proof
Esempio n. 12
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
Esempio n. 13
0
    def prompt_transfer_networks(self):
        """Prompt user to choose 2 networks between registered bridged
        networks.

        """
        networks = self.get_registered_networks()
        questions = [{
            'type': 'list',
            'name': 'from_chain',
            'message': 'Departure network',
            'choices': networks
        }]
        answers = inquirer.prompt(questions, style=aergo_style)
        from_chain = answers['from_chain']
        networks = [
            net for net in self.wallet.config_data('networks', from_chain,
                                                   'bridges')
        ]
        if len(networks) == 0:
            raise InvalidArgumentsError('No bridge registered to this network')
        questions = [{
            'type': 'list',
            'name': 'to_chain',
            'message': 'Destination network',
            'choices': networks
        }]
        answers = inquirer.prompt(questions, style=aergo_style)
        to_chain = answers['to_chain']
        return from_chain, to_chain
Esempio n. 14
0
 def get_aergo(self,
               network_name: str,
               privkey_name: str = 'default',
               privkey_pwd: str = None,
               skip_state: bool = False) -> herapy.Aergo:
     """ Return aergo provider with account loaded from keystore """
     if network_name is None:
         raise InvalidArgumentsError("Provide network_name")
     keystore_path = self.config_data('wallet', privkey_name, 'keystore')
     with open(keystore_path, "r") as f:
         keystore = f.read()
     aergo = self._connect_aergo(network_name)
     if privkey_pwd is None:
         logger.info("Decrypt exported private key '%s'", privkey_name)
         while True:
             try:
                 privkey_pwd = getpass("Password: "******"Wrong password, try again")
                 continue
             break
     else:
         aergo.import_account_from_keystore(keystore,
                                            privkey_pwd,
                                            skip_state=skip_state)
     return aergo
Esempio n. 15
0
    def get_balance_eth(
        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.get_eth_wallet_address(account_name)
        if not is_ethereum_address(account_addr):
            raise InvalidArgumentsError(
                "Account {} must be an Ethereum address".format(account_addr))
        w3 = self.get_web3(network_name)
        asset_addr = self.get_asset_address(asset_name, network_name,
                                            asset_origin_chain)
        if asset_origin_chain is None:
            if asset_addr == 'ether':
                balance = eth_u.get_balance(account_addr, asset_addr, w3)
                return balance, asset_addr
            abi = self.load_erc20_abi(network_name, asset_name)
        else:
            abi = self.load_minted_erc20_abi(network_name, asset_origin_chain)

        balance = eth_u.get_balance(account_addr, asset_addr, w3, abi)
        return balance, asset_addr
Esempio n. 16
0
def mint(
    aergo_to: herapy.Aergo,
    receiver: str,
    lock_proof: herapy.obj.sc_state.SCState,
    token_origin: str,
    bridge_to: str,
    gas_limit: int,
    gas_price: int
) -> Tuple[str, str]:
    """ Mint the receiver's deposit balance on aergo_to. """
    if not is_aergo_address(receiver):
        raise InvalidArgumentsError(
            "Receiver {} must be an Aergo address".format(receiver)
        )
    auditPath = lock_proof.var_proofs[0].auditPath
    ap = [node.hex() for node in auditPath]
    balance = lock_proof.var_proofs[0].value.decode('utf-8')[1:-1]
    ubig_balance = {'_bignum': str(balance)}
    # call mint on aergo_to with the lock proof from aergo_from
    tx, result = aergo_to.call_sc(
        bridge_to, "mint", args=[receiver, ubig_balance, token_origin, ap],
        gas_limit=gas_limit, gas_price=gas_price
    )
    if result.status != herapy.CommitStatus.TX_OK:
        raise TxError("Mint asset Tx commit failed : {}".format(result))

    result = aergo_to.wait_tx_result(tx.tx_hash)
    if result.status != herapy.TxResultStatus.SUCCESS:
        raise TxError("Mint asset Tx execution failed : {}".format(result))
    logger.info("\u26fd gas used: %s", result.gas_used)
    token_pegged = json.loads(result.detail)[0]
    return token_pegged, str(tx.tx_hash)
Esempio n. 17
0
def withdrawable(bridge_from: str, bridge_to: str, w3: Web3,
                 hera: herapy.Aergo, eth_trie_key: bytes,
                 aergo_storage_key: bytes) -> Tuple[int, int]:
    # total_deposit : total latest deposit including pending
    bridge_from = Web3.toChecksumAddress(bridge_from)
    storage_value = w3.eth.getStorageAt(bridge_from, eth_trie_key, 'latest')
    total_deposit = int.from_bytes(storage_value, "big")

    # get total withdrawn and last anchor height
    withdraw_proof = hera.query_sc_state(
        bridge_to, ["_sv__anchorHeight", aergo_storage_key], compressed=False)
    if not withdraw_proof.account.state_proof.inclusion:
        raise InvalidArgumentsError(
            "Contract doesnt exist in state, check contract deployed and "
            "chain synced {}".format(withdraw_proof))
    if not withdraw_proof.var_proofs[0].inclusion:
        raise InvalidMerkleProofError("Cannot query last anchored height",
                                      withdraw_proof)
    total_withdrawn = 0
    if withdraw_proof.var_proofs[1].inclusion:
        total_withdrawn = int(
            withdraw_proof.var_proofs[1].value.decode('utf-8')[1:-1])
    last_anchor_height = int(withdraw_proof.var_proofs[0].value)

    # get anchored deposit : total deposit before the last anchor
    storage_value = w3.eth.getStorageAt(bridge_from, eth_trie_key,
                                        last_anchor_height)
    anchored_deposit = int.from_bytes(storage_value, "big")

    withdrawable_balance = anchored_deposit - total_withdrawn
    pending = total_deposit - anchored_deposit
    return withdrawable_balance, pending
Esempio n. 18
0
def unfreeze(aergo_to: herapy.Aergo, receiver: str, lock_proof: AttributeDict,
             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))
    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 unlock on aergo_to with the burn proof from aergo_from
    tx, result = aergo_to.call_sc(bridge_to,
                                  "unfreeze",
                                  args=[receiver, ubig_balance, ap],
                                  gas_limit=gas_limit,
                                  gas_price=gas_price)
    if result.status != herapy.CommitStatus.TX_OK:
        raise TxError("Unfreeze asset Tx commit failed : {}".format(result))

    result = aergo_to.wait_tx_result(tx.tx_hash)
    if result.status != herapy.TxResultStatus.SUCCESS:
        raise TxError("Unfreeze asset Tx execution failed : {}".format(result))
    logger.info("\u26fd Unfreeze tx fee paid: %s", result.fee_used)
    logger.info("\u26fd Aergo gas used: %s", result.gas_used)
    return str(tx.tx_hash), result
Esempio n. 19
0
 def register_account(self,
                      account_name: str,
                      keystore_path: str,
                      password: str = None,
                      addr: str = None) -> str:
     """Register and exported account to config.json"""
     try:
         self.config_data('wallet', account_name)
     except KeyError:
         # if KeyError then account doesn't already exists
         if addr is None:
             aergo = herapy.Aergo()
             with open(keystore_path, "r") as f:
                 keystore = f.read()
             account = aergo.import_account_from_keystore(keystore,
                                                          password,
                                                          skip_state=True,
                                                          skip_self=True)
             addr = str(account.address)
         self.config_data('wallet', account_name, value={})
         self.config_data('wallet', account_name, 'addr', value=addr)
         self.config_data('wallet',
                          account_name,
                          'keystore',
                          value=keystore_path)
         self.save_config()
         return addr
     error = "Error: account name '{}' already exists".format(account_name)
     raise InvalidArgumentsError(error)
Esempio n. 20
0
def freeze(
    aergo_from: herapy.Aergo,
    bridge_from: str,
    receiver: str,
    value: int,
    gas_limit: int,
    gas_price: int,
) -> Tuple[int, str, Transaction]:
    """ Freeze aergo native """
    if not is_ethereum_address(receiver):
        raise InvalidArgumentsError(
            "Receiver {} must be an Ethereum address".format(receiver))
    args = (receiver[2:].lower(), {"_bignum": str(value)})
    tx, result = aergo_from.call_sc(bridge_from,
                                    "freeze",
                                    amount=value,
                                    args=args,
                                    gas_limit=gas_limit,
                                    gas_price=gas_price)

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

    # Check freeze success
    result = aergo_from.wait_tx_result(tx.tx_hash)
    if result.status != herapy.TxResultStatus.SUCCESS:
        raise TxError("Freeze Aer 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)
    freeze_height = tx_detail.block.height
    return freeze_height, str(tx.tx_hash), tx_detail
Esempio n. 21
0
    def lock_to_eth(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 Aergo Standard Token transfer to Ethereum sidechain"""
        logger.info(from_chain + ' -> ' + to_chain)
        if not is_ethereum_address(receiver):
            raise InvalidArgumentsError(
                "receiver {} must be an Ethereum address".format(receiver))
        if asset_name == 'aergo':
            raise InvalidArgumentsError(
                'aer cannot be locked on Aergo, must be frozen')
        aergo_from = self.get_aergo(from_chain, privkey_name, privkey_pwd)
        sender = str(aergo_from.account.address)
        bridge_from = self.get_bridge_contract_address(from_chain, to_chain)
        asset_address = self.get_asset_address(asset_name, from_chain)

        gas_limit = 300000
        balance = aergo_u.get_balance(sender, asset_address, aergo_from)
        if balance < amount:
            raise InsufficientBalanceError("not enough token balance")
        logger.info("\U0001f4b0 %s balance on origin before transfer: %s",
                    asset_name, balance / 10**18)

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

        lock_height, tx_hash, _ = aergo_to_eth.lock(aergo_from, bridge_from,
                                                    receiver, amount,
                                                    asset_address, gas_limit,
                                                    self.aergo_gas_price)
        logger.info('\U0001f512 Lock success: %s', tx_hash)

        # remaining balance on origin : aer or asset
        balance = aergo_u.get_balance(sender, asset_address, aergo_from)
        logger.info(
            "\U0001f4b0 remaining %s balance on origin after transfer: %s",
            asset_name, balance / 10**18)

        aergo_from.disconnect()
        return lock_height, tx_hash
Esempio n. 22
0
    def unlock_to_eth(
        self,
        from_chain: str,
        to_chain: str,
        asset_name: str,
        receiver: str = None,
        burn_height: int = 0,
        privkey_name: str = 'default',
        privkey_pwd: str = None,
    ) -> str:
        """ Finalize ERC20 or Eth transfer back to Ethereum origin """
        logger.info(from_chain + ' -> ' + to_chain)
        bridge_to_abi = self.load_bridge_abi(to_chain, from_chain)
        erc20_abi = self.load_erc20_abi(to_chain, asset_name)
        aergo_from = self.connect_aergo(from_chain)
        # get ethereum tx signer
        w3 = self.get_web3(to_chain)
        signer_acct = self.get_signer(w3, privkey_name, privkey_pwd)
        tx_sender = signer_acct.address

        if receiver is None:
            receiver = tx_sender
        if not is_ethereum_address(receiver):
            raise InvalidArgumentsError(
                "receiver {} must be an Ethereum address".format(receiver))

        bridge_from = self.get_bridge_contract_address(from_chain, to_chain)
        bridge_to = self.get_bridge_contract_address(to_chain, from_chain)
        asset_address = self.get_asset_address(asset_name, to_chain)
        balance = eth_u.get_balance(receiver, asset_address, w3, erc20_abi)
        logger.info(
            "\U0001f4b0 %s balance on destination before transfer : %s",
            asset_name, balance / 10**18)

        gas_limit = 200000
        eth_balance = eth_u.get_balance(tx_sender, '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_proof = aergo_to_eth.build_burn_proof(aergo_from, w3, receiver,
                                                   bridge_from, bridge_to,
                                                   bridge_to_abi, burn_height,
                                                   asset_address)
        logger.info("\u2699 Built burn proof")

        tx_hash, _ = aergo_to_eth.unlock(w3, signer_acct, receiver, burn_proof,
                                         asset_address, bridge_to,
                                         bridge_to_abi, gas_limit,
                                         self.eth_gas_price)
        logger.info('\U0001f513 Unlock success: %s', tx_hash)

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

        aergo_from.disconnect()
        return tx_hash
Esempio n. 23
0
def _build_deposit_proof(w3: Web3, aergo_to: herapy.Aergo, bridge_from: str,
                         bridge_to: str, deposit_height: int, trie_key: bytes):
    """ Check the last anchored root includes the deposit and build
    a deposit (lock or burn) proof for that root
    """
    bridge_from = Web3.toChecksumAddress(bridge_from)
    # check last merged height
    _, aergo_current_height = aergo_to.get_blockchain_status()
    anchor_info = aergo_to.query_sc_state(bridge_to, ["_sv__anchorHeight"])
    if not anchor_info.account.state_proof.inclusion:
        raise InvalidArgumentsError(
            "Contract doesnt exist in state, check contract deployed and "
            "chain synced {}".format(anchor_info))
    if not anchor_info.var_proofs[0].inclusion:
        raise InvalidArgumentsError("Cannot query last anchored height",
                                    anchor_info)
    last_merged_height_to = int(anchor_info.var_proofs[0].value)
    # waite for anchor containing our transfer
    stream = aergo_to.receive_event_stream(bridge_to,
                                           "newAnchor",
                                           start_block_no=aergo_current_height)
    while last_merged_height_to < deposit_height:
        logger.info(
            "\u23F0 deposit not recorded in current anchor, waiting new "
            "anchor event... / deposit height : %s / last anchor height : %s ",
            deposit_height, last_merged_height_to)
        new_anchor_event = next(stream)
        last_merged_height_to = new_anchor_event.arguments[1]
    stream.stop()
    # get inclusion proof of lock in last merged block
    block = w3.eth.getBlock(last_merged_height_to)
    eth_proof = w3.eth.getProof(bridge_from, [trie_key], last_merged_height_to)
    try:
        verify_eth_getProof_inclusion(eth_proof, block.stateRoot)
    except AssertionError as e:
        raise InvalidMerkleProofError("Unable to verify deposit proof",
                                      eth_proof, e)
    if trie_key != eth_proof.storageProof[0].key:
        raise InvalidMerkleProofError("Proof doesnt match requested key",
                                      eth_proof, trie_key)
    if len(eth_proof.storageProof[0].value) == 0:
        raise InvalidMerkleProofError("Trie key {} doesn't exist".format(
            trie_key.hex()))
    return eth_proof
Esempio n. 24
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
Esempio n. 25
0
def get_balance(account_addr: str,
                asset_addr: str,
                w3: Web3,
                erc20_abi: str = None) -> int:
    if not is_ethereum_address(account_addr):
        raise InvalidArgumentsError(
            "Account {} must be an Ethereum address".format(account_addr))
    account_addr = Web3.toChecksumAddress(account_addr)
    balance = 0
    if asset_addr == "ether":
        balance = w3.eth.getBalance(account_addr)
    else:
        asset_addr = Web3.toChecksumAddress(asset_addr)
        if erc20_abi is None:
            raise InvalidArgumentsError("Provide token abi to query balance")
        token_contract = w3.eth.contract(address=asset_addr, abi=erc20_abi)
        try:
            balance = token_contract.functions.balanceOf(account_addr).call()
        except BadFunctionCallOutput as e:
            raise InvalidArgumentsError(e, asset_addr)
    return balance
Esempio n. 26
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
Esempio n. 27
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
Esempio n. 28
0
 def update_unfreeze_fee(self):
     from_chain, to_chain = self.prompt_transfer_networks()
     if self.wallet.config_data('networks', to_chain, 'type') == 'ethereum':
         raise InvalidArgumentsError(
             "Unfreeze only available on Aergo network")
     fee = prompt_number("New Aergo unfreeze fee on {}".format(to_chain))
     self.wallet.config_data('networks',
                             to_chain,
                             'bridges',
                             from_chain,
                             'unfreeze_fee',
                             value=fee)
     self.wallet.save_config()
Esempio n. 29
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
Esempio n. 30
0
def _build_deposit_proof(
    aergo_from: herapy.Aergo,
    w3: Web3,
    bridge_from: str,
    bridge_to: str,
    bridge_to_abi: str,
    deposit_height: int,
    trie_key: bytes,
):
    """ Check the last anchored root includes the deposit and build
    a deposit proof for that root
    """
    # check last merged height
    eth_bridge = w3.eth.contract(
        address=bridge_to,
        abi=bridge_to_abi
    )
    try:
        last_merged_height_to = eth_bridge.functions._anchorHeight().call()
    except BadFunctionCallOutput as e:
        raise InvalidArgumentsError(e, bridge_to)
    # waite for anchor containing our transfer
    if last_merged_height_to < deposit_height:
        logger.info(
            "\u23F0 deposit not recorded in current anchor, waiting new "
            "anchor event... / deposit height : %s / last anchor height : %s ",
            deposit_height, last_merged_height_to
        )
        while last_merged_height_to < deposit_height:
            time.sleep(1)
            last_merged_height_to = eth_bridge.functions._anchorHeight().call()
    # get inclusion proof of lock in last merged block
    merge_block_from = aergo_from.get_block_headers(
        block_height=last_merged_height_to, list_size=1)
    root_from = merge_block_from[0].blocks_root_hash
    proof = aergo_from.query_sc_state(
        bridge_from, [trie_key],
        root=root_from, compressed=True
    )
    if not proof.verify_proof(root_from):
        raise InvalidMerkleProofError("Unable to verify deposit proof",
                                      proof)
    if not proof.account.state_proof.inclusion:
        raise InvalidMerkleProofError(
            "Contract doesnt exist in state, check contract deployed and "
            "chain synced {}".format(proof))
    if not proof.var_proofs[0].inclusion:
        raise InvalidMerkleProofError(
            "No tokens deposited for this account reference: {}"
            .format(proof))
    return proof