Exemple #1
0
    def transfer(self, txn):
        """
        Make a transaction/transfer on regtest bitcoin
        """
        self.wait_for_node()

        if not isinstance(txn.coins, list):
            txn.coins = [txn.coins]

        if txn.to_address in get_aliases():
            txn.to_address = get_alias_address(txn.chain, txn.to_address)

        if txn.from_address in get_aliases():
            txn.from_address = get_alias_address(txn.chain, txn.from_address)

        # update memo with actual address (over alias name)
        for alias in get_aliases():
            chain = txn.chain
            asset = txn.get_asset_from_memo()
            if asset:
                chain = asset.get_chain()
            # we use RUNE BNB address to identify a cross chain stake
            if txn.memo.startswith("STAKE"):
                chain = RUNE.get_chain()
            addr = get_alias_address(chain, alias)
            txn.memo = txn.memo.replace(alias, addr)

        # create transaction
        amount = float(txn.coins[0].amount / Coin.ONE)
        tx_out_dest = {txn.to_address: amount}
        tx_out_op_return = {"data": txn.memo.encode().hex()}

        # get unspents UTXOs
        address = txn.from_address
        min_amount = float(amount +
                           (self.default_gas / Coin.ONE))  # add more for fee
        unspents = self.call("listunspent", 1, 9999, [str(address)], True,
                             {"minimumAmount": min_amount})
        if len(unspents) == 0:
            raise Exception(
                f"Cannot transfer. No BTC UTXO available for {address}")

        # choose the first UTXO
        unspent = unspents[0]
        tx_in = [{"txid": unspent["txid"], "vout": unspent["vout"]}]
        tx_out = [tx_out_dest]

        # create change output if needed
        amount_utxo = float(unspent["amount"])
        amount_change = Decimal(amount_utxo) - Decimal(min_amount)
        if amount_change > 0:
            tx_out.append({txn.from_address: float(amount_change)})

        tx_out.append(tx_out_op_return)

        tx = self.call("createrawtransaction", tx_in, tx_out)
        tx = self.call("signrawtransactionwithwallet", tx)
        txn.id = self.call("sendrawtransaction", tx["hex"]).upper()
        txn.gas = [Coin("BTC.BTC", self.default_gas)]
Exemple #2
0
    def transfer(self, txns):
        if not isinstance(txns, list):
            txns = [txns]

        for txn in txns:
            if not isinstance(txn.coins, list):
                txn.coins = [txn.coins]

            name = txn.from_address
            txn.gas = [Coin("THOR.RUNE", 100000000)]
            if txn.from_address in get_aliases():
                txn.from_address = get_alias_address(txn.chain,
                                                     txn.from_address)
            if txn.to_address in get_aliases():
                txn.to_address = get_alias_address(txn.chain, txn.to_address)

            # update memo with actual address (over alias name)
            for alias in get_aliases():
                chain = txn.chain
                asset = txn.get_asset_from_memo()
                if asset:
                    chain = asset.get_chain()
                addr = get_alias_address(chain, alias)
                txn.memo = txn.memo.replace(alias, addr)

            acct = self._get_account(txn.from_address)

            payload = {
                "coins": [coin.to_thorchain_fmt() for coin in txn.coins],
                "memo": txn.memo,
                "base_req": {
                    "chain_id": "thorchain",
                    "from": txn.from_address
                },
            }

            payload = self.post("/thorchain/native/tx", payload)
            msgs = payload["value"]["msg"]
            fee = payload["value"]["fee"]
            acct_num = acct["result"]["value"]["account_number"]
            seq = acct["result"]["value"]["sequence"]
            sig = self._sign(
                name,
                self._get_sign_message("thorchain", acct_num, fee, seq, msgs))
            pushable = self.get_pushable(name, msgs, sig, fee, acct_num, seq)
            result = self.send(pushable)
            txn.id = result["txhash"]
Exemple #3
0
    def transfer(self, txns):
        """
        Make a transaction/transfer on mock binance
        """
        if not isinstance(txns, list):
            txns = [txns]

        payload = []
        for txn in txns:
            if not isinstance(txn.coins, list):
                txn.coins = [txn.coins]

            if txn.to_address in get_aliases():
                txn.to_address = get_alias_address(txn.chain, txn.to_address)

            if txn.from_address in get_aliases():
                txn.from_address = get_alias_address(txn.chain,
                                                     txn.from_address)

            # update memo with actual address (over alias name)
            for alias in get_aliases():
                chain = txn.chain
                asset = txn.get_asset_from_memo()
                if asset:
                    chain = asset.get_chain()
                if txn.memo.startswith("STAKE"):
                    if asset and txn.chain == asset.get_chain():
                        chain = RUNE.get_chain()
                addr = get_alias_address(chain, alias)
                txn.memo = txn.memo.replace(alias, addr)

            payload.append({
                "from":
                txn.from_address,
                "to":
                txn.to_address,
                "memo":
                txn.memo,
                "coins": [coin.to_binance_fmt() for coin in txn.coins],
            })
        result = self.post("/broadcast/easy", payload)
        txn.id = self.get_tx_id_from_block(result["height"])
Exemple #4
0
    def transfer(self, txn):
        """
        Make a transaction/transfer on localnet Ethereum
        """
        if not isinstance(txn.coins, list):
            txn.coins = [txn.coins]

        if txn.to_address in get_aliases():
            txn.to_address = get_alias_address(txn.chain, txn.to_address)

        if txn.from_address in get_aliases():
            txn.from_address = get_alias_address(txn.chain, txn.from_address)

        # update memo with actual address (over alias name)
        for alias in get_aliases():
            chain = txn.chain
            asset = txn.get_asset_from_memo()
            if asset:
                chain = asset.get_chain()
            # we use RUNE BNB address to identify a cross chain stake
            if txn.memo.startswith("STAKE"):
                chain = RUNE.get_chain()
            addr = get_alias_address(chain, alias)
            txn.memo = txn.memo.replace(alias, addr)

        # create and send transaction
        tx = {
            "from": Web3.toChecksumAddress(txn.from_address),
            "to": Web3.toChecksumAddress(txn.to_address),
            "value": txn.coins[0].amount,
            "data": "0x" + txn.memo.encode().hex(),
            "gas": calculate_gas(txn.memo),
        }

        tx_hash = self.web3.geth.personal.send_transaction(tx, self.passphrase)
        receipt = self.web3.eth.waitForTransactionReceipt(tx_hash)
        txn.id = receipt["transactionHash"].hex()[2:].upper()
        txn.gas = [Coin("ETH.ETH", receipt["cumulativeGasUsed"] * self.gas_price)]
Exemple #5
0
    def handle_unstake(self, txn):
        """
        handles a unstaking transaction
        MEMO: WITHDRAW:<asset(req)>:<address(op)>:<basis_points(op)>
        """
        withdraw_basis_points = 10000

        # parse memo
        parts = txn.memo.split(":")
        if len(parts) < 2:
            if txn.memo == "":
                return self.refund(txn, 105, "memo can't be empty")
            return self.refund(txn, 105, f"invalid tx type: {txn.memo}")

        # get withdrawal basis points, if it exists in the memo
        if len(parts) >= 3:
            withdraw_basis_points = int(parts[2])

        # empty asset
        if parts[1] == "":
            return self.refund(txn, 105, "Invalid symbol")

        asset = Asset(parts[1])

        # add any rune to the reserve
        for coin in txn.coins:
            if coin.asset.is_rune():
                self.reserve += coin.amount
            else:
                coin.amount = 0

        pool = self.get_pool(asset)
        staker = pool.get_staker(txn.from_address)
        if staker.is_zero():
            # FIXME real world message
            return self.refund(txn, 105, "refund reason message")

        # calculate gas prior to update pool in case we empty the pool
        # and need to subtract
        gas = None
        if asset.get_chain() == "BTC":
            gas = [Bitcoin._calculate_gas(pool, txn)]

        if asset.get_chain() == "ETH":
            gas = [Ethereum._calculate_gas(pool, txn)]

        unstake_units, rune_amt, asset_amt = pool.unstake(
            txn.from_address, withdraw_basis_points)

        # if this is our last staker of bnb, subtract a little BNB for gas.
        if pool.total_units == 0:
            if pool.asset.is_bnb():
                fee_amt = 37500
                if RUNE.get_chain() == "BNB":
                    fee_amt *= 2
                asset_amt -= fee_amt
                pool.asset_balance += fee_amt
            elif pool.asset.is_btc() or pool.asset.is_eth():
                asset_amt -= gas[0].amount
                pool.asset_balance += gas[0].amount

        self.set_pool(pool)

        # get from address VAULT cross chain
        from_address = txn.to_address
        if from_address != "VAULT":  # don't replace for unit tests
            from_alias = get_alias(txn.chain, from_address)
            from_address = get_alias_address(asset.get_chain(), from_alias)

        # get to address cross chain
        to_address = txn.from_address
        if to_address not in get_aliases():  # don't replace for unit tests
            to_alias = get_alias(txn.chain, to_address)
            to_address = get_alias_address(asset.get_chain(), to_alias)

        out_txns = [
            Transaction(
                RUNE.get_chain(),
                txn.to_address,
                txn.from_address,
                [Coin(RUNE, rune_amt)],
                f"OUTBOUND:{txn.id.upper()}",
            ),
            Transaction(
                asset.get_chain(),
                from_address,
                to_address,
                [Coin(asset, asset_amt)],
                f"OUTBOUND:{txn.id.upper()}",
                gas=gas,
            ),
        ]

        # generate event for UNSTAKE transaction
        event = Event(
            "unstake",
            [
                {
                    "pool": pool.asset
                },
                {
                    "stake_units": unstake_units
                },
                {
                    "basis_points": withdraw_basis_points
                },
                {
                    "asymmetry": "0.000000000000000000"
                },
                *txn.get_attributes(),
            ],
        )
        self.events.append(event)

        return out_txns