@onekeyfe/blockchain-libs
Version:
OneKey Blockchain Libs
169 lines • 7.98 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.UniswapV3 = exports.UniswapV2 = void 0;
const console = __importStar(require("console"));
const abi_1 = require("@ethersproject/abi");
const bytes_1 = require("@ethersproject/bytes");
const bignumber_js_1 = __importDefault(require("bignumber.js"));
const array_plus_1 = require("../../basic/array-plus");
const bignumber_plus_1 = require("../../basic/bignumber-plus");
const eth_1 = require("../../provider/chains/eth");
const BASE_TOKEN_DECIMALS_MULTIPLY = new bignumber_js_1.default(1).shiftedBy(18);
class ABCUniswap {
constructor(config, providerController) {
this.config = config;
this.providerController = providerController;
}
async pricing(coins) {
const prices = [];
for (const [chainCode, partCoins] of Object.entries((0, array_plus_1.groupBy)(coins, (i) => i.chainCode))) {
if (typeof this.config[chainCode] !== 'object') {
continue;
}
const configPerChain = this.config[chainCode];
const wbtcConfig = configPerChain['wbtc'];
if (typeof wbtcConfig === 'string' && wbtcConfig.includes('@')) {
const [wbtcTokenAddress, wbtcDecimals] = wbtcConfig.split('@');
partCoins.push({
code: 'btc',
chainCode: chainCode,
decimals: parseInt(wbtcDecimals),
tokenAddress: wbtcTokenAddress,
});
}
const baseTokenAddress = configPerChain['base_token_address']; // also called wrapped token address, like WETH, WBNB etc.
const mediaTokenAddresses = configPerChain['media_token_addresses']; // like WBTC, USDT, BUSD etc.
const quotePaths = [];
const refCoins = [];
for (const coin of partCoins) {
if (!coin.tokenAddress) {
continue;
}
else if (coin.tokenAddress === baseTokenAddress) {
prices.push({
coin: coin.code,
value: new bignumber_js_1.default(1),
unit: chainCode,
});
continue;
}
if (mediaTokenAddresses.includes(coin.tokenAddress)) {
quotePaths.push([coin.tokenAddress, baseTokenAddress]);
refCoins.push({ coin, quoteCount: 1 });
}
else {
const paths = [
[coin.tokenAddress, baseTokenAddress],
...mediaTokenAddresses.map((media) => [
coin.tokenAddress,
media,
baseTokenAddress,
]),
];
quotePaths.push(...paths);
refCoins.push({ coin, quoteCount: paths.length });
}
}
const calls = [];
const batchRefCoins = [];
const lastIndex = refCoins.length - 1;
const quoteContract = this.getContract(configPerChain);
for (const [index, ref] of Object.entries(refCoins)) {
calls.push(...quotePaths
.splice(0, ref.quoteCount)
.map((path) => this.encodeCallData(ref.coin, path)));
batchRefCoins.push(ref);
if (parseInt(index) < lastIndex &&
calls.length < ABCUniswap.BATCH_SIZE) {
continue;
}
try {
const priceBNs = await this.callContract(chainCode, quoteContract, calls);
for (const i of batchRefCoins) {
const bns = priceBNs
.splice(0, i.quoteCount)
.filter((bn) => typeof bn !== 'undefined' && bn.isFinite() && bn.gt(0))
.map((bn) => bn);
const priceBN = bignumber_js_1.default.max(new bignumber_js_1.default(0), ...bns);
prices.push({
coin: i.coin.code,
unit: i.coin.chainCode,
value: priceBN,
});
}
}
catch (e) {
console.error(`Error in batching quote prices. coins: ${batchRefCoins.map((i) => i.coin.code)}`, e);
}
finally {
calls.splice(0, calls.length);
batchRefCoins.splice(0, batchRefCoins.length);
}
}
}
return prices;
}
async callContract(chainCode, contract, calls) {
const client = (await this.providerController.getClient(chainCode, (i) => i instanceof eth_1.Geth));
const resp = await client.batchEthCall(calls.map((i) => ({ to: contract, data: i })));
return resp.map((i) => {
if (typeof i === 'undefined') {
return undefined;
}
const [value] = abi_1.defaultAbiCoder.decode(['uint256'], '0x' + i.slice(-64));
const bn = new bignumber_js_1.default(value.toString());
return bn.isFinite() ? bn.div(BASE_TOKEN_DECIMALS_MULTIPLY) : undefined;
});
}
}
ABCUniswap.BATCH_SIZE = 50;
class UniswapV2 extends ABCUniswap {
getContract(configPerChain) {
return configPerChain['router_address'];
}
encodeCallData(coin, path) {
// https://docs.uniswap.org/protocol/V2/reference/smart-contracts/library#getamountsout
const methodID = '0xd06ca61f'; // keccak256(Buffer.from("getAmountsOut(uint256,address[])")).slice(0, 10)
const amountIn = (0, bignumber_plus_1.toBigIntHex)(new bignumber_js_1.default(1).shiftedBy(coin.decimals));
const encodedParams = abi_1.defaultAbiCoder.encode(['uint256', 'address[]'], [amountIn, path]);
return (0, bytes_1.hexConcat)([methodID, encodedParams]);
}
}
exports.UniswapV2 = UniswapV2;
class UniswapV3 extends ABCUniswap {
getContract(configPerChain) {
return configPerChain['quoter_address'];
}
encodeCallData(coin, path) {
// https://docs.uniswap.org/protocol/reference/periphery/interfaces/IQuoter#quoteexactinput
const methodID = '0xcdca1753'; // keccak256(Buffer.from("quoteExactInput(bytes,uint256)")).slice(0, 10)
const amountIn = (0, bignumber_plus_1.toBigIntHex)(new bignumber_js_1.default(1).shiftedBy(coin.decimals));
const pathHex = '0x' + path.map((i) => i.slice(2)).join('000bb8');
const encodedParams = abi_1.defaultAbiCoder.encode(['bytes', 'uint256'], [pathHex, amountIn]);
return (0, bytes_1.hexConcat)([methodID, encodedParams]);
}
}
exports.UniswapV3 = UniswapV3;
//# sourceMappingURL=uniswap.js.map