UNPKG

ptokens-pbtc

Version:

repo holding the code for interacting with pBTC

196 lines (193 loc) 8.42 kB
import Web3 from 'web3'; import Web3PromiEvent from 'web3-core-promievent'; import { NodeSelector } from 'ptokens-node-selector'; import { helpers, constants, eth, eos, algo, btc, redeemFrom } from 'ptokens-utils'; import { DepositAddress } from 'ptokens-deposit-address'; import Web3Utils from 'web3-utils'; const MINIMUM_BTC_REDEEMABLE = 0.00005; class pBTC extends NodeSelector { constructor(_configs) { const { hostBlockchain, hostNetwork, nativeBlockchain, nativeNetwork } = helpers.parseParams(_configs, _configs.nativeBlockchain || constants.blockchains.Bitcoin); super({ pToken: _configs.pToken || constants.pTokens.pBTC, hostBlockchain, hostNetwork, nativeBlockchain, nativeNetwork, defaultNode: _configs.defaultNode }); const { ethPrivateKey, ethProvider, eosPrivateKey, eosRpc, eosSignatureProvider, algoProvider, algoClient } = _configs; if ((ethProvider || ethPrivateKey) && (eosSignatureProvider || eosPrivateKey)) throw new Error('Bad Initialization. Impossible to use both ETH and EOS'); if (ethProvider) this.hostApi = new Web3(ethProvider); if (ethPrivateKey) { const account = this.hostApi.eth.accounts.privateKeyToAccount(eth.addHexPrefix(ethPrivateKey)); this.hostApi.eth.defaultAccount = account.address; this.hostPrivateKey = eth.addHexPrefix(ethPrivateKey); } else { this.hostPrivateKey = null; } if (eosSignatureProvider) { this.hostApi = eos.getApi(null, eosRpc, eosSignatureProvider); } else if (eosPrivateKey && eosRpc) { this.hostApi = eos.getApi(eosPrivateKey, eosRpc, null); this.hostPrivateKey = eosPrivateKey; } else if (!eosSignatureProvider && !eosPrivateKey && eosRpc) { this.hostApi = eos.getApi(null, eosRpc, null); } if (algoProvider) this.hostApi = algoProvider; if (algoClient) this.algoClient = algoClient; this.contractAddress = null; this.decimals = null; } async getDepositAddress(_hostAddress) { const isValidAddress = { [constants.blockchains.Ethereum]: _address => Web3Utils.isAddress(_address), [constants.blockchains.BinanceSmartChain]: _address => Web3Utils.isAddress(_address), [constants.blockchains.Xdai]: _address => Web3Utils.isAddress(_address), [constants.blockchains.Polygon]: _address => Web3Utils.isAddress(_address), [constants.blockchains.Arbitrum]: _address => Web3Utils.isAddress(_address), [constants.blockchains.Eosio]: _address => eos.isValidAccountName(_address), [constants.blockchains.Telos]: _address => eos.isValidAccountName(_address), [constants.blockchains.Algorand]: _address => algo.isValidAddress(_address), [constants.blockchains.Libre]: _address => eos.isValidAccountName(_address) }; if (!isValidAddress[this.hostBlockchain](_hostAddress)) throw new Error('Invalid host account'); const selectedNode = this.selectedNode ? this.selectedNode : await this.select(); if (!selectedNode) throw new Error('No node selected. Impossible to generate a deposit Address.'); const depositAddress = new DepositAddress({ node: selectedNode, nativeBlockchain: this.nativeBlockchain, nativeNetwork: this.nativeNetwork, hostBlockchain: this.hostBlockchain, hostNetwork: this.hostNetwork, hostApi: this.hostApi }); await depositAddress.generate(_hostAddress); return depositAddress; } redeem(_amount, _btcAddress, _options = {}) { const promiEvent = Web3PromiEvent(); const start = async () => { try { const { gas, gasPrice, blocksBehind, expireSeconds, permission, actor, from, swapInfo } = _options; await this._loadData(); if (_amount < MINIMUM_BTC_REDEEMABLE) { promiEvent.reject(`Impossible to burn less than ${MINIMUM_BTC_REDEEMABLE} pBTC`); return; } if (this.pToken === constants.pTokens.pBTC) { if (!btc.isValidAddress(_btcAddress)) { promiEvent.reject('Invalid Bitcoin address'); return; } } if (!this.selectedNode) await this.select(); const { redeemFromEvmCompatible, redeemFromEosio, redeemFromAlgorand } = redeemFrom; const destinationChainId = this.version === 'v2' ? constants.chainIds[this.nativeBlockchain][this.nativeNetwork] : null; let hostTxHash = null; if (this.hostBlockchain === constants.blockchains.Ethereum || this.hostBlockchain === constants.blockchains.BinanceSmartChain || this.hostBlockchain === constants.blockchains.Xdai || this.hostBlockchain === constants.blockchains.Polygon || this.hostBlockchain === constants.blockchains.Arbitrum) { const hostTxReceipt = await redeemFromEvmCompatible(this.hostApi, { privateKey: this.hostPrivateKey, gas, gasPrice, contractAddress: this.contractAddress, value: 0 }, this.version === 'v1' ? [_amount, _btcAddress] : [_amount, _btcAddress, destinationChainId], promiEvent, 'hostTxBroadcasted', this.version || 'v1'); if (this.hostBlockchain === constants.blockchains.Ethereum) promiEvent.eventEmitter.emit('onEthTxConfirmed', hostTxReceipt); promiEvent.eventEmitter.emit('hostTxConfirmed', hostTxReceipt); hostTxHash = hostTxReceipt.transactionHash; } if (this.hostBlockchain === constants.blockchains.Eosio || this.hostBlockchain === constants.blockchains.Telos || this.hostBlockchain === constants.blockchains.Libre) { const eosTxReceipt = await redeemFromEosio(this.hostApi, _amount, _btcAddress, this.version === 'v2' ? 9 : 8, this.contractAddress, constants.pTokens.pBTC, { blocksBehind, expireSeconds, permission, actor, version: this.version, destinationChainId: destinationChainId }); if (this.hostBlockchain === constants.blockchains.Eosio) { promiEvent.eventEmitter.emit('onEosTxConfirmed', eosTxReceipt); } promiEvent.eventEmitter.emit('hostTxConfirmed', eosTxReceipt); hostTxHash = eosTxReceipt.transaction_id; } if (this.hostBlockchain === constants.blockchains.Algorand) { const hostTxReceipt = await redeemFromAlgorand({ provider: this.hostApi, client: this.algoClient, amount: _amount, to: this.hostIdentity, assetIndex: this.contractAddress, nativeAccount: _btcAddress, from, destinationChainId, eventEmitter: promiEvent.eventEmitter, swapInfo }); promiEvent.eventEmitter.emit('hostTxConfirmed', hostTxReceipt); hostTxHash = hostTxReceipt.txID().toString(); } const broadcastedBtcTxReport = await this.selectedNode.monitorIncomingTransaction(hostTxHash, promiEvent.eventEmitter); const broadcastedBtcTxReceipt = await btc.waitForTransactionConfirmation(this.nativeNetwork, broadcastedBtcTxReport.broadcast_tx_hash, 5000); promiEvent.eventEmitter.emit('onBtcTxConfirmed', broadcastedBtcTxReceipt); promiEvent.eventEmitter.emit('nativeTxConfirmed', broadcastedBtcTxReceipt); promiEvent.resolve({ amount: _amount, hostTx: hostTxHash, nativeTx: broadcastedBtcTxReport.broadcast_tx_hash, to: _btcAddress }); } catch (err) { promiEvent.reject(err); } }; start(); return promiEvent.eventEmitter; } async _loadData() { try { if (!this.contractAddress) { if (!this.selectedNode) await this.select(); const { host_identity, smart_contract_address, host_smart_contract_address, versions } = await this.selectedNode.getInfo(); this.contractAddress = smart_contract_address || host_smart_contract_address; this.version = versions && versions.network ? versions.network : 'v1'; this.hostIdentity = host_identity; } } catch (_err) { throw new Error(`Error during getting contract address: ${_err.message}`); } } } export { pBTC };