PHP WebShell

Текущая директория: /opt/BitGoJS/modules/blockapis/src/impl

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

import { bitgo } from '@bitgo/utxo-lib';
import { AddressApi, AddressInfo } from '../AddressApi';
import { OutputSpend, TransactionIO, UtxoApi } from '../UtxoApi';
import { ApiRequestError, BaseHttpClient, HttpClient, mapSeries } from '../BaseHttpClient';
import { ApiNotImplementedError } from '../ApiBuilder';
import { BlockApi, TransactionStatus } from '../TransactionApi';

type Unspent = bitgo.Unspent;
const formatOutputId = bitgo.formatOutputId;

// https://github.com/Blockstream/esplora/blob/master/API.md#get-addressaddress
type EsploraAddressStats = {
  tx_count: number;
  funded_txo_sum: number;
  spent_txo_sum: number;
};

// https://github.com/Blockstream/esplora/blob/master/API.md#get-addressaddress
type EsploraAddress = {
  address: string;
  chain_stats: EsploraAddressStats;
  mempool_staats: EsploraAddressStats;
};

// https://github.com/Blockstream/esplora/blob/master/API.md#get-addressaddressutxo
type EsploraVin = {
  txid: string;
  vout: number;
  status: unknown;
  value: number;
  prevout: EsploraVout;
};

// https://github.com/Blockstream/esplora/blob/master/API.md#get-addressaddressutxo
type EsploraVout = {
  scriptpubkey: string;
  scriptpubkey_address: string;
  value: number;
};

// https://github.com/Blockstream/esplora/blob/master/API.md#get-txtxidoutspendvout
type EsploraOutspend = {
  txid: string;
  vin: number;
};

function toBitGoUnspent(u: EsploraVin, address: string, value: number): Unspent {
  return {
    id: formatOutputId(u),
    address,
    value,
  };
}

// https://github.com/Blockstream/esplora/blob/master/API.md#get-txtxidstatus
type EsploraStatus =
  | {
      confirmed: false;
    }
  | {
      confirmed: true;
      block_height: number;
      block_hash: string;
    };

// https://github.com/Blockstream/esplora/blob/master/API.md#get-txtxid
type EsploraTransaction = {
  txid: string;
  vin: EsploraVin[];
  vout: EsploraVout[];
  status: EsploraStatus;
};

export class BlockstreamApi implements AddressApi, BlockApi, UtxoApi {
  static forCoin(coinName: string, params: { httpClient?: HttpClient } = {}): BlockstreamApi {
    const { httpClient = new BaseHttpClient() } = params;
    switch (coinName) {
      case 'btc':
        return new BlockstreamApi(httpClient.withBaseUrl('https://blockstream.info/api'));
      case 'tbtc':
        return new BlockstreamApi(httpClient.withBaseUrl('https://blockstream.info/testnet/api'));
    }

    throw new ApiNotImplementedError(coinName);
  }

  constructor(public client: HttpClient) {}

  async getBlockIdAtHeight(height: number): Promise<string> {
    // https://github.com/Blockstream/esplora/blob/master/API.md#get-block-heightheight
    return (await this.client.get<string>(`/block-height/${height}`)).map((body) => body);
  }

  async getTransactionIds(hash: string): Promise<string[]> {
    // https://github.com/Blockstream/esplora/blob/master/API.md#get-blockhashtxids
    return (await this.client.get<string[]>(`/block/${hash}/txids`)).map((body) => body);
  }

  async getAddressInfo(address: string): Promise<AddressInfo> {
    const response = await this.client.get<EsploraAddress>(`/address/${address}`);
    return response.map((body) => {
      return {
        txCount: body.chain_stats.tx_count,
        balance: body.chain_stats.funded_txo_sum - body.chain_stats.spent_txo_sum,
      };
    });
  }

  async getUnspentsForAddresses(addrs: string[]): Promise<Unspent[]> {
    if (addrs.length !== 1) {
      return (await mapSeries(addrs, (a) => this.getUnspentsForAddresses([a]))).flat();
    }

    const [address] = addrs;

    return (await this.client.get<EsploraVin[]>(`/address/${address}/utxo`)).map((unspents) =>
      unspents.map((u) => toBitGoUnspent(u, address, u.value))
    );
  }

  async getTransactionHex(txid: string): Promise<string> {
    return (await this.client.get<string>(`/tx/${txid}/hex`)).map((v) => v);
  }

  async getTransactionStatus(txid: string): Promise<TransactionStatus> {
    try {
      return (await this.client.get<EsploraTransaction>(`/tx/${txid}`)).map(({ status }) =>
        status.confirmed
          ? { found: true, confirmed: true, blockHeight: status.block_height, blockHash: status.block_hash }
          : { found: true, confirmed: false }
      );
    } catch (e) {
      if (e instanceof ApiRequestError) {
        const reason = e.reason as any;
        if (reason.response.status === 404 && reason.response.text === 'Transaction not found') {
          return { found: false };
        }
      }
      throw e;
    }
  }

  async getTransactionInputs(txid: string): Promise<Unspent[]> {
    return (await this.client.get<EsploraTransaction>(`/tx/${txid}`)).map((body) =>
      body.vin.map((u) => toBitGoUnspent(u, u.prevout.scriptpubkey_address, u.prevout.value))
    );
  }

  async getTransactionIO(txid: string): Promise<TransactionIO> {
    const tx = await this.client.get<EsploraTransaction>(`/tx/${txid}`);
    const inputs = tx.map((body) =>
      body.vin.map((u) => {
        return {
          address: u.prevout.scriptpubkey_address,
        };
      })
    );
    const outputs = tx.map((body) =>
      body.vout.map((u) => {
        return {
          address: u.scriptpubkey_address,
        };
      })
    );
    return {
      inputs,
      outputs,
    };
  }

  async getTransactionSpends(txid: string): Promise<OutputSpend[]> {
    return (await this.client.get<EsploraOutspend[]>(`/tx/${txid}/outspends`)).map((arr) =>
      arr.map((v) => (v.txid ? { txid: v.txid, vin: v.vin } : { txid: undefined, vin: undefined }))
    );
  }
}

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


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