UNPKG

@renproject/ren

Version:

Official Ren JavaScript SDK for bridging crypto assets cross-chain.

215 lines 10.8 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.GatewayTransaction = void 0; const utils_1 = require("@renproject/utils"); const bignumber_js_1 = __importDefault(require("bignumber.js")); const renVMTxSubmitter_1 = require("./renVMTxSubmitter"); const config_1 = require("./utils/config"); const inputAndOutputTypes_1 = require("./utils/inputAndOutputTypes"); /** * A GatewayTransaction handles a specific bridging transaction through RenVM. * It includes an input chain transaction that should have already been * submitted, a RenVM transaction, and an output chain transaction. * Additionally, there may be some setup transactions required to be submitted * before the output transaction. */ class GatewayTransaction { /** @hidden */ constructor(renVM, fromChain, toChain, params, fromTxWaiter, config) { this.outSetup = {}; /** @hidden */ this.initialize = async () => { const { inputType, outputType, selector } = await (0, inputAndOutputTypes_1.getInputAndOutputTypes)({ asset: this.params.asset, fromChain: this.fromChain, toChain: this.toChain, }); this.inputType = inputType; this.outputType = outputType; this.selector = selector; const { asset, nonce, to, fromTx } = this.params; let payload = await this.toChain.getOutputPayload(asset, this.inputType, this.outputType, to); if (fromTx.toRecipient) { try { const fromTxPayload = { to: fromTx.toRecipient, toBytes: this.toChain.addressToBytes(fromTx.toRecipient), payload: fromTx.toPayload ? utils_1.utils.fromBase64(fromTx.toPayload) : new Uint8Array([]), }; if (payload) { if (payload.to !== fromTxPayload.to) { this._config.logger.warn(`Expected recipient to be ${fromTxPayload.to}, instead got ${payload.to}.`); } if (utils_1.utils.toBase64(payload.payload) !== utils_1.utils.toBase64(fromTxPayload.payload)) { this._config.logger.warn(`Expected payload to be ${utils_1.utils.toBase64(fromTxPayload.payload)}, instead got ${utils_1.utils.toBase64(payload.payload)}.`); } if (utils_1.utils.toBase64(payload.toBytes) !== utils_1.utils.toBase64(fromTxPayload.toBytes)) { this._config.logger.warn(`Expected decoded recipient to be ${utils_1.utils.toBase64(fromTxPayload.toBytes)}, instead got ${utils_1.utils.toBase64(payload.toBytes)}.`); } } else { payload = fromTxPayload; } } catch (error) { if (!payload) { throw utils_1.ErrorWithCode.updateError(error, utils_1.RenJSError.PARAMETER_ERROR, `No target payload provided.`); } } } if (!payload) { throw new utils_1.ErrorWithCode(`No target payload provided.`, utils_1.RenJSError.PARAMETER_ERROR); } const sHash = (0, utils_1.generateSHash)(`${this.params.asset}/to${this.params.to.chain}`); this.pHash = (0, utils_1.generatePHash)(payload.payload); const nonceBytes = typeof nonce === "string" ? utils_1.utils.fromBase64(nonce) : utils_1.utils.toNBytes(nonce || 0, 32); this.gHash = (0, utils_1.generateGHash)(this.pHash, sHash, payload.toBytes, nonceBytes); const gPubKey = this.params.shard ? utils_1.utils.fromBase64(this.params.shard.gPubKey) : new Uint8Array(); if (!this.in) { this.in = new utils_1.DefaultTxWaiter({ chainTransaction: this.params.fromTx, chain: this.fromChain, target: await this.provider.getConfirmationTarget(this.fromChain.chain), }); } const onSignatureReady = async (txWithStatus) => { this.queryTxResult = txWithStatus; const { tx } = txWithStatus; if (tx.out && tx.out.revert && tx.out.revert !== "") { throw new utils_1.ErrorWithCode(`RenVM transaction reverted: ${tx.out.revert}`, utils_1.RenJSError.RENVM_TRANSACTION_REVERTED); } if (tx.out && tx.out.txid && tx.out.txid.length > 0 && (0, utils_1.isEmptySignature)(tx.out.sig)) { // The transaction has already been submitted. const txid = utils_1.utils.toURLBase64(tx.out.txid); const txindex = tx.out.txindex.toFixed(); const txHash = this.toChain.txHashFromBytes(utils_1.utils.fromBase64(txid)); await this.out.setTransaction({ chain: this.toChain.chain, txid, txindex, txHash, explorerLink: this.toChain.transactionExplorerLink({ txHash, txindex, txid, }) || "", }); } else if ((0, utils_1.isDepositChain)(this.toChain) && (await this.toChain.isDepositAsset(this.params.asset))) { throw new utils_1.ErrorWithCode(`Expected release transaction details in RenVM response.`, utils_1.RenJSError.INTERNAL_ERROR); } }; const network = await this.provider.getNetwork(); this.renVM = new renVMTxSubmitter_1.RenVMCrossChainTxSubmitter(this.provider, this.selector, { txid: utils_1.utils.fromBase64(this.params.fromTx.txid), txindex: new bignumber_js_1.default(this.params.fromTx.txindex), amount: new bignumber_js_1.default(this.params.fromTx.amount), payload: payload.payload, phash: this.pHash, to: payload.to, nonce: nonceBytes, nhash: this.nHash, gpubkey: gPubKey, ghash: this.gHash, }, onSignatureReady, this._config, network); this._hash = this.renVM.tx.hash; this.renVM.eventEmitter.on("progress", (status) => { // Check if status.response's txStatus is outdated. if (this.queryTxResult && this.queryTxResult.txStatus === utils_1.TxStatus.TxStatusDone && status.response && status.response.txStatus !== utils_1.TxStatus.TxStatusDone) { console.warn("RenVM transaction emitted outdated progress event.", this.queryTxResult, status.response); return; } this.queryTxResult = status.response; }); try { await this.renVM.query(); } catch (error) { // Ignore error, possible submitted too soon before RenVM's nodes // have seen the transaction. } const getOutputParams = () => ({ amount: this.queryTxResult && this.queryTxResult.tx.out ? this.queryTxResult.tx.out.amount : undefined, nHash: this.nHash, sHash: utils_1.utils.keccak256(utils_1.utils.fromUTF8String(this.selector)), pHash: this.pHash, sigHash: this.queryTxResult && this.queryTxResult.tx.out ? this.queryTxResult.tx.out.sighash : undefined, signature: this.queryTxResult && this.queryTxResult.tx.out ? this.queryTxResult.tx.out.sig : undefined, }); if ((0, utils_1.isContractChain)(this.toChain) && this.toChain.getOutSetup) { this.outSetup = Object.assign(Object.assign({}, this.outSetup), (await this.toChain.getOutSetup(asset, this.inputType, this.outputType, to, getOutputParams))); } if ((0, utils_1.isDepositChain)(this.toChain) && (await this.toChain.isDepositAsset(this.params.asset))) { this.out = new utils_1.DefaultTxWaiter({ chain: this.toChain, target: 0, }); } else if ((0, utils_1.isContractChain)(this.toChain)) { this.out = await this.toChain.getOutputTx(this.inputType, this.outputType, this.params.asset, this.params.to, getOutputParams, 1); } else { throw new Error(`Error setting 'out' transaction submitter.`); } return this; }; /** PRIVATE METHODS */ this._defaultGetter = (name) => { if (this[name] === undefined) { throw new Error(`Must call 'initialize' before accessing '${name}'.`); } return this[name]; }; this.provider = renVM; this.params = Object.assign({}, params); this._config = Object.assign(Object.assign({}, config_1.defaultRenJSConfig), config); this.fromChain = fromChain; this.toChain = toChain; const nonce = typeof params.nonce === "string" ? utils_1.utils.fromBase64(params.nonce) : utils_1.utils.toNBytes(params.nonce || 0, 32); this.nHash = (0, utils_1.generateNHash)(nonce, utils_1.utils.fromBase64(params.fromTx.txid), params.fromTx.txindex); if (fromTxWaiter) { this.in = new utils_1.TxWaiterProxy(fromTxWaiter, params.fromTx); } else { this.in = undefined; } // TODO: Throw error if this.out is accessed before this.signed(). this.selector = undefined; this.out = undefined; this.pHash = undefined; this.gHash = undefined; this.renVM = undefined; } get hash() { return this._defaultGetter("_hash") || this._hash; } } exports.GatewayTransaction = GatewayTransaction; //# sourceMappingURL=gatewayTransaction.js.map