UNPKG

@uniswap/smart-order-router

Version:
165 lines 14.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.OnChainTokenFeeFetcher = exports.DEFAULT_TOKEN_FEE_RESULT = void 0; const bignumber_1 = require("@ethersproject/bignumber"); const sdk_core_1 = require("@uniswap/sdk-core"); const TokenFeeDetector__factory_1 = require("../types/other/factories/TokenFeeDetector__factory"); const util_1 = require("../util"); const token_provider_1 = require("./token-provider"); const DEFAULT_TOKEN_BUY_FEE_BPS = bignumber_1.BigNumber.from(0); const DEFAULT_TOKEN_SELL_FEE_BPS = bignumber_1.BigNumber.from(0); // on detector failure, assume no fee exports.DEFAULT_TOKEN_FEE_RESULT = { buyFeeBps: DEFAULT_TOKEN_BUY_FEE_BPS, sellFeeBps: DEFAULT_TOKEN_SELL_FEE_BPS, }; // address at which the FeeDetector lens is deployed const FEE_DETECTOR_ADDRESS = (chainId) => { switch (chainId) { case sdk_core_1.ChainId.MAINNET: return '0xbc708B192552e19A088b4C4B8772aEeA83bCf760'; case sdk_core_1.ChainId.OPTIMISM: return '0x95aDC98A949dCD94645A8cD56830D86e4Cf34Eff'; case sdk_core_1.ChainId.BNB: return '0xCF6220e4496B091a6b391D48e770f1FbaC63E740'; case sdk_core_1.ChainId.POLYGON: return '0xC988e19819a63C0e487c6Ad8d6668Ac773923BF2'; case sdk_core_1.ChainId.BASE: return '0xCF6220e4496B091a6b391D48e770f1FbaC63E740'; case sdk_core_1.ChainId.ARBITRUM_ONE: return '0x37324D81e318260DC4f0fCb68035028eFdE6F50e'; case sdk_core_1.ChainId.CELO: return '0x8eEa35913DdeD795001562f9bA5b282d3ac04B60'; case sdk_core_1.ChainId.AVALANCHE: return '0x8269d47c4910B8c87789aA0eC128C11A8614dfC8'; case sdk_core_1.ChainId.WORLDCHAIN: return '0xbc708B192552e19A088b4C4B8772aEeA83bCf760'; case sdk_core_1.ChainId.UNICHAIN_SEPOLIA: return '0xbc708B192552e19A088b4C4B8772aEeA83bCf760'; case sdk_core_1.ChainId.UNICHAIN: return '0xbc708B192552e19A088b4C4B8772aEeA83bCf760'; case sdk_core_1.ChainId.SONEIUM: return '0x7A5299822b2cD9aC9A9f67756Aa2d62140e9A66f'; default: // just default to mainnet contract return '0xbc708B192552e19A088b4C4B8772aEeA83bCf760'; } }; // Amount has to be big enough to avoid rounding errors, but small enough that // most v2 pools will have at least this many token units // 100000 is the smallest number that avoids rounding errors in bps terms // 10000 was not sufficient due to rounding errors for rebase token (e.g. stETH) const AMOUNT_TO_FLASH_BORROW = '100000'; // 1M gas limit per validate call, should cover most swap cases const GAS_LIMIT_PER_VALIDATE = 1000000; class OnChainTokenFeeFetcher { constructor(chainId, rpcProvider, tokenFeeAddress = FEE_DETECTOR_ADDRESS(chainId), gasLimitPerCall = GAS_LIMIT_PER_VALIDATE, amountToFlashBorrow = AMOUNT_TO_FLASH_BORROW) { this.tokenFeeAddress = tokenFeeAddress; this.gasLimitPerCall = gasLimitPerCall; this.amountToFlashBorrow = amountToFlashBorrow; this.BASE_TOKENS = this.getBaseTokensByChain(chainId); this.contract = TokenFeeDetector__factory_1.TokenFeeDetector__factory.connect(this.tokenFeeAddress, rpcProvider); } getBaseTokensByChain(chainId) { var _a, _b, _c; const baseTokens = []; // Priority order: WETH -> USDT -> USDC const weth = (_a = util_1.WRAPPED_NATIVE_CURRENCY[chainId]) === null || _a === void 0 ? void 0 : _a.address; if (weth) { baseTokens.push(weth); } try { const usdt = (_b = (0, token_provider_1.USDT_ON)(chainId)) === null || _b === void 0 ? void 0 : _b.address; if (usdt) { baseTokens.push(usdt); } } catch (e) { // USDT not available on this chain } try { const usdc = (_c = (0, token_provider_1.USDC_ON)(chainId)) === null || _c === void 0 ? void 0 : _c.address; if (usdc) { baseTokens.push(usdc); } } catch (e) { // USDC not available on this chain } return baseTokens; } async fetchFees(addresses, providerConfig) { const tokenToResult = {}; // Filter out all base tokens from the addresses to check const baseTokensLower = this.BASE_TOKENS.map((addr) => addr.toLowerCase()); const addressesWithoutBaseTokens = addresses.filter((address) => !baseTokensLower.includes(address.toLowerCase())); // Create function params for all token x base token combinations const functionParams = []; for (const address of addressesWithoutBaseTokens) { for (const baseToken of this.BASE_TOKENS) { functionParams.push([address, baseToken, this.amountToFlashBorrow]); } } // Execute all validation calls in parallel const results = await Promise.all(functionParams.map(async ([address, baseToken, amountToBorrow]) => { try { // We use the validate function instead of batchValidate to avoid poison pill problem. // One token that consumes too much gas could cause the entire batch to fail. const feeResult = await this.contract.callStatic.validate(address, baseToken, amountToBorrow, { gasLimit: this.gasLimitPerCall, blockTag: providerConfig === null || providerConfig === void 0 ? void 0 : providerConfig.blockNumber, }); util_1.metric.putMetric('TokenFeeFetcherFetchFeesSuccess', 1, util_1.MetricLoggerUnit.Count); return Object.assign({ address, baseToken }, feeResult); } catch (err) { util_1.log.error({ err }, `Error calling validate on-chain for token ${address} with base token ${baseToken}`); util_1.metric.putMetric('TokenFeeFetcherFetchFeesFailure', 1, util_1.MetricLoggerUnit.Count); // in case of FOT token fee fetch failure, we return null // so that they won't get returned from the token-fee-fetcher // and thus no fee will be applied, and the cache won't cache on FOT tokens with failed fee fetching return { address, baseToken, buyFeeBps: undefined, sellFeeBps: undefined, feeTakenOnTransfer: false, externalTransferFailed: false, sellReverted: false, }; } })); // Group results by token address and pick the first successful result // (prioritizing by base token order: WETH -> USDT -> USDC) const resultsByToken = new Map(); for (const result of results) { const existing = resultsByToken.get(result.address) || []; existing.push(result); resultsByToken.set(result.address, existing); } // For each token, find the first successful result (by base token priority) for (const [address, tokenResults] of resultsByToken) { // Sort by base token priority order const sortedResults = tokenResults.sort((a, b) => { const aIndex = this.BASE_TOKENS.indexOf(a.baseToken); const bIndex = this.BASE_TOKENS.indexOf(b.baseToken); return aIndex - bIndex; }); // Find first result with valid fee data (buyFeeBps or sellFeeBps > 0) const validResult = sortedResults.find((result) => (result.buyFeeBps && !result.buyFeeBps.isZero()) || (result.sellFeeBps && !result.sellFeeBps.isZero())); if (validResult) { tokenToResult[address] = { buyFeeBps: validResult.buyFeeBps, sellFeeBps: validResult.sellFeeBps, feeTakenOnTransfer: validResult.feeTakenOnTransfer, externalTransferFailed: validResult.externalTransferFailed, sellReverted: validResult.sellReverted, }; } } return tokenToResult; } } exports.OnChainTokenFeeFetcher = OnChainTokenFeeFetcher; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidG9rZW4tZmVlLWZldGNoZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvcHJvdmlkZXJzL3Rva2VuLWZlZS1mZXRjaGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLHdEQUFxRDtBQUVyRCxnREFBNEM7QUFFNUMsa0dBQStGO0FBRS9GLGtDQUtpQjtBQUdqQixxREFBb0Q7QUFFcEQsTUFBTSx5QkFBeUIsR0FBRyxxQkFBUyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztBQUNwRCxNQUFNLDBCQUEwQixHQUFHLHFCQUFTLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO0FBRXJELHFDQUFxQztBQUN4QixRQUFBLHdCQUF3QixHQUFHO0lBQ3RDLFNBQVMsRUFBRSx5QkFBeUI7SUFDcEMsVUFBVSxFQUFFLDBCQUEwQjtDQUN2QyxDQUFDO0FBYUYsb0RBQW9EO0FBQ3BELE1BQU0sb0JBQW9CLEdBQUcsQ0FBQyxPQUFnQixFQUFFLEVBQUU7SUFDaEQsUUFBUSxPQUFPLEVBQUU7UUFDZixLQUFLLGtCQUFPLENBQUMsT0FBTztZQUNsQixPQUFPLDRDQUE0QyxDQUFDO1FBQ3RELEtBQUssa0JBQU8sQ0FBQyxRQUFRO1lBQ25CLE9BQU8sNENBQTRDLENBQUM7UUFDdEQsS0FBSyxrQkFBTyxDQUFDLEdBQUc7WUFDZCxPQUFPLDRDQUE0QyxDQUFDO1FBQ3RELEtBQUssa0JBQU8sQ0FBQyxPQUFPO1lBQ2xCLE9BQU8sNENBQTRDLENBQUM7UUFDdEQsS0FBSyxrQkFBTyxDQUFDLElBQUk7WUFDZixPQUFPLDRDQUE0QyxDQUFDO1FBQ3RELEtBQUssa0JBQU8sQ0FBQyxZQUFZO1lBQ3ZCLE9BQU8sNENBQTRDLENBQUM7UUFDdEQsS0FBSyxrQkFBTyxDQUFDLElBQUk7WUFDZixPQUFPLDRDQUE0QyxDQUFDO1FBQ3RELEtBQUssa0JBQU8sQ0FBQyxTQUFTO1lBQ3BCLE9BQU8sNENBQTRDLENBQUM7UUFDdEQsS0FB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