PHP WebShell

Текущая директория: /opt/BitGoJS/examples/ts/btc/omni

Просмотр файла: index.ts

/**
 * Send or create an omni asset from a multi-sig wallet at BitGo.
 *
 * Copyright 2024, BitGo, Inc.  All Rights Reserved.
 */
import { Wallet } from 'modules/bitgo/src';
import { omniConfig } from './config';
import * as superagent from 'superagent';
import * as utxolib from '@bitgo/utxo-lib';

const RECEIVE_ADDRESS = '';
const SEND_ADDRESS = '';
const ASSET_ID = 31;
const BASE_AMOUNT = 729100000n; // this is currently 7.291 USDT

async function getWallet() {
  return await omniConfig.sdk.coin(omniConfig.coin).wallets().get({ id: omniConfig.walletId });
}

/**
 * Send an omni asset to a receiver. This function is used when you have sent an omni asset to a BitGo BTC wallet
 * and need to manually recover it
 * This function assumes that:
 *   - Your address has at least one unspent that is large enough to cover the transaction
 *   - The receiver address is a legacy or wrapped segwit ([reference](https://developers.bitgo.com/coins/address-types)) address, otherwise the transaction will not be recognized by the omni explorer.
 * @param wallet - The wallet to send the omni asset from.
 * @param receiver - The address to send the omni asset to (legacy address required).
 * @param sender - The address to send the omni asset from (legacy address required).
 * @param omniBaseAmount - The amount of the omni asset to send
 *   with respect to its smallest unit (e.g., microcents for USDT).
 *   Can be found at https://api.omniexplorer.info/v1/transaction/tx/{prev_txid}
 *   by multiplying `amount` by 10e8 if `divisible` is true.
 *   If `divisible` is false, `amount` is the amount of the omni asset to send
 * @param assetId - The id of the omni asset to send.
 *   Can be found at https://api.omniexplorer.info/v1/transaction/tx/{prev_txid}
 *   by looking at the `propertyid` field.
 *   This is 31 for USDT.
 * @param feeRateSatPerKB - The fee rate to use for the transaction, in satoshis per kilobyte.
 */
async function sendOmniAsset(
  wallet: Wallet,
  receiver: string,
  sender: string,
  omniBaseAmount: bigint,
  assetId = 31,
  feeRateSatPerKB = 20_000
) {
  if (!['1', '3', 'n', 'm'].includes(receiver.slice(0, 1))) {
    throw new Error(
      'Omni has only been verified to work with legacy and wrapped segwit addresses - use other address formats at your own risk'
    );
  }

  const res = await superagent.get(`https://mempool.space/${omniConfig.MEMPOOL_PREFIX}api/address/${sender}/utxo`);
  const unspent = res.body[0];
  const unspent_id = unspent.txid + ':' + unspent.vout;

  // scriptPubkey: op_return omni simple_send tether amount
  const transactionType = Buffer.alloc(4);
  const assetHex = Buffer.alloc(4);
  assetHex.writeUInt32BE(assetId);
  const amountHex = Buffer.alloc(8);
  amountHex.writeBigUInt64BE(omniBaseAmount);
  const omniScript = Buffer.concat([omniConfig.OMNI_PREFIX, transactionType, assetHex, amountHex]);
  const output = utxolib.payments.embed({ data: [omniScript], network: omniConfig.network }).output;
  if (!output) {
    throw new Error('Invalid output');
  }
  const script = output.toString('hex');
  const tx = await wallet.sendMany({
    recipients: [
      // this signals the receiver of the omni asset
      // we are not actually trying to send BTC to the receiver
      // so we send the minimum amount above the dust limit
      {
        amount: '546',
        address: receiver,
      },
      // this is the actual script that the omni layer reads for the send
      {
        amount: '0',
        address: `scriptPubkey:${script}`,
      },
    ],
    isReplaceableByFee: true,
    feeRate: feeRateSatPerKB,
    walletPassphrase: omniConfig.walletPassphrase,
    // we must send change to our input address to ensure that omni won't
    // accidentally send our asset to the change address instead of the recipient
    changeAddress: sender,
    unspents: [unspent_id],
  });
  console.log('Omni asset sent: ', tx);
}

/*
 * Usage: npx ts-node btc/omni/index.ts
 * */
async function main() {
  console.log('Starting...');

  const feeRateRes = await superagent.get(`https://mempool.space/${omniConfig.MEMPOOL_PREFIX}api/v1/fees/recommended`);
  const feeRate = feeRateRes.body.fastestFee;

  const wallet = await getWallet();
  // we multiply feeRate by 1000 because mempool returns sat/vB and BitGo uses sat/kvB
  await sendOmniAsset(wallet, RECEIVE_ADDRESS, SEND_ADDRESS, BASE_AMOUNT, ASSET_ID, feeRate * 1000);
}

main().catch((e) => {
  console.error(e);
  process.exit(1);
});

Выполнить команду


Для локальной разработки. Не используйте в интернете!