@thorwallet/xchain-ethereum
Version:
Ethereum client for XChainJS
1,709 lines (1,621 loc) • 726 kB
JavaScript
import { BigNumber as BigNumber$1, ethers } from 'ethers';
import { getDefaultProvider, EtherscanProvider } from '@ethersproject/providers';
import { parseUnits, hexDataSlice as hexDataSlice$1, keccak256 as keccak256$1, computeAddress as computeAddress$2, toUtf8Bytes as toUtf8Bytes$1 } from 'ethers/lib/utils';
import { baseAmount, assetToString, AssetETH, assetFromString, ETHChain, assetToBase, assetAmount } from '@thorwallet/xchain-util';
import { bip32, validatePhrase } from '@thorwallet/xchain-crypto';
import axios from 'axios';
import { bnOrZero } from '@thorwallet/xchain-util/lib';
import hexLite from 'hex-lite';
import { NativeModules } from 'react-native';
import buffer from 'buffer';
import { toUtf8Bytes, toUtf8String, UnicodeNormalizationForm } from '@ethersproject/strings';
import { Provider } from '@ethersproject/abstract-provider';
var Network;
(function (Network) {
Network["TEST"] = "ropsten";
Network["MAIN"] = "homestead";
})(Network || (Network = {}));
/*! *****************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
function __awaiter(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());
});
}
var erc20ABI = [
{
inputs: [
],
stateMutability: "nonpayable",
type: "constructor"
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "address",
name: "owner",
type: "address"
},
{
indexed: true,
internalType: "address",
name: "spender",
type: "address"
},
{
indexed: false,
internalType: "uint256",
name: "value",
type: "uint256"
}
],
name: "Approval",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "address",
name: "from",
type: "address"
},
{
indexed: true,
internalType: "address",
name: "to",
type: "address"
},
{
indexed: false,
internalType: "uint256",
name: "value",
type: "uint256"
}
],
name: "Transfer",
type: "event"
},
{
inputs: [
{
internalType: "address",
name: "",
type: "address"
},
{
internalType: "address",
name: "",
type: "address"
}
],
name: "allowance",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [
{
internalType: "address",
name: "spender",
type: "address"
},
{
internalType: "uint256",
name: "value",
type: "uint256"
}
],
name: "approve",
outputs: [
{
internalType: "bool",
name: "success",
type: "bool"
}
],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "address",
name: "",
type: "address"
}
],
name: "balanceOf",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [
],
name: "decimals",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [
],
name: "name",
outputs: [
{
internalType: "string",
name: "",
type: "string"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [
],
name: "symbol",
outputs: [
{
internalType: "string",
name: "",
type: "string"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [
],
name: "totalSupply",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [
{
internalType: "address",
name: "to",
type: "address"
},
{
internalType: "uint256",
name: "value",
type: "uint256"
}
],
name: "transfer",
outputs: [
{
internalType: "bool",
name: "success",
type: "bool"
}
],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "address",
name: "from",
type: "address"
},
{
internalType: "address",
name: "to",
type: "address"
},
{
internalType: "uint256",
name: "value",
type: "uint256"
}
],
name: "transferFrom",
outputs: [
{
internalType: "bool",
name: "success",
type: "bool"
}
],
stateMutability: "nonpayable",
type: "function"
}
];
class AbortError extends Error {
constructor() {
super('Throttled function aborted');
this.name = 'AbortError';
}
}
const pThrottle = ({limit, interval, strict}) => {
if (!Number.isFinite(limit)) {
throw new TypeError('Expected `limit` to be a finite number');
}
if (!Number.isFinite(interval)) {
throw new TypeError('Expected `interval` to be a finite number');
}
const queue = new Map();
let currentTick = 0;
let activeCount = 0;
function windowedDelay() {
const now = Date.now();
if ((now - currentTick) > interval) {
activeCount = 1;
currentTick = now;
return 0;
}
if (activeCount < limit) {
activeCount++;
} else {
currentTick += interval;
activeCount = 1;
}
return currentTick - now;
}
const strictTicks = [];
function strictDelay() {
const now = Date.now();
if (strictTicks.length < limit) {
strictTicks.push(now);
return 0;
}
const earliestTime = strictTicks.shift() + interval;
if (now >= earliestTime) {
strictTicks.push(now);
return 0;
}
strictTicks.push(earliestTime);
return earliestTime - now;
}
const getDelay = strict ? strictDelay : windowedDelay;
return function_ => {
const throttled = function (...args) {
if (!throttled.isEnabled) {
return (async () => function_.apply(this, args))();
}
let timeout;
return new Promise((resolve, reject) => {
const execute = () => {
resolve(function_.apply(this, args));
queue.delete(timeout);
};
timeout = setTimeout(execute, getDelay());
queue.set(timeout, reject);
});
};
throttled.abort = () => {
for (const timeout of queue.keys()) {
clearTimeout(timeout);
queue.get(timeout)(new AbortError());
}
queue.clear();
strictTicks.splice(0, strictTicks.length);
};
throttled.isEnabled = true;
return throttled;
};
};
var pThrottle_1 = pThrottle;
var AbortError_1 = AbortError;
pThrottle_1.AbortError = AbortError_1;
/**
* Get address information.
*
* @see https://github.com/EverexIO/Ethplorer/wiki/Ethplorer-API#get-address-info
*
* @param {string} baseUrl The ethplorer api url.
* @param {string} address
* @param {string} apiKey The ethplorer API key. (optional)
* @returns {AddressInfo} The address information.
*/
const getAddress = (baseUrl, address, apiKey) => __awaiter(void 0, void 0, void 0, function* () {
try {
const response = yield axios.get(`${baseUrl}/getAddressInfo/${address}`, {
params: {
apiKey: apiKey || 'freekey',
},
});
return response.data;
}
catch (error) {
return Promise.reject(error);
}
});
/**
* Get transaction by hash.
*
* @see https://github.com/EverexIO/Ethplorer/wiki/Ethplorer-API#get-transaction-info
*
* @param {string} baseUrl The ethplorer api url.
* @param {string} hash The transaction hash.
* @param {string} apiKey The ethplorer API key. (optional)
* @returns {Transactions} The transaction result.
*/
const getTxInfo = (baseUrl, hash, apiKey) => __awaiter(void 0, void 0, void 0, function* () {
try {
const response = yield axios.get(`${baseUrl}/getTxInfo/${hash}`, {
params: {
apiKey: apiKey || 'freekey',
},
});
return response.data;
}
catch (error) {
return Promise.reject(error);
}
});
const ETH_DECIMAL = 18;
// from https://github.com/MetaMask/metamask-extension/blob/ee205b893fe61dc4736efc576e0663189a9d23da/ui/app/pages/send/send.constants.js#L39
// and based on recommendations of https://ethgasstation.info/blog/gas-limit/
const SIMPLE_GAS_COST = BigNumber$1.from(21000);
const BASE_TOKEN_GAS_COST = BigNumber$1.from(100000);
// default gas price in gwei
const DEFAULT_GAS_PRICE = 50;
const ETHAddress = '0x0000000000000000000000000000000000000000';
const MAX_APPROVAL = BigNumber$1.from(2).pow(256).sub(1);
/**
* XChainNetwork -> EthNetwork
*
* @param {XChainNetwork} network
* @returns {EthNetwork}
*/
const xchainNetworkToEths = (network) => {
switch (network) {
// DO NOT use switch/case's default branch
// to be sure that ALL possible cases are
// processed in a similar way to reverted ethNetworkToXchains
case 'mainnet':
return Network.MAIN;
case 'testnet':
return Network.TEST;
}
};
/**
* EthNetwork -> XChainNetwork
*
* @param {EthNetwork} network
* @returns {XChainNetwork}
*/
const ethNetworkToXchains = (network) => {
switch (network) {
// DO NOT use switch/case's default branch
// to be sure that ALL possible cases are
// processed in a similar way to reverted xchainNetworkToEths
case Network.MAIN:
return 'mainnet';
case Network.TEST:
return 'testnet';
}
};
/**
* Validate the given address.
*
* @param {Address} address
* @returns {boolean} `true` or `false`
*/
const validateAddress = (address) => {
try {
ethers.utils.getAddress(address);
return true;
}
catch (error) {
return false;
}
};
/**
* Get token address from asset.
*
* @param {Asset} asset
* @returns {string|null} The token address.
*/
const getTokenAddress = (asset) => {
try {
// strip 0X only - 0x is still valid
return ethers.utils.getAddress(asset.symbol.slice(asset.ticker.length + 1).replace(/^0X/, ''));
}
catch (err) {
return null;
}
};
/**
* Check if the symbol is valid.
*
* @param {string|null|undefined} symbol
* @returns {boolean} `true` or `false`.
*/
const validateSymbol = (symbol) => (symbol ? symbol.length >= 3 : false);
/**
* Get transactions from token tx
*
* @param {TokenTransactionInfo} tx
* @returns {Tx|null} The parsed transaction.
*/
const getTxFromTokenTransaction = (tx) => {
const decimals = parseInt(tx.tokenDecimal) || ETH_DECIMAL;
const symbol = tx.tokenSymbol;
const address = tx.contractAddress;
if (validateSymbol(symbol) && validateAddress(address)) {
const tokenAsset = assetFromString(`${ETHChain}.${symbol}-${address}`);
if (tokenAsset) {
return {
asset: tokenAsset,
from: [
{
from: tx.from,
amount: baseAmount(tx.value, decimals),
},
],
to: [
{
to: tx.to,
amount: baseAmount(tx.value, decimals),
},
],
date: new Date(parseInt(tx.timeStamp) * 1000),
type: 'transfer',
hash: tx.hash,
ethTokenSymbol: tx.tokenSymbol,
ethTokenName: tx.tokenName,
ethGasPrice: tx.gasPrice,
ethGas: tx.gas,
ethGasUsed: tx.gasUsed,
ethCumulativeGasUsed: tx.cumulativeGasUsed,
confirmations: Number(tx.confirmations),
binanceFee: null,
memo: null,
};
}
}
return null;
};
/**
* Get transactions from ETH transaction
*
* @param {ETHTransactionInfo} tx
* @returns {Tx} The parsed transaction.
*/
const getTxFromEthTransaction = (tx) => {
return {
asset: AssetETH,
from: [
{
from: tx.from,
amount: baseAmount(tx.value, ETH_DECIMAL),
},
],
to: [
{
to: tx.to,
amount: baseAmount(tx.value, ETH_DECIMAL),
},
],
date: new Date(parseInt(tx.timeStamp) * 1000),
type: 'transfer',
hash: tx.hash,
confirmations: null,
binanceFee: null,
ethCumulativeGasUsed: null,
ethGasUsed: tx.gasUsed,
ethGas: tx.gas,
ethGasPrice: tx.gasPrice,
ethTokenName: null,
ethTokenSymbol: null,
memo: null,
};
};
/**
* Get transactions from operation
*
* @param {TransactionOperation} operation
* @returns {Tx|null} The parsed transaction.
*/
const getTxFromEthplorerTokenOperation = (operation) => {
const decimals = parseInt(operation.tokenInfo.decimals) || ETH_DECIMAL;
const { symbol, address } = operation.tokenInfo;
if (validateSymbol(symbol) && validateAddress(address)) {
const tokenAsset = assetFromString(`${ETHChain}.${symbol}-${address}`);
if (tokenAsset) {
return {
asset: tokenAsset,
from: [
{
from: operation.from,
amount: baseAmount(operation.value, decimals),
},
],
to: [
{
to: operation.to,
amount: baseAmount(operation.value, decimals),
},
],
date: new Date(operation.timestamp * 1000),
type: operation.type === 'transfer' ? 'transfer' : 'unknown',
hash: operation.transactionHash,
confirmations: null,
binanceFee: null,
ethCumulativeGasUsed: null,
ethGas: null,
ethGasPrice: null,
ethGasUsed: null,
ethTokenName: null,
ethTokenSymbol: null,
memo: null,
};
}
}
return null;
};
/**
* Get transactions from ETH transaction
*
* @param {TransactionInfo} txInfo
* @returns {Tx} The parsed transaction.
*/
const getTxFromEthplorerEthTransaction = (txInfo) => {
var _a;
return {
asset: AssetETH,
from: [
{
from: txInfo.from,
amount: assetToBase(assetAmount(txInfo.value, ETH_DECIMAL)),
},
],
to: [
{
to: txInfo.to,
amount: assetToBase(assetAmount(txInfo.value, ETH_DECIMAL)),
},
],
date: new Date(txInfo.timestamp * 1000),
type: 'transfer',
hash: txInfo.hash,
confirmations: (_a = txInfo.confirmations) !== null && _a !== void 0 ? _a : null,
binanceFee: null,
ethCumulativeGasUsed: null,
ethGas: null,
ethGasUsed: String(txInfo.gasUsed),
ethGasPrice: null,
ethTokenName: null,
ethTokenSymbol: null,
memo: null,
};
};
/**
* Calculate fees by multiplying .
*
* @returns {Fees} The default gas price.
*/
const getFee = ({ gasPrice, gasLimit }) => baseAmount(gasPrice.amount().multipliedBy(gasLimit.toString()), ETH_DECIMAL);
const estimateDefaultFeesWithGasPricesAndLimits = (asset) => {
const gasPrices = {
average: baseAmount(parseUnits(DEFAULT_GAS_PRICE.toString(), 'gwei').toString(), ETH_DECIMAL),
fast: baseAmount(parseUnits((DEFAULT_GAS_PRICE * 2).toString(), 'gwei').toString(), ETH_DECIMAL),
fastest: baseAmount(parseUnits((DEFAULT_GAS_PRICE * 3).toString(), 'gwei').toString(), ETH_DECIMAL),
};
const { fast: fastGP, fastest: fastestGP, average: averageGP } = gasPrices;
let assetAddress;
if (asset && assetToString(asset) !== assetToString(AssetETH)) {
assetAddress = getTokenAddress(asset);
}
let gasLimit;
if (assetAddress && assetAddress !== ETHAddress) {
gasLimit = BigNumber$1.from(BASE_TOKEN_GAS_COST);
}
else {
gasLimit = BigNumber$1.from(SIMPLE_GAS_COST);
}
return {
gasPrices,
gasLimit,
fees: {
type: 'byte',
average: getFee({ gasPrice: averageGP, gasLimit }),
fast: getFee({ gasPrice: fastGP, gasLimit }),
fastest: getFee({ gasPrice: fastestGP, gasLimit }),
},
};
};
/**
* Get the default fees.
*
* @returns {Fees} The default gas price.
*/
const getDefaultFees = (asset) => {
const { fees } = estimateDefaultFeesWithGasPricesAndLimits(asset);
return fees;
};
/**
* Get the default gas price.
*
* @returns {Fees} The default gas prices.
*/
const getDefaultGasPrices = (asset) => {
const { gasPrices } = estimateDefaultFeesWithGasPricesAndLimits(asset);
return gasPrices;
};
/**
* Get address prefix based on the network.
*
* @returns {string} The address prefix based on the network.
*
**/
const getPrefix = () => '0x';
/**
* Get Decimals
*
* @param {Asset} asset
* @returns {Number} the decimal of a given asset
*
* @throws {"Invalid asset"} Thrown if the given asset is invalid
* @throws {"Invalid provider"} Thrown if the given provider is invalid
*/
const getDecimal = (asset, provider) => __awaiter(void 0, void 0, void 0, function* () {
if (assetToString(asset) === assetToString(AssetETH)) {
return Promise.resolve(ETH_DECIMAL);
}
const assetAddress = getTokenAddress(asset);
if (!assetAddress) {
throw new Error(`Invalid asset ${assetToString(asset)}`);
}
try {
const contract = new ethers.Contract(assetAddress, erc20ABI, provider);
const decimal = yield contract.decimals();
return ethers.BigNumber.from(decimal).toNumber();
}
catch (err) {
throw new Error(`Invalid provider: ${err}`);
}
});
/**
* Get Token Balances
*
* @param {Array<TokenBalance>} tokenBalances
* @returns {Array<Balance>} the parsed balances
*
*/
const getTokenBalances = (tokenBalances) => {
return tokenBalances.reduce((acc, cur) => {
var _a;
const { symbol, address: tokenAddress } = cur.tokenInfo;
if (validateSymbol(symbol) && validateAddress(tokenAddress) && ((_a = cur === null || cur === void 0 ? void 0 : cur.tokenInfo) === null || _a === void 0 ? void 0 : _a.decimals) !== undefined) {
const decimals = parseInt(cur.tokenInfo.decimals, 10);
const tokenAsset = assetFromString(`${ETHChain}.${symbol}-${ethers.utils.getAddress(tokenAddress)}`);
if (tokenAsset) {
return [
...acc,
{
asset: tokenAsset,
amount: baseAmount(cur.balance, decimals),
},
];
}
}
return acc;
}, []);
};
const getApiKeyQueryParameter = (apiKey) => (!!apiKey ? `&apiKey=${apiKey}` : '');
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const getAxiosWithRateLimitHandling = (url) => __awaiter(void 0, void 0, void 0, function* () {
const response = yield axios.get(url);
if (JSON.stringify(response.data).includes('Max rate limit reached')) {
console.log('reached rate limit for', url, 'waiting 2s then trying again...');
yield new Promise((resolve) => setTimeout(resolve, 2000));
return getAxiosWithRateLimitHandling(url);
}
return response;
});
/**
* SafeGasPrice, ProposeGasPrice And FastGasPrice returned in string-Gwei
*
* @see https://etherscan.io/apis#gastracker
*
* @param {string} baseUrl The etherscan node url.
* @param {string} apiKey The etherscan API key. (optional)
* @returns {GasOracleResponse} LastBlock, SafeGasPrice, ProposeGasPrice, FastGasPrice
*/
const getGasOracle = (baseUrl, apiKey) => {
const url = baseUrl + '/api?module=gastracker&action=gasoracle';
return getAxiosWithRateLimitHandling(url + getApiKeyQueryParameter(apiKey)).then((response) => response.data.result);
};
/**
* Get token balance
*
* @see https://etherscan.io/apis#tokens
*
* @param {string} baseUrl The etherscan node url.
* @param {string} address The address.
* @param {string} assetAddress The token contract address.
* @param {string} apiKey The etherscan API key. (optional)
* @returns {BigNumberish} The token balance
*/
const getTokenBalance = ({ baseUrl, address, assetAddress, apiKey, }) => __awaiter(void 0, void 0, void 0, function* () {
const url = baseUrl + `/api?module=account&action=tokenbalance&contractaddress=${assetAddress}&address=${address}`;
const response = yield getAxiosWithRateLimitHandling(url + getApiKeyQueryParameter(apiKey));
return response.data.result;
});
/**
* Get ETH transaction history
*
* @see https://etherscan.io/apis#accounts
*
* @param {string} baseUrl The etherscan node url.
* @param {string} address The address.
* @param {TransactionHistoryParam} params The search options.
* @param {string} apiKey The etherscan API key. (optional)
* @returns {Array<ETHTransactionInfo>} The ETH transaction history
*/
const getETHTransactionHistory = ({ baseUrl, address, page, offset, startblock, endblock, apiKey, }) => __awaiter(void 0, void 0, void 0, function* () {
let url = baseUrl + `/api?module=account&action=txlist&sort=desc` + getApiKeyQueryParameter(apiKey);
if (address)
url += `&address=${address}`;
if (offset)
url += `&offset=${offset}`;
if (page)
url += `&page=${page}`;
if (startblock)
url += `&startblock=${startblock}`;
if (endblock)
url += `&endblock=${endblock}`;
try {
const result = yield getAxiosWithRateLimitHandling(url).then((response) => response.data.result);
if (JSON.stringify(result).includes('Invalid API Key')) {
return Promise.reject(new Error('Invalid API Key'));
}
if (typeof result !== typeof []) {
throw new Error(JSON.stringify(result));
}
return result.filter((tx) => !bnOrZero(tx.value).isZero()).map(getTxFromEthTransaction);
}
catch (error) {
return Promise.reject(error);
}
});
/**
* Get token transaction history
*
* @see https://etherscan.io/apis#accounts
*
* @param {string} baseUrl The etherscan node url.
* @param {string} address The address.
* @param {TransactionHistoryParam} params The search options.
* @param {string} apiKey The etherscan API key. (optional)
* @returns {Array<Tx>} The token transaction history
*/
const getTokenTransactionHistory = ({ baseUrl, address, assetAddress, page, offset, startblock, endblock, apiKey, }) => __awaiter(void 0, void 0, void 0, function* () {
let url = baseUrl + `/api?module=account&action=tokentx&sort=desc` + getApiKeyQueryParameter(apiKey);
if (address)
url += `&address=${address}`;
if (assetAddress)
url += `&contractaddress=${assetAddress}`;
if (offset)
url += `&offset=${offset}`;
if (page)
url += `&page=${page}`;
if (startblock)
url += `&startblock=${startblock}`;
if (endblock)
url += `&endblock=${endblock}`;
try {
const result = yield getAxiosWithRateLimitHandling(url).then((response) => response.data.result);
if (JSON.stringify(result).includes('Invalid API Key')) {
return Promise.reject(new Error('Invalid API Key'));
}
return result
.filter((tx) => !bnOrZero(tx.value).isZero())
.reduce((acc, cur) => {
const tx = getTxFromTokenTransaction(cur);
return tx ? [...acc, tx] : acc;
}, []);
}
catch (error) {
return Promise.reject(error);
}
});
var byteLength_1 = byteLength;
var toByteArray_1 = toByteArray;
var fromByteArray_1 = fromByteArray;
var lookup = [];
var revLookup = [];
var Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array;
var code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
for (var i = 0, len = code.length; i < len; ++i) {
lookup[i] = code[i];
revLookup[code.charCodeAt(i)] = i;
}
// Support decoding URL-safe base64 strings, as Node.js does.
// See: https://en.wikipedia.org/wiki/Base64#URL_applications
revLookup['-'.charCodeAt(0)] = 62;
revLookup['_'.charCodeAt(0)] = 63;
function getLens (b64) {
var len = b64.length;
if (len % 4 > 0) {
throw new Error('Invalid string. Length must be a multiple of 4')
}
// Trim off extra bytes after placeholder bytes are found
// See: https://github.com/beatgammit/base64-js/issues/42
var validLen = b64.indexOf('=');
if (validLen === -1) validLen = len;
var placeHoldersLen = validLen === len
? 0
: 4 - (validLen % 4);
return [validLen, placeHoldersLen]
}
// base64 is 4/3 + up to two characters of the original data
function byteLength (b64) {
var lens = getLens(b64);
var validLen = lens[0];
var placeHoldersLen = lens[1];
return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen
}
function _byteLength (b64, validLen, placeHoldersLen) {
return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen
}
function toByteArray (b64) {
var tmp;
var lens = getLens(b64);
var validLen = lens[0];
var placeHoldersLen = lens[1];
var arr = new Arr(_byteLength(b64, validLen, placeHoldersLen));
var curByte = 0;
// if there are placeholders, only get up to the last complete 4 chars
var len = placeHoldersLen > 0
? validLen - 4
: validLen;
var i;
for (i = 0; i < len; i += 4) {
tmp =
(revLookup[b64.charCodeAt(i)] << 18) |
(revLookup[b64.charCodeAt(i + 1)] << 12) |
(revLookup[b64.charCodeAt(i + 2)] << 6) |
revLookup[b64.charCodeAt(i + 3)];
arr[curByte++] = (tmp >> 16) & 0xFF;
arr[curByte++] = (tmp >> 8) & 0xFF;
arr[curByte++] = tmp & 0xFF;
}
if (placeHoldersLen === 2) {
tmp =
(revLookup[b64.charCodeAt(i)] << 2) |
(revLookup[b64.charCodeAt(i + 1)] >> 4);
arr[curByte++] = tmp & 0xFF;
}
if (placeHoldersLen === 1) {
tmp =
(revLookup[b64.charCodeAt(i)] << 10) |
(revLookup[b64.charCodeAt(i + 1)] << 4) |
(revLookup[b64.charCodeAt(i + 2)] >> 2);
arr[curByte++] = (tmp >> 8) & 0xFF;
arr[curByte++] = tmp & 0xFF;
}
return arr
}
function tripletToBase64 (num) {
return lookup[num >> 18 & 0x3F] +
lookup[num >> 12 & 0x3F] +
lookup[num >> 6 & 0x3F] +
lookup[num & 0x3F]
}
function encodeChunk (uint8, start, end) {
var tmp;
var output = [];
for (var i = start; i < end; i += 3) {
tmp =
((uint8[i] << 16) & 0xFF0000) +
((uint8[i + 1] << 8) & 0xFF00) +
(uint8[i + 2] & 0xFF);
output.push(tripletToBase64(tmp));
}
return output.join('')
}
function fromByteArray (uint8) {
var tmp;
var len = uint8.length;
var extraBytes = len % 3; // if we have 1 byte left, pad 2 bytes
var parts = [];
var maxChunkLength = 16383; // must be multiple of 3
// go through the array every three bytes, we'll deal with trailing stuff later
for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) {
parts.push(encodeChunk(uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength)));
}
// pad the end with zeros, but make sure to not forget the extra bytes
if (extraBytes === 1) {
tmp = uint8[len - 1];
parts.push(
lookup[tmp >> 2] +
lookup[(tmp << 4) & 0x3F] +
'=='
);
} else if (extraBytes === 2) {
tmp = (uint8[len - 2] << 8) + uint8[len - 1];
parts.push(
lookup[tmp >> 10] +
lookup[(tmp >> 4) & 0x3F] +
lookup[(tmp << 2) & 0x3F] +
'='
);
}
return parts.join('')
}
var base64Js = {
byteLength: byteLength_1,
toByteArray: toByteArray_1,
fromByteArray: fromByteArray_1
};
function convertArrayBufferToUtf8(arrayBuffer) {
const array = new Uint8Array(arrayBuffer);
const chars = [];
let i = 0;
while (i < array.length) {
const byte = array[i];
if (byte < 128) {
chars.push(String.fromCharCode(byte));
i++;
} else if (byte > 191 && byte < 224) {
chars.push(
String.fromCharCode(((byte & 0x1f) << 6) | (array[i + 1] & 0x3f))
);
i += 2;
} else {
chars.push(
String.fromCharCode(
((byte & 0x0f) << 12) |
((array[i + 1] & 0x3f) << 6) |
(array[i + 2] & 0x3f)
)
);
i += 3;
}
}
return chars.join('');
}
function convertUtf8ToArrayBuffer(utf8) {
const bytes = [];
let i = 0;
utf8 = encodeURI(utf8);
while (i < utf8.length) {
const byte = utf8.charCodeAt(i++);
if (byte === 37) {
bytes.push(parseInt(utf8.substr(i, 2), 16));
i += 2;
} else {
bytes.push(byte);
}
}
const array = new Uint8Array(bytes);
return array.buffer;
}
function convertArrayBufferToBase64(arrayBuffer) {
return base64Js.fromByteArray(new Uint8Array(arrayBuffer));
}
function convertBase64ToArrayBuffer(base64) {
return base64Js.toByteArray(base64).buffer;
}
const convertArrayBufferToHex = hexLite.fromBuffer;
const convertHexToArrayBuffer = hexLite.toBuffer;
async function randomBytes(length) {
return convertBase64ToArrayBuffer(await NativeModules.RNSCRandomBytes.randomBytes(length));
}
async function SHAWrapper(data, algorithm) {
if (typeof data === 'string') {
return NativeModules.RNSCSha.shaUtf8(data, algorithm);
} else {
const dataBase64 = convertArrayBufferToBase64(data);
const result = await NativeModules.RNSCSha.shaBase64(dataBase64, algorithm);
return convertBase64ToArrayBuffer(result);
}
}
const AES = {
encrypt: async function (textArrayBuffer, keyArrayBuffer, ivArrayBuffer) {
const textBase64 = convertArrayBufferToBase64(textArrayBuffer);
const keyHex = convertArrayBufferToHex(keyArrayBuffer);
const ivHex = convertArrayBufferToHex(ivArrayBuffer);
return convertBase64ToArrayBuffer(await NativeModules.RNSCAes.encrypt(textBase64, keyHex, ivHex));
},
decrypt: async function (cipherTextArrayBuffer, keyArrayBuffer, ivArrayBuffer) {
const cipherTextBase64 = convertArrayBufferToBase64(cipherTextArrayBuffer);
const keyHex = convertArrayBufferToHex(keyArrayBuffer);
const ivHex = convertArrayBufferToHex(ivArrayBuffer);
return convertBase64ToArrayBuffer(await NativeModules.RNSCAes.decrypt(cipherTextBase64, keyHex, ivHex));
}
};
const SHA = {
sha1: data => SHAWrapper(data, 'SHA-1'),
sha256: data => SHAWrapper(data, 'SHA-256'),
sha512: data => SHAWrapper(data, 'SHA-512')
};
const HMAC = {
hmac256: async function (textArrayBuffer, keyArrayBuffer) {
const textHex = convertArrayBufferToHex(textArrayBuffer);
const keyHex = convertArrayBufferToHex(keyArrayBuffer);
const signatureHex = await NativeModules.RNSCHmac.hmac256(textHex, keyHex);
return convertHexToArrayBuffer(signatureHex);
}
};
const PBKDF2 = {
hash: async function (password, salt, iterations, keyLength, algorithm) {
let passwordToHash = password;
let saltToHash = salt;
if (typeof password === 'string') {
passwordToHash = convertUtf8ToArrayBuffer(password);
}
if (typeof salt === 'string') {
saltToHash = convertUtf8ToArrayBuffer(salt);
}
const digest = await NativeModules.RNSCPbkdf2.hash(
convertArrayBufferToBase64(passwordToHash),
convertArrayBufferToBase64(saltToHash),
iterations,
keyLength,
algorithm
);
return convertBase64ToArrayBuffer(digest);
}
};
const RSA = NativeModules.RNSCRsa;
const utils = {
randomBytes,
convertArrayBufferToUtf8,
convertUtf8ToArrayBuffer,
convertArrayBufferToBase64,
convertBase64ToArrayBuffer,
convertArrayBufferToHex,
convertHexToArrayBuffer
};
var RNSimple = {
AES,
SHA,
HMAC,
PBKDF2,
RSA,
utils
};
const version = "logger/5.1.0";
let _permanentCensorErrors = false;
let _censorErrors = false;
const LogLevels = { debug: 1, "default": 2, info: 2, warning: 3, error: 4, off: 5 };
let _logLevel = LogLevels["default"];
let _globalLogger = null;
function _checkNormalize() {
try {
const missing = [];
// Make sure all forms of normalization are supported
["NFD", "NFC", "NFKD", "NFKC"].forEach((form) => {
try {
if ("test".normalize(form) !== "test") {
throw new Error("bad normalize");
}
;
}
catch (error) {
missing.push(form);
}
});
if (missing.length) {
throw new Error("missing " + missing.join(", "));
}
if (String.fromCharCode(0xe9).normalize("NFD") !== String.fromCharCode(0x65, 0x0301)) {
throw new Error("broken implementation");
}
}
catch (error) {
return error.message;
}
return null;
}
const _normalizeError = _checkNormalize();
var LogLevel;
(function (LogLevel) {
LogLevel["DEBUG"] = "DEBUG";
LogLevel["INFO"] = "INFO";
LogLevel["WARNING"] = "WARNING";
LogLevel["ERROR"] = "ERROR";
LogLevel["OFF"] = "OFF";
})(LogLevel || (LogLevel = {}));
var ErrorCode;
(function (ErrorCode) {
///////////////////
// Generic Errors
// Unknown Error
ErrorCode["UNKNOWN_ERROR"] = "UNKNOWN_ERROR";
// Not Implemented
ErrorCode["NOT_IMPLEMENTED"] = "NOT_IMPLEMENTED";
// Unsupported Operation
// - operation
ErrorCode["UNSUPPORTED_OPERATION"] = "UNSUPPORTED_OPERATION";
// Network Error (i.e. Ethereum Network, such as an invalid chain ID)
// - event ("noNetwork" is not re-thrown in provider.ready; otherwise thrown)
ErrorCode["NETWORK_ERROR"] = "NETWORK_ERROR";
// Some sort of bad response from the server
ErrorCode["SERVER_ERROR"] = "SERVER_ERROR";
// Timeout
ErrorCode["TIMEOUT"] = "TIMEOUT";
///////////////////
// Operational Errors
// Buffer Overrun
ErrorCode["BUFFER_OVERRUN"] = "BUFFER_OVERRUN";
// Numeric Fault
// - operation: the operation being executed
// - fault: the reason this faulted
ErrorCode["NUMERIC_FAULT"] = "NUMERIC_FAULT";
///////////////////
// Argument Errors
// Missing new operator to an object
// - name: The name of the class
ErrorCode["MISSING_NEW"] = "MISSING_NEW";
// Invalid argument (e.g. value is incompatible with type) to a function:
// - argument: The argument name that was invalid
// - value: The value of the argument
ErrorCode["INVALID_ARGUMENT"] = "INVALID_ARGUMENT";
// Missing argument to a function:
// - count: The number of arguments received
// - expectedCount: The number of arguments expected
ErrorCode["MISSING_ARGUMENT"] = "MISSING_ARGUMENT";
// Too many arguments
// - count: The number of arguments received
// - expectedCount: The number of arguments expected
ErrorCode["UNEXPECTED_ARGUMENT"] = "UNEXPECTED_ARGUMENT";
///////////////////
// Blockchain Errors
// Call exception
// - transaction: the transaction
// - address?: the contract address
// - args?: The arguments passed into the function
// - method?: The Solidity method signature
// - errorSignature?: The EIP848 error signature
// - errorArgs?: The EIP848 error parameters
// - reason: The reason (only for EIP848 "Error(string)")
ErrorCode["CALL_EXCEPTION"] = "CALL_EXCEPTION";
// Insufficien funds (< value + gasLimit * gasPrice)
// - transaction: the transaction attempted
ErrorCode["INSUFFICIENT_FUNDS"] = "INSUFFICIENT_FUNDS";
// Nonce has already been used
// - transaction: the transaction attempted
ErrorCode["NONCE_EXPIRED"] = "NONCE_EXPIRED";
// The replacement fee for the transaction is too low
// - transaction: the transaction attempted
ErrorCode["REPLACEMENT_UNDERPRICED"] = "REPLACEMENT_UNDERPRICED";
// The gas limit could not be estimated
// - transaction: the transaction passed to estimateGas
ErrorCode["UNPREDICTABLE_GAS_LIMIT"] = "UNPREDICTABLE_GAS_LIMIT";
})(ErrorCode || (ErrorCode = {}));
class Logger {
constructor(version) {
Object.defineProperty(this, "version", {
enumerable: true,
value: version,
writable: false
});
}
_log(logLevel, args) {
const level = logLevel.toLowerCase();
if (LogLevels[level] == null) {
this.throwArgumentError("invalid log level name", "logLevel", logLevel);
}
if (_logLevel > LogLevels[level]) {
return;
}
console.log.apply(console, args);
}
debug(...args) {
this._log(Logger.levels.DEBUG, args);
}
info(...args) {
this._log(Logger.levels.INFO, args);
}
warn(...args) {
this._log(Logger.levels.WARNING, args);
}
makeError(message, code, params) {
// Errors are being censored
if (_censorErrors) {
return this.makeError("censored error", code, {});
}
if (!code) {
code = Logger.errors.UNKNOWN_ERROR;
}
if (!params) {
params = {};
}
const messageDetails = [];
Object.keys(params).forEach((key) => {
try {
messageDetails.push(key + "=" + JSON.stringify(params[key]));
}
catch (error) {
messageDetails.push(key + "=" + JSON.stringify(params[key].toString()));
}
});
messageDetails.push(`code=${code}`);
messageDetails.push(`version=${this.version}`);
const reason = message;
if (messageDetails.length) {
message += " (" + messageDetails.join(", ") + ")";
}
// @TODO: Any??
const error = new Error(message);
error.reason = reason;
error.code = code;
Object.keys(params).forEach(function (key) {
error[key] = params[key];
});
return error;
}
throwError(message, code, params) {
throw this.makeError(message, code, params);
}
throwArgumentError(message, name, value) {
return this.throwError(message, Logger.errors.INVALID_ARGUMENT, {
argument: name,
value: value
});
}
assert(condition, message, code, params) {
if (!!condition) {
return;
}
this.throwError(message, code, params);
}
assertArgument(condition, message, name, value) {
if (!!condition) {
return;
}
this.throwArgumentError(message, name, value);
}
checkNormalize(message) {
if (_normalizeError) {
this.throwError("platform missing String.prototype.normalize", Logger.errors.UNSUPPORTED_OPERATION, {
operation: "String.prototype.normalize", form: _normalizeError
});
}
}
checkSafeUint53(value, message) {
if (typeof (value) !== "number") {
return;
}
if (message == null) {
message = "value not safe";
}
if (value < 0 || value >= 0x1fffffffffffff) {
this.throwError(message, Logger.errors.NUMERIC_FAULT, {
operation: "checkSafeInteger",
fault: "out-of-safe-range",
value: value
});
}
if (value % 1) {
this.throwError(message, Logger.errors.NUMERIC_FAULT, {
operation: "checkSafeInteger",
fault: "non-integer",
value: value
});
}
}
checkArgumentCount(count, expectedCount, message) {
if (message) {
message = ": " + message;
}
else {
message = "";
}
if (count < expectedCount) {
this.throwError("missing argument" + message, Logger.errors.MISSING_ARGUMENT, {
count: count,
expectedCount: expectedCount
});
}
if (count > expectedCount) {
this.throwError("too many arguments" + message, Logger.errors.UNEXPECTED_ARGUMENT, {
count: count,
expectedCount: expectedCount
});
}
}
checkNew(target, kind) {
if (target === Object || target == null) {
this.throwError("missing new", Logger.errors.MISSING_NEW, { name: kind.name });
}
}
checkAbstract(target, kind) {
if (target === kind) {
this.throwError("cannot instantiate abstract class " + JSON.stringify(kind.name) + " directly; use a sub-class", Logger.errors.UNSUPPORTED_OPERATION, { name: target.name, operation: "new" });
}
else if (target === Object || target == null) {
this.throwError("missing new", Logger.errors.MISSING_NEW, { name: kind.name });
}
}
static globalLogger() {
if (!_globalLogger) {
_globalLogger = new Logger(version);
}
return _globalLogger;
}
static setCensorship(censorship, permanent) {
if (!censorship && permanent) {
this.globalLogger().throwError("cannot permanently disable censorship", Logger.errors.UNSUPPORTED_OPERATION, {
operation: "setCensorship"
});
}
if (_permanentCensorErrors) {
if (!censorship) {
return;
}
this.globalLogger().throwError("error censorship permanent", Logger.errors.UNSUPPORTED_OPERATION, {
operation: "setCensorship"
});
}
_censorErrors = !!censorship;
_permanentCensorErrors = !!permanent;
}
static setLogLevel(logLevel) {
const level = LogLevels[logLevel.toLowerCase()];
if (level == null) {
Logger.globalLogger().warn("invalid log level - " + logLevel);
return;
}
_logLevel = level;
}
static from(version) {
return new Logger(version);
}
}
Logger.errors = ErrorCode;
Logger.levels = LogLevel;
const version$1 = "bytes/5.1.0";
const logger = new Logger(version$1);
///////////////////////////////
function isHexable(value) {
return !!(value.toHexString);
}
function addSlice(array) {
if (array.slice) {
return array;
}
array.slice = function () {
const args = Array.prototype.slice.call(arguments);
return addSlice(new Uint8Array(Array.prototype.slice.apply(array, args)));
};
return array;
}
function isBytesLike(value) {
return ((isHexString(value) && !(value.length % 2)) || isBytes(value));
}
function isBytes(value) {
if (value == null) {
return false;
}
if (value.constructor === Uint8Array) {
return true;
}
if (typeof (value) === "string") {
return false;
}
if (value.length == null) {
return false;
}
for (let i = 0; i < value.length; i++) {
const v = value[i];
if (typeof (v) !== "number" || v < 0 || v >= 256 || (v % 1)) {
return false;
}
}
return true;
}
function arrayify(value, options) {
if (!options) {
options = {};
}
if (typeof (value) === "number") {
logger.checkSafeUint53(value, "invalid arrayify value");
const result = [];
while (value) {
result.unshift(value & 0xff);
value = parseInt(String(value / 256));
}
if (result.length === 0) {
result.push(0);
}
return addSlice(new Uint8Array(result));
}
if (options.allowMissingPrefix && typeof (value) === "string" && value.substring(0, 2) !== "0x") {
value = "0x" + value;
}
if (isHexable(value)) {
value = value.toHexString();
}
if (isHexString(value)) {
let hex = value.substring(2);
if (hex.length % 2) {
if (options.hexPad === "left") {
hex = "0x0" + hex.substring(2);
}
else if (options.hexPad === "right") {
hex += "0";
}
else {
logger.throwArgumentError("hex data is odd-length", "value", value);
}
}
const result = [];
for (let i = 0; i < hex.length; i += 2) {
result.push(parseInt(hex.substring(i, i + 2), 16));
}
return addSlice(new Uint8Array(result));
}
if (isBytes(value)) {
return addSlice(new Uint8Array(value));
}
return logger.throwArgumentError("invalid arrayify value", "value", value);
}
function concat(items) {
const objects = items.map(item => arrayify(item));
const length = objects.reduce((accum, item) => (accum + item.length), 0);
const result = new Uint8Array(length);
objects.reduce((offset, object) => {
result.set(object, offset);
return offset + object.length;
}, 0);
return addSlice(result);
}
function stripZeros(value) {
let result = arrayify(value);
if (result.length === 0) {
return result;
}
// Find the first non-zero entry
let start = 0;
while (start < result.length && result[start] === 0) {
start++;
}
// If we started with zeros, strip them
if (start) {
result = result.slice(start);
}
return result;
}
function zeroPad(value, length) {
value = arrayify(value);
if (value.length > length) {
logger.throwArgumentError("value out of range", "value", arguments[0]);
}
const result = new Uint8Array(length);
result.set(value, length - value.length);
return addSlice(result);
}
function isHexString(value, length) {
if (typeof (value) !== "string" || !value.match(/^0x[0-9A-Fa-f]*$/)) {
return false;
}
if (length && value.length !== 2 + 2 * length) {
return false;
}
return true;
}
const HexCharacters = "0123456789abcdef";
function hexlify(value, options) {
if (!options) {
options = {};
}
if (typeof (value) === "number") {
logger.checkSafeUint53(value, "invalid hexlify value");
let hex = "";
while (value) {
hex = HexCharacters[value & 0x0f] + hex;
value = Math.floor(value / 16);
}
if (hex.length) {
if (hex.length % 2) {
hex = "0" + hex;
}
return "0x" + hex;
}
return "0x00";
}
if (options.allowMissingPrefix && typeof (value) === "string" && value.substring(0, 2) !== "0x") {
value = "0x" + value;
}
if (isHexable(value)) {
return value.toHexString();
}
if (isHexString(value)) {
if (value.length % 2) {
if (options.hexPad === "left") {
value = "0x0" + value.substring(2);
}
else if (options.hexPad === "right") {
value += "0";
}
else {
logger.throwArgumentError("hex data is odd-length", "value", value);
}
}
return value.toLowerCase();
}
if (isBytes(value)) {
let result = "0x";
for (let i = 0; i < value.length; i++) {
let v = value[i];
result += HexCharacters[(v & 0xf0) >> 4] + HexCharacters[v & 0x0f];
}
return result;
}
return logger.throwArgumentError("invalid hexlify value", "value", value);
}
/*
function unoddify(value: BytesLike | Hexable | number): BytesLike | Hexable | number {
if (typeof(value) === "string" && value.length % 2 && value.substring(0, 2) === "0x") {
return "0x0" + value.substring(2);
}
return value;
}
*/
function hexDataLength(data) {
if (typeof (data) !== "string") {
data = hexlify(data);
}
else if (!isHexString(data) || (data.length % 2)) {
return null;
}
return (data.length - 2) / 2;
}
function hexDataSlice(data, offset, endOffset) {
if (typeof (data) !== "string") {
data = hexlify(data);
}
else if (!isHexString(data) || (data.length % 2)) {
logger.throwArgumentError("invalid hexData", "value", data);
}
offset = 2 + 2 * offset;
if (endOffset != null) {
return "0x" + data.substring(offset, 2 + 2 * endOffset);
}
return "0x" + data.su