@uniswap/smart-order-router
Version:
Uniswap Smart Order Router
108 lines • 9.89 kB
JavaScript
import { BigNumber } from '@ethersproject/bignumber';
import { ChainId } from '@uniswap/sdk-core';
import { TokenFeeDetector__factory } from '../types/other/factories/TokenFeeDetector__factory';
import { log, metric, MetricLoggerUnit, WRAPPED_NATIVE_CURRENCY, } from '../util';
const DEFAULT_TOKEN_BUY_FEE_BPS = BigNumber.from(0);
const DEFAULT_TOKEN_SELL_FEE_BPS = BigNumber.from(0);
// on detector failure, assume no fee
export const 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 ChainId.MAINNET:
return '0xbc708B192552e19A088b4C4B8772aEeA83bCf760';
case ChainId.OPTIMISM:
return '0x95aDC98A949dCD94645A8cD56830D86e4Cf34Eff';
case ChainId.BNB:
return '0xCF6220e4496B091a6b391D48e770f1FbaC63E740';
case ChainId.POLYGON:
return '0xC988e19819a63C0e487c6Ad8d6668Ac773923BF2';
case ChainId.BASE:
return '0xCF6220e4496B091a6b391D48e770f1FbaC63E740';
case ChainId.ARBITRUM_ONE:
return '0x37324D81e318260DC4f0fCb68035028eFdE6F50e';
case ChainId.CELO:
return '0x8eEa35913DdeD795001562f9bA5b282d3ac04B60';
case ChainId.AVALANCHE:
return '0x8269d47c4910B8c87789aA0eC128C11A8614dfC8';
case ChainId.WORLDCHAIN:
return '0xbc708B192552e19A088b4C4B8772aEeA83bCf760';
case ChainId.UNICHAIN_SEPOLIA:
return '0xbc708B192552e19A088b4C4B8772aEeA83bCf760';
case ChainId.UNICHAIN:
return '0xbc708B192552e19A088b4C4B8772aEeA83bCf760';
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;
export class OnChainTokenFeeFetcher {
constructor(chainId, rpcProvider, tokenFeeAddress = FEE_DETECTOR_ADDRESS(chainId), gasLimitPerCall = GAS_LIMIT_PER_VALIDATE, amountToFlashBorrow = AMOUNT_TO_FLASH_BORROW) {
var _a;
this.chainId = chainId;
this.tokenFeeAddress = tokenFeeAddress;
this.gasLimitPerCall = gasLimitPerCall;
this.amountToFlashBorrow = amountToFlashBorrow;
this.BASE_TOKEN = (_a = WRAPPED_NATIVE_CURRENCY[this.chainId]) === null || _a === void 0 ? void 0 : _a.address;
this.contract = TokenFeeDetector__factory.connect(this.tokenFeeAddress, rpcProvider);
}
async fetchFees(addresses, providerConfig) {
const tokenToResult = {};
const addressesWithoutBaseToken = addresses.filter((address) => address.toLowerCase() !== this.BASE_TOKEN.toLowerCase());
const functionParams = addressesWithoutBaseToken.map((address) => [
address,
this.BASE_TOKEN,
this.amountToFlashBorrow,
]);
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,
});
metric.putMetric('TokenFeeFetcherFetchFeesSuccess', 1, MetricLoggerUnit.Count);
return { address, ...feeResult };
}
catch (err) {
log.error({ err }, `Error calling validate on-chain for token ${address}`);
metric.putMetric('TokenFeeFetcherFetchFeesFailure', 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,
buyFeeBps: undefined,
sellFeeBps: undefined,
feeTakenOnTransfer: false,
externalTransferFailed: false,
sellReverted: false,
};
}
}));
results.forEach(({ address, buyFeeBps, sellFeeBps, feeTakenOnTransfer, externalTransferFailed, sellReverted, }) => {
if (buyFeeBps || sellFeeBps) {
tokenToResult[address] = {
buyFeeBps,
sellFeeBps,
feeTakenOnTransfer,
externalTransferFailed,
sellReverted,
};
}
});
return tokenToResult;
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidG9rZW4tZmVlLWZldGNoZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvcHJvdmlkZXJzL3Rva2VuLWZlZS1mZXRjaGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUVyRCxPQUFPLEVBQUUsT0FBTyxFQUFFLE1BQU0sbUJBQW1CLENBQUM7QUFFNUMsT0FBTyxFQUFFLHlCQUF5QixFQUFFLE1BQU0sb0RBQW9ELENBQUM7QUFFL0YsT0FBTyxFQUNMLEdBQUcsRUFDSCxNQUFNLEVBQ04sZ0JBQWdCLEVBQ2hCLHVCQUF1QixHQUN4QixNQUFNLFNBQVMsQ0FBQztBQUlqQixNQUFNLHlCQUF5QixHQUFHLFNBQVMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUM7QUFDcEQsTUFBTSwwQkFBMEIsR0FBRyxTQUFTLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO0FBRXJELHFDQUFxQztBQUNyQyxNQUFNLENBQUMsTUFBTSx3QkFBd0IsR0FBRztJQUN0QyxTQUFTLEVBQUUseUJBQXlCO0lBQ3BDLFVBQVUsRUFBRSwwQkFBMEI7Q0FDdkMsQ0FBQztBQWFGLG9EQUFvRDtBQUNwRCxNQUFNLG9CQUFvQixHQUFHLENBQUMsT0FBZ0IsRUFBRSxFQUFFO0lBQ2hELFFBQVEsT0FBTyxFQUFFO1FBQ2YsS0FBSyxPQUFPLENBQUMsT0FBTztZQUNsQixPQUFPLDRDQUE0QyxDQUFDO1FBQ3RELEtBQUssT0FBTyxDQUFDLFFBQVE7WUFDbkIsT0FBTyw0Q0FBNEMsQ0FBQztRQUN0RCxLQUFLLE9BQU8sQ0FBQyxHQUFHO1lBQ2QsT0FBTyw0Q0FBNEMsQ0FBQztRQUN0RCxLQUFLLE9BQU8sQ0FBQyxPQUFPO1lBQ2xCLE9BQU8sNENBQTRDLENBQUM7UUFDdEQsS0FBSyxPQUFPLENBQUMsSUFBSTtZQUNmLE9BQU8sNENBQTRDLENBQUM7UUFDdEQsS0FBSyxPQUFPLENBQUMsWUFBWTtZQUN2QixPQUFPLDRDQUE0QyxDQUFDO1FBQ3RELEtBQUssT0FBTyxDQUFDLElBQUk7WUFDZixPQUFPLDRDQUE0QyxDQUFDO1FBQ3RELEtBQUssT0FBTyxDQUFDLFNBQVM7WUFDcEIsT0FBTyw0Q0FBNEMsQ0FBQztRQUN0RCxLQUFLLE9BQU8sQ0FBQyxVQUFVO1lBQ3JCLE9BQU8sNENBQTRDLENBQUM7UUFDdEQsS0FBSyxPQUFPLENBQUMsZ0JBQWdCO1lBQzNCLE9BQU8sNENBQTRDLENBQUM7UUFDdEQsS0FBSyxPQUFPLENBQUMsUUFBUTtZQUNuQixPQUFPLDRDQUE0QyxDQUFDO1FBQ3REO1lBQ0UsbUNBQW1DO1lBQ25DLE9BQU8sNENBQTRDLENBQUM7S0FDdkQ7QUFDSCxDQUFDLENBQUM7QUFFRiw4RUFBOEU7QUFDOUUseURBQXlEO0FBQ3pELHlFQUF5RTtBQUN6RSxnRkFBZ0Y7QUFDaEYsTUFBTSxzQkFBc0IsR0FBRyxRQUFRLENBQUM7QUFDeEMsK0RBQStEO0FBQy9ELE1BQU0sc0JBQXNCLEdBQUcsT0FBUyxDQUFDO0FBU3pDLE1BQU0sT0FBTyxzQkFBc0I7SUFJakMsWUFDVSxPQUFnQixFQUN4QixXQUF5QixFQUNqQixrQkFBa0Isb0JBQW9CLENBQUMsT0FBTyxDQUFDLEVBQy9DLGtCQUFrQixzQkFBc0IsRUFDeEMsc0JBQXNCLHNCQUFzQjs7UUFKNUMsWUFBTyxHQUFQLE9BQU8sQ0FBUztRQUVoQixvQkFBZSxHQUFmLGVBQWUsQ0FBZ0M7UUFDL0Msb0JBQWUsR0FBZixlQUFlLENBQXlCO1FBQ3hDLHdCQUFtQixHQUFuQixtQkFBbUIsQ0FBeUI7UUFFcEQsSUFBSSxDQUFDLFVBQVUsR0FBRyxNQUFBLHVCQUF1QixDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsMENBQUUsT0FBTyxDQUFDO1FBQ2pFLElBQUksQ0FBQyxRQUFRLEdBQUcseUJBQXlCLENBQUMsT0FBTyxDQUMvQyxJQUFJLENBQUMsZUFBZSxFQUNwQixXQUFXLENBQ1osQ0FBQztJQUNKLENBQUM7SUFFTSxLQUFLLENBQUMsU0FBUyxDQUNwQixTQUFvQixFQUNwQixjQUErQjtRQUUvQixNQUFNLGFBQWEsR0FBZ0IsRUFBRSxDQUFDO1FBRXRDLE1BQU0seUJBQXlCLEdBQUcsU0FBUyxDQUFDLE1BQU0sQ0FDaEQsQ0FBQyxPQUFPLEVBQUUsRUFBRSxDQUFDLE9BQU8sQ0FBQyxXQUFXLEVBQUUsS0FBSyxJQUFJLENBQUMsVUFBVSxDQUFDLFdBQVcsRUFBRSxDQUNyRSxDQUFDO1FBQ0YsTUFBTSxjQUFjLEdBQUcseUJBQXlCLENBQUMsR0FBRyxDQUFDLENBQUMsT0FBTyxFQUFFLEVBQUUsQ0FBQztZQUNoRSxPQUFPO1lBQ1AsSUFBSSxDQUFDLFVBQVU7WUFDZixJQUFJLENBQUMsbUJBQW1CO1NBQ3pCLENBQStCLENBQUM7UUFFakMsTUFBTSxPQUFPLEdBQUcsTUFBTSxPQUFPLENBQUMsR0FBRyxDQUMvQixjQUFjLENBQUMsR0FBRyxDQUFDLEtBQUssRUFBRSxDQUFDLE9BQU8sRUFBRSxTQUFTLEVBQUUsY0FBYyxDQUFDLEVBQUUsRUFBRTtZQUNoRSxJQUFJO2dCQUNGLHNGQUFzRjtnQkFDdEYsNkVBQTZFO2dCQUM3RSxNQUFNLFNBQVMsR0FBRyxNQUFNLElBQUksQ0FBQyxRQUFRLENBQUMsVUFBVSxDQUFDLFFBQVEsQ0FDdkQsT0FBTyxFQUNQLFNBQVMsRUFDVCxjQUFjLEVBQ2Q7b0JBQ0UsUUFBUSxFQUFFLElBQUksQ0FBQyxlQUFlO29CQUM5QixRQUFRLEVBQUUsY0FBYyxhQUFkLGNBQWMsdUJBQWQsY0FBYyxDQUFFLFdBQVc7aUJBQ3RDLENBQ0YsQ0FBQztnQkFFRixNQUFNLENBQUMsU0FBUyxDQUNkLGlDQUFpQyxFQUNqQyxDQUFDLEVBQ0QsZ0JBQWdCLENBQUMsS0FBSyxDQUN2QixDQUFDO2dCQUVGLE9BQU8sRUFBRSxPQUFPLEVBQUUsR0FBRyxTQUFTLEVBQUUsQ0FBQzthQUNsQztZQUFDLE9BQU8sR0FBRyxFQUFFO2dCQUNaLEdBQUcsQ0FBQyxLQUFLLENBQ1AsRUFBRSxHQUFHLEVBQUUsRUFDUCw2Q0FBNkMsT0FBTyxFQUFFLENBQ3ZELENBQUM7Z0JBRUYsTUFBTSxDQUFDLFNBQVMsQ0FDZCxpQ0FBaUMsRUFDakMsQ0FBQyxFQUNELGdCQUFnQixDQUFDLEtBQUssQ0FDdkIsQ0FBQztnQkFFRix5REFBeUQ7Z0JBQ3pELDZEQUE2RDtnQkFDN0Qsb0dBQW9HO2dCQUNwRyxPQUFPO29CQUNMLE9BQU87b0JBQ1AsU0FBUyxFQUFFLFNBQVM7b0JBQ3BCLFVBQVUsRUFBRSxTQUFTO29CQUNyQixrQkFBa0IsRUFBRSxLQUFLO29CQUN6QixzQkFBc0IsRUFBRSxLQUFLO29CQUM3QixZQUFZLEVBQUUsS0FBSztpQkFDcEIsQ0FBQzthQUNIO1FBQ0gsQ0FBQyxDQUFDLENBQ0gsQ0FBQztRQUVGLE9BQU8sQ0FBQyxPQUFPLENBQ2IsQ0FBQyxFQUNDLE9BQU8sRUFDUCxTQUFTLEVBQ1QsVUFBVSxFQUNWLGtCQUFrQixFQUNsQixzQkFBc0IsRUFDdEIsWUFBWSxHQUNiLEVBQUUsRUFBRTtZQUNILElBQUksU0FBUyxJQUFJLFVBQVUsRUFBRTtnQkFDM0IsYUFBYSxDQUFDLE9BQU8sQ0FBQyxHQUFHO29CQUN2QixTQUFTO29CQUNULFVBQVU7b0JBQ1Ysa0JBQWtCO29CQUNsQixzQkFBc0I7b0JBQ3RCLFlBQVk7aUJBQ2IsQ0FBQzthQUNIO1FBQ0gsQ0FBQyxDQUNGLENBQUM7UUFFRixPQUFPLGFBQWEsQ0FBQztJQUN2QixDQUFDO0NBQ0YifQ==