ptokens-perc20
Version:
repo holding the code for interacting with Ether or a generic Ethereum ERC20 token
623 lines (618 loc) • 22.7 kB
JavaScript
import Web3 from 'web3';
import Web3PromiEvent from 'web3-core-promievent';
import { NodeSelector } from 'ptokens-node-selector';
import { constants, helpers, eth, eos, algo, abi, redeemFrom } from 'ptokens-utils';
import BigNumber from 'bignumber.js';
import Web3Utils from 'web3-utils';
var minimumAmounts = {
[constants.pTokens.pETH]: {
issue: 1000000000,
redeem: {
[constants.blockchains.Eosio]: 0.000000001,
[constants.blockchains.Telos]: 0.000000001
}
},
[constants.pTokens.pWETH]: {
issue: 1000000000,
redeem: {
[constants.blockchains.Eosio]: 0.000000001,
[constants.blockchains.Telos]: 0.000000001
}
},
[constants.pTokens.PNT]: {
issue: 1000000000,
redeem: {
[constants.blockchains.Eosio]: 0.000000001,
[constants.blockchains.Telos]: 0.000000001,
[constants.blockchains.BinanceSmartChain]: 1000000000,
[constants.blockchains.Xdai]: 1000000000,
[constants.blockchains.Polygon]: 1000000000,
[constants.blockchains.Arbitrum]: 1000000000
}
},
[constants.pTokens.pLINK]: {
issue: 1000000000,
redeem: {
[constants.blockchains.Eosio]: 0.000000001,
[constants.blockchains.Telos]: 0.000000001
}
},
[constants.pTokens.pMKR]: {
issue: 1000000000,
redeem: {
[constants.blockchains.Eosio]: 0.000000001
}
},
[constants.pTokens.pYFI]: {
issue: 1000000000,
redeem: {
[constants.blockchains.Eosio]: 0.000000001
}
},
[constants.pTokens.PTERIA]: {
issue: 1000000000,
redeem: {
[constants.blockchains.Eosio]: 0.000000001,
[constants.blockchains.BinanceSmartChain]: 1000000000
}
},
[constants.pTokens.pUNI]: {
issue: 1000000000,
redeem: {
[constants.blockchains.Eosio]: 0.000000001
}
},
[constants.pTokens.pBAND]: {
issue: 1000000000,
redeem: {
[constants.blockchains.Eosio]: 0.000000001
}
},
[constants.pTokens.PBAL]: {
issue: 1000000000,
redeem: {
[constants.blockchains.Eosio]: 0.000000001
}
},
[constants.pTokens.pCOMP]: {
issue: 1000000000,
redeem: {
[constants.blockchains.Eosio]: 0.000000001
}
},
[constants.pTokens.pSNX]: {
issue: 1000000000,
redeem: {
[constants.blockchains.Eosio]: 0.000000001
}
},
[constants.pTokens.pOMG]: {
issue: 1000000000,
redeem: {
[constants.blockchains.Eosio]: 0.000000001
}
},
[constants.pTokens.pDAI]: {
issue: 1000000000,
redeem: {
[constants.blockchains.Eosio]: 0.000000001
}
},
[constants.pTokens.pANT]: {
issue: 1000000000,
redeem: {
[constants.blockchains.Eosio]: 0.000000001
}
},
[constants.pTokens.pLRC]: {
issue: 1000000000,
redeem: {
[constants.blockchains.Eosio]: 0.000000001
}
},
[constants.pTokens.pUOS]: {
issue: 500000,
redeem: {
[constants.blockchains.Eosio]: 50,
[constants.blockchains.Ultra]: 50
}
},
[constants.pTokens.pBAT]: {
issue: 1000000000,
redeem: {
[constants.blockchains.Eosio]: 0.000000001
}
},
[constants.pTokens.pREP]: {
issue: 1000000000,
redeem: {
[constants.blockchains.Eosio]: 0.000000001
}
},
[constants.pTokens.pZRX]: {
issue: 1000000000,
redeem: {
[constants.blockchains.Eosio]: 0.000000001
}
},
[constants.pTokens.pPNK]: {
issue: 1000000000,
redeem: {
[constants.blockchains.Eosio]: 0.000000001
}
},
[constants.pTokens.pOPIUM]: {
issue: 1000000000,
redeem: {
[constants.blockchains.BinanceSmartChain]: 1000000000
}
},
[constants.pTokens.pDEFIPlusPlus]: {
issue: 1000000000,
redeem: {
[constants.blockchains.BinanceSmartChain]: 1000000000
}
},
[constants.pTokens.pBCP]: {
issue: 1000000000,
redeem: {
[constants.blockchains.BinanceSmartChain]: 1000000000
}
},
[constants.pTokens.CGG]: {
issue: 1000000000,
redeem: {
[constants.blockchains.BinanceSmartChain]: 1000000000
}
},
[constants.pTokens.pUSDC]: {
issue: 5000000,
redeem: {
[constants.blockchains.Telos]: 0.000000001,
[constants.blockchains.Algorand]: 5000000
}
},
[constants.pTokens.pUSDT]: {
issue: 5000000,
redeem: {
[constants.blockchains.Telos]: 0.000000001,
[constants.blockchains.Algorand]: 5000000
}
},
[constants.pTokens.pOPEN]: {
issue: 1000000000,
redeem: {
[constants.blockchains.BinanceSmartChain]: 1000000000
}
},
[constants.pTokens.ANRX]: {
issue: 1000000000,
redeem: {
[constants.blockchains.BinanceSmartChain]: 1000000000
}
},
[constants.pTokens.GALA]: {
issue: 1,
redeem: {
[constants.blockchains.BinanceSmartChain]: 10000000000
}
},
[constants.pTokens.ZMT]: {
issue: 1,
redeem: {
[constants.blockchains.BinanceSmartChain]: 10000000000
}
},
[constants.pTokens.BIST]: {
issue: 10000000000,
redeem: {
[constants.blockchains.BinanceSmartChain]: 10000000000
}
},
[constants.pTokens.LUXO]: {
issue: 10000000000,
redeem: {
[constants.blockchains.Luxochain]: 10000000000,
[constants.blockchains.BinanceSmartChain]: 10000000000
}
},
[constants.pTokens.pKEYS]: {
issue: 1000,
redeem: {
[constants.blockchains.BinanceSmartChain]: 10000000000
}
},
[constants.pTokens.OATH]: {
issue: 500e18,
redeem: {
[constants.blockchains.Ethereum]: 500e18
}
},
[constants.pTokens.pFTM]: {
issue: 100e18,
redeem: {
[constants.blockchains.Ethereum]: 100e18
}
},
[constants.pTokens.pWFTM]: {
issue: 100e18,
redeem: {
[constants.blockchains.Ethereum]: 100e18
}
},
[constants.pTokens.NUCO]: {
issue: 1500e18,
redeem: {
[constants.blockchains.Telos]: 1500
}
}
};
const {
blockchains,
networks,
pTokens
} = constants;
var mapDecimals = {
[pTokens.pDAI]: {
[blockchains.Eosio]: {
[networks.EosioMainnet]: 4
}
},
[pTokens.pUOS]: {
[blockchains.Eosio]: {
[networks.EosioMainnet]: 4
},
[blockchains.Ultra]: {
[networks.UltraTestnet]: 8
}
},
[pTokens.pUSDT]: {
[blockchains.Telos]: {
[networks.TelosMainnet]: 6
},
[blockchains.Libre]: {
[networks.LibreMainnet]: 9
}
},
[pTokens.pUSDC]: {
[blockchains.Telos]: {
[networks.TelosMainnet]: 6
}
}
};
class pERC20 extends NodeSelector {
constructor(_configs) {
const {
hostBlockchain,
hostNetwork,
nativeBlockchain,
nativeNetwork
} = helpers.parseParams(_configs, _configs.nativeBlockchain || constants.blockchains.Ethereum);
super({
pToken: _configs.pToken.toLowerCase() === constants.pTokens.pETH ? 'pweth' : _configs.pToken,
hostBlockchain,
hostNetwork,
nativeBlockchain,
nativeNetwork,
defaultNode: _configs.defaultNode
});
const {
ethPrivateKey,
ethProvider,
bscPrivateKey,
bscProvider,
xdaiPrivateKey,
xdaiProvider,
polygonPrivateKey,
polygonProvider,
eosPrivateKey,
eosRpc,
eosSignatureProvider,
telosPrivateKey,
telosRpc,
telosSignatureProvider,
ultraPrivateKey,
ultraRpc,
ultraSignatureProvider,
arbitrumPrivateKey,
arbitrumProvider,
luxochainPrivateKey,
luxochainProvider,
algoProvider,
algoClient,
ftmPrivateKey,
ftmProvider
} = _configs;
if (ftmProvider) this.web3 = new Web3(ftmProvider);
if (ftmPrivateKey) {
const account = this.web3.eth.accounts.privateKeyToAccount(eth.addHexPrefix(ftmPrivateKey));
this.web3.eth.defaultAccount = account.address;
this.nativePrivateKey = eth.addHexPrefix(ftmPrivateKey);
} else {
this.nativePrivateKey = null;
}
if (nativeBlockchain === constants.blockchains.Fantom && (ethProvider || ethPrivateKey)) {
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;
}
} else if (ethProvider || ethPrivateKey) {
if (ethProvider) this.web3 = new Web3(ethProvider);
if (ethPrivateKey) {
const account = this.web3.eth.accounts.privateKeyToAccount(eth.addHexPrefix(ethPrivateKey));
this.web3.eth.defaultAccount = account.address;
this.nativePrivateKey = eth.addHexPrefix(ethPrivateKey);
} else {
this.nativePrivateKey = null;
}
}
if (bscProvider) this.hostApi = new Web3(bscProvider);
if (bscPrivateKey) {
const account = this.hostApi.eth.accounts.privateKeyToAccount(eth.addHexPrefix(bscPrivateKey));
this.hostApi.eth.defaultAccount = account.address;
this.hostPrivateKey = eth.addHexPrefix(bscPrivateKey);
} else {
this.hostPrivateKey = null;
}
if (xdaiProvider) this.hostApi = new Web3(xdaiProvider);
if (xdaiPrivateKey) {
const account = this.hostApi.eth.accounts.privateKeyToAccount(eth.addHexPrefix(xdaiPrivateKey));
this.hostApi.eth.defaultAccount = account.address;
this.hostPrivateKey = eth.addHexPrefix(xdaiPrivateKey);
} else {
this.hostPrivateKey = null;
}
if (polygonProvider) this.hostApi = new Web3(polygonProvider);
if (polygonPrivateKey) {
const account = this.hostApi.eth.accounts.privateKeyToAccount(eth.addHexPrefix(polygonPrivateKey));
this.hostApi.eth.defaultAccount = account.address;
this.hostPrivateKey = eth.addHexPrefix(polygonPrivateKey);
} 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 (telosSignatureProvider) {
this.hostApi = eos.getApi(null, telosRpc, telosSignatureProvider);
} else if (telosPrivateKey && telosRpc) {
this.hostApi = eos.getApi(telosPrivateKey, telosRpc, null);
this.hostPrivateKey = telosPrivateKey;
} else if (!telosSignatureProvider && !telosPrivateKey && telosRpc) {
this.hostApi = eos.getApi(null, telosRpc, null);
}
if (ultraSignatureProvider) {
this.hostApi = eos.getApi(null, ultraRpc, ultraSignatureProvider);
} else if (ultraPrivateKey && ultraRpc) {
this.hostApi = eos.getApi(ultraPrivateKey, ultraRpc, null);
this.hostPrivateKey = ultraPrivateKey;
} else if (!ultraSignatureProvider && !ultraPrivateKey && ultraRpc) {
this.hostApi = eos.getApi(null, ultraRpc, null);
}
if (arbitrumProvider) this.hostApi = new Web3(arbitrumProvider);
if (arbitrumPrivateKey) {
const account = this.hostApi.eth.accounts.privateKeyToAccount(eth.addHexPrefix(arbitrumPrivateKey));
this.hostApi.eth.defaultAccount = account.address;
this.hostPrivateKey = eth.addHexPrefix(arbitrumPrivateKey);
} else {
this.hostPrivateKey = null;
}
if (luxochainProvider) this.hostApi = new Web3(luxochainProvider);
if (luxochainPrivateKey) {
const account = this.hostApi.eth.accounts.privateKeyToAccount(eth.addHexPrefix(luxochainPrivateKey));
this.hostApi.eth.defaultAccount = account.address;
this.hostPrivateKey = eth.addHexPrefix(luxochainPrivateKey);
} else {
this.hostPrivateKey = null;
}
if (algoProvider) this.hostApi = algoProvider;
if (algoClient) this.algoClient = algoClient;
this._peginEth = _configs.pToken.toLowerCase() === constants.pTokens.pETH || _configs.pToken.toLowerCase() === constants.pTokens.pFTM;
}
issue(_amount, _hostAccount, _options = {}) {
return this._issue(_amount, _hostAccount, null, _options);
}
issueWithMetadata(_amount, _hostAccount, _metadata, _options = {}) {
return this._issue(_amount, _hostAccount, _metadata, _options);
}
_issue(_amount, _hostAccount, _metadata, _options = {}) {
const promiEvent = Web3PromiEvent();
const start = async () => {
try {
const {
gas,
gasPrice
} = _options;
const {
blockchains
} = constants;
await this._loadData();
const minimumAmount = minimumAmounts[this.pToken.toLowerCase()].issue;
if (BigNumber(_amount).isLessThan(minimumAmount)) {
promiEvent.reject(`Impossible to issue less than ${minimumAmount}`);
return;
}
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.Luxochain]: _address => Web3Utils.isAddress(_address),
[constants.blockchains.Eosio]: _address => eos.isValidAccountName(_address),
[constants.blockchains.Telos]: _address => eos.isValidAccountName(_address),
[constants.blockchains.Ultra]: _address => eos.isValidAccountName(_address),
[constants.blockchains.Algorand]: _address => algo.isValidAddress(_address, true),
[constants.blockchains.Libre]: _address => eos.isValidAccountName(_address)
};
if (!isValidAddress[this.hostBlockchain](_hostAccount)) {
promiEvent.reject('Invalid host account');
return;
}
if (!this.selectedNode) await this.select();
const destinationChainId = this.version === 'v2' ? constants.chainIds[this.hostBlockchain][this.hostNetwork] : null;
let ethTxHash = null;
const waitForEthTransaction = () => new Promise((_resolve, _reject) => {
eth[this.nativePrivateKey ? 'sendSignedMethodTx' : 'makeContractSend'](this.web3, this._peginEth ? 'pegInEth' : 'pegIn', {
privateKey: this.nativePrivateKey,
abi: this.version === 'v1' ? abi.pERC20Vault : abi.pERC20VaultV2,
gas,
gasPrice,
contractAddress: eth.addHexPrefix(this.nativeVaultAddress),
value: this._peginEth ? _amount : 0
}, this.version === 'v1' ? _metadata ? this._peginEth ? [_hostAccount, _metadata] : [_amount, this.nativeContractAddress, _hostAccount, _metadata] : this._peginEth ? [_hostAccount] : [_amount, this.nativeContractAddress, _hostAccount] : _metadata ? this._peginEth ? [_hostAccount, destinationChainId, _metadata] : [_amount, this.nativeContractAddress, _hostAccount, _metadata, destinationChainId] : this._peginEth ? [_hostAccount, destinationChainId] : [_amount, this.nativeContractAddress, _hostAccount, destinationChainId]).once('transactionHash', _hash => {
ethTxHash = _hash;
promiEvent.eventEmitter.emit('nativeTxBroadcasted', ethTxHash);
}).once('receipt', _resolve).once('error', _reject);
});
const ethTxReceipt = await waitForEthTransaction();
promiEvent.eventEmitter.emit('nativeTxConfirmed', ethTxReceipt);
const incomingTxReport = await this.selectedNode.monitorIncomingTransaction(ethTxHash, promiEvent.eventEmitter);
let hostTxReceipt;
if (this.hostBlockchain === blockchains.Eosio || this.hostBlockchain === blockchains.Telos || this.hostBlockchain === blockchains.Libre) hostTxReceipt = await eos.waitForTransactionConfirmation(this.hostApi, incomingTxReport.broadcast_tx_hash);
if (this.hostBlockchain === blockchains.Xdai || this.hostBlockchain === blockchains.BinanceSmartChain || this.hostBlockchain === blockchains.Polygon || this.hostBlockchain === blockchains.Arbitrum || this.hostBlockchain === blockchains.Luxochain || this.hostBlockchain === blockchains.Ethereum) hostTxReceipt = await eth.waitForTransactionConfirmation(this.hostApi, incomingTxReport.broadcast_tx_hash);
if (this.hostBlockchain === blockchains.Algorand)
await new Promise(resolve => setTimeout(resolve, 5000));
promiEvent.eventEmitter.emit('hostTxConfirmed', hostTxReceipt);
promiEvent.resolve({
to: _hostAccount,
nativeTx: ethTxHash,
hostTx: incomingTxReport.broadcast_tx_hash,
amount: BigNumber(_amount).toFixed()
});
} catch (_err) {
promiEvent.reject(_err);
}
};
start();
return promiEvent.eventEmitter;
}
redeem(_amount, _nativeAccount, _options = {}) {
return this._redeem(_amount, _nativeAccount, null, _options);
}
redeemWithMetadata(_amount, _nativeAccount, _metadata, _options = {}) {
return this._redeem(_amount, _nativeAccount, _metadata, _options);
}
_redeem(_amount, _nativeAccount, _metadata, _options = {}) {
const promiEvent = Web3PromiEvent();
const start = async () => {
try {
const {
blocksBehind,
expireSeconds,
permission,
gas,
gasPrice,
actor,
from,
swapInfo
} = _options;
const {
blockchains,
pTokens
} = constants;
await this._loadData();
const minimumAmount = minimumAmounts[this.pToken.toLowerCase()].redeem[this.hostBlockchain];
if (BigNumber(_amount).isLessThan(minimumAmount)) {
promiEvent.reject(`Impossible to redeem less than ${minimumAmount}`);
return;
}
if (this.nativeBlockchain === blockchains.Ethereum && !Web3Utils.isAddress(_nativeAccount)) {
promiEvent.reject('Invalid native account');
return;
}
if (!this.selectedNode) await this.select();
const {
redeemFromEosio,
redeemFromEvmCompatible,
redeemFromAlgorand
} = redeemFrom;
const destinationChainId = this.version === 'v2' ? constants.chainIds[this.nativeBlockchain][this.nativeNetwork] : null;
let hostTxHash;
if (this.hostBlockchain === blockchains.BinanceSmartChain || this.hostBlockchain === blockchains.Xdai || this.hostBlockchain === blockchains.Polygon || this.hostBlockchain === blockchains.Arbitrum || this.hostBlockchain === blockchains.Luxochain || this.hostBlockchain === blockchains.Ethereum) {
const hostTxReceipt = await redeemFromEvmCompatible(this.hostApi, {
privateKey: this.hostPrivateKey,
gas,
gasPrice,
contractAddress: this.hostContractAddress,
value: 0
}, this.version === 'v1' ? _metadata ? [_amount, _metadata, _nativeAccount] : [_amount, _nativeAccount] : _metadata ? [_amount, _metadata, _nativeAccount, destinationChainId] : [_amount, _nativeAccount, destinationChainId], promiEvent, 'hostTxBroadcasted', this.version || 'v1');
promiEvent.eventEmitter.emit('hostTxConfirmed', hostTxReceipt);
hostTxHash = hostTxReceipt.transactionHash;
}
if (this.hostBlockchain === blockchains.Eosio || this.hostBlockchain === blockchains.Telos || this.hostBlockchain === blockchains.Ultra || this.hostBlockchain === blockchains.Libre) {
const hostTxReceipt = await redeemFromEosio(this.hostApi, _amount, _nativeAccount, mapDecimals[this.pToken.toLowerCase()] ? mapDecimals[this.pToken.toLowerCase()][this.hostBlockchain][this.hostNetwork] : 9, this.hostContractAddress, this.pToken === pTokens.pWETH ? 'peth' : this.pToken, {
blocksBehind,
expireSeconds,
permission,
actor,
destinationChainId,
version: this.version
});
promiEvent.eventEmitter.emit('hostTxConfirmed', hostTxReceipt);
hostTxHash = hostTxReceipt.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.hostContractAddress,
nativeAccount: _nativeAccount,
from,
destinationChainId,
eventEmitter: promiEvent.eventEmitter,
swapInfo
});
promiEvent.eventEmitter.emit('hostTxConfirmed', hostTxReceipt);
hostTxHash = hostTxReceipt.txID().toString();
}
const incomingTxReport = await this.selectedNode.monitorIncomingTransaction(hostTxHash, promiEvent.eventEmitter);
const nativeTxReceipt = await eth.waitForTransactionConfirmation(this.web3, incomingTxReport.broadcast_tx_hash);
promiEvent.eventEmitter.emit('nativeTxConfirmed', nativeTxReceipt);
promiEvent.resolve({
to: _nativeAccount,
nativeTx: nativeTxReceipt.transactionHash,
hostTx: hostTxHash,
amount: BigNumber(_amount).toFixed()
});
} catch (_err) {
promiEvent.reject(_err);
}
};
start();
return promiEvent.eventEmitter;
}
async _loadData() {
try {
if (!this.selectedNode) await this.select();
if (!this.nativeContractAddress || !this.hostContractAddress) {
const {
native_smart_contract_address,
host_smart_contract_address,
native_vault_address,
versions,
host_identity
} = await this.selectedNode.getInfo();
this.nativeContractAddress = eth.addHexPrefix(native_smart_contract_address);
this.hostContractAddress = this.hostBlockchain === constants.blockchains.Eosio || this.hostBlockchain === constants.blockchains.Telos || this.hostBlockchain === constants.blockchains.Ultra || this.hostBlockchain === constants.blockchains.Algorand || this.hostBlockchain === constants.blockchains.Libre ? host_smart_contract_address : eth.addHexPrefix(host_smart_contract_address);
this.nativeVaultAddress = native_vault_address ? eth.addHexPrefix(native_vault_address) : null;
this.version = versions && versions.network ? versions.network : 'v1';
this.hostIdentity = host_identity;
}
} catch (_err) {
throw new Error(`Error during loading data: ${_err.message}`);
}
}
}
export { pERC20 };