UNPKG

meta-contract

Version:

Meta Contract SDK

244 lines (243 loc) 10.8 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Wallet = void 0; const mvc = require("../mvc"); const utils_1 = require("../common/utils"); const api_1 = require("../api"); const tx_composer_1 = require("../tx-composer"); const transactionHelpers_1 = require("../helpers/transactionHelpers"); class Wallet { constructor(wif, network = api_1.API_NET.MAIN, feeb = 1, apiTarget = api_1.API_TARGET.MVC, apiUrl) { if (wif) { this.privateKey = mvc.PrivateKey.fromWIF(wif); } else { this.privateKey = mvc.PrivateKey.fromRandom(network); } this.address = this.privateKey.toAddress(network); this.blockChainApi = new api_1.Api(network, apiTarget, apiUrl); this.feeb = feeb; this.network = network; } get api() { return this.blockChainApi; } getUnspents() { return __awaiter(this, void 0, void 0, function* () { return yield this.blockChainApi.getUnspents(this.address.toString()); }); } getUtxos() { return __awaiter(this, void 0, void 0, function* () { return this.getUnspents(); }); } getBalance() { return __awaiter(this, void 0, void 0, function* () { let { pendingBalance, balance } = yield this.blockChainApi.getBalance(this.address.toString()); return balance + pendingBalance; }); } send(address, amount, options) { return __awaiter(this, void 0, void 0, function* () { const txComposer = new tx_composer_1.TxComposer(); let utxos = yield this.blockChainApi.getUnspents(this.address.toString()); const pickedUtxos = this.pickUtxo(utxos, amount); pickedUtxos.forEach((v) => { txComposer.appendP2PKHInput({ address: new mvc.Address(v.address, this.network), txId: v.txId, outputIndex: v.outputIndex, satoshis: v.satoshis, }); }); txComposer.appendP2PKHOutput({ address: new mvc.Address(address, this.network), satoshis: amount, }); txComposer.appendChangeOutput(this.address, this.feeb); pickedUtxos.forEach((v, index) => { txComposer.unlockP2PKHInput(this.privateKey, index); }); (0, transactionHelpers_1.checkFeeRate)(txComposer, this.feeb); return yield this.broadcastTxComposer(txComposer, options); }); } sendArray(receivers, utxos, options) { return __awaiter(this, void 0, void 0, function* () { const txComposer = new tx_composer_1.TxComposer(); if (!utxos) { utxos = yield this.blockChainApi.getUnspents(this.address.toString()); } utxos.forEach((v) => { txComposer.appendP2PKHInput({ address: new mvc.Address(v.address, this.network), txId: v.txId, outputIndex: v.outputIndex, satoshis: v.satoshis, }); }); receivers.forEach((v) => { txComposer.appendP2PKHOutput({ address: new mvc.Address(v.address, this.network), satoshis: v.amount, }); }); txComposer.appendChangeOutput(this.address, this.feeb); utxos.forEach((v, index) => { txComposer.unlockP2PKHInput(this.privateKey, index); }); (0, transactionHelpers_1.checkFeeRate)(txComposer, this.feeb); return yield this.broadcastTxComposer(txComposer, options); }); } merge(options) { return __awaiter(this, void 0, void 0, function* () { const txComposer = new tx_composer_1.TxComposer(); let utxos = []; let currentUtxos = yield this.blockChainApi.getUnspents(this.address.toString()); while (currentUtxos.length) { utxos = [...utxos, ...currentUtxos]; const flag = currentUtxos[currentUtxos.length - 1].flag; currentUtxos = yield this.blockChainApi.getUnspents(this.address.toString(), flag); } utxos.forEach((v) => { txComposer.appendP2PKHInput({ address: new mvc.Address(v.address, this.network), txId: v.txId, outputIndex: v.outputIndex, satoshis: v.satoshis, }); }); txComposer.appendChangeOutput(this.address, this.feeb); utxos.forEach((v, index) => { txComposer.unlockP2PKHInput(this.privateKey, index); }); (0, transactionHelpers_1.checkFeeRate)(txComposer, this.feeb); return yield this.broadcastTxComposer(txComposer, options); }); } // evenly split all amount into utxo shares, this will increase parallel for wallet // minShareValue is used to guarantee single share value won't be too small // the last share will cover the tx fee evenSplit(shares, minShareValue = 0, options) { return __awaiter(this, void 0, void 0, function* () { const txComposer = new tx_composer_1.TxComposer(); const utxos = yield this.blockChainApi.getUnspents(this.address.toString()); const sumAmount = utxos.map((utxo) => utxo.satoshis).reduce((a, b) => a + b, 0); utxos.forEach((v) => { txComposer.appendP2PKHInput({ address: new mvc.Address(v.address, this.network), txId: v.txId, outputIndex: v.outputIndex, satoshis: v.satoshis, }); }); const shareValue = Math.floor(sumAmount / shares); if (shareValue < minShareValue) { throw new Error(`"Share value ${shareValue} is less than minShareValue ${minShareValue}"`); } for (let i = 0; i < shares - 1; i++) { txComposer.appendP2PKHOutput({ address: this.address, satoshis: shareValue }); } txComposer.appendChangeOutput(this.address, this.feeb); utxos.forEach((v, index) => { txComposer.unlockP2PKHInput(this.privateKey, index); }); (0, transactionHelpers_1.checkFeeRate)(txComposer, this.feeb); return yield this.broadcastTxComposer(txComposer, options); }); } broadcastTxComposer(txComposer, options) { return __awaiter(this, void 0, void 0, function* () { const { noBroadcast, dump } = options || {}; if (dump) { (0, utils_1.dumpTx)(txComposer.getTx(), this.network); } if (noBroadcast) { return { txId: txComposer.getTx().id, txHex: txComposer.getRawHex(), }; } const txId = yield this.blockChainApi.broadcast(txComposer.getRawHex()); return { txId, txHex: txComposer.getRawHex(), }; }); } sendOpReturn(opreturnData, options) { return __awaiter(this, void 0, void 0, function* () { const txComposer = new tx_composer_1.TxComposer(); // pick enough amount for writing opreturn const amountRequired = this.feeb * mvc.Script.buildSafeDataOut(opreturnData).getData().length + 100; let utxos = yield this.blockChainApi.getUnspents(this.address.toString()); // pick enough utxo instead of all utxo const pickedUtxo = this.pickUtxo(utxos, amountRequired); pickedUtxo.forEach((v) => { txComposer.appendP2PKHInput({ address: new mvc.Address(v.address, this.network), txId: v.txId, outputIndex: v.outputIndex, satoshis: v.satoshis, }); }); txComposer.appendOpReturnOutput(opreturnData); txComposer.appendChangeOutput(this.address, this.feeb); pickedUtxo.forEach((v, index) => { txComposer.unlockP2PKHInput(this.privateKey, index); }); return yield this.broadcastTxComposer(txComposer, options); }); } // pick utxo enough to pay amount and fee, use confirmed utxo in priority // shuffle confirmed utxo for higher concurrency pickUtxo(utxos, amount) { // amount + 2 outputs + buffer let requiredAmount = amount + 34 * 2 * this.feeb + 100; const candidateUtxos = []; // split utxo to confirmed and unconfirmed and shuffle them const confirmedUtxos = utxos .filter((utxo) => { return utxo.height > 0; }) .sort(() => Math.random() - 0.5); const unconfirmedUtxos = utxos .filter((utxo) => { return utxo.height <= 0 || utxo.height === null || utxo.height === undefined; }) .sort(() => Math.random() - 0.5); let current = 0; // use confirmed first for (let utxo of confirmedUtxos) { current += Number(utxo.satoshis); // add input fee requiredAmount += this.feeb * utils_1.P2PKH_UNLOCK_SIZE; candidateUtxos.push(utxo); if (current > requiredAmount) { return candidateUtxos; } } for (let utxo of unconfirmedUtxos) { current += Number(utxo.satoshis); // add input fee requiredAmount += this.feeb * utils_1.P2PKH_UNLOCK_SIZE; candidateUtxos.push(utxo); if (current > requiredAmount) { return candidateUtxos; } } return candidateUtxos; } } exports.Wallet = Wallet;