swapnet-sdk-test-4
Version:
TypeScript SDK for SwapNet API.
204 lines (203 loc) • 8.59 kB
JavaScript
// @ts-ignore
import universalRouterData from '../../abi/universalRouter.json' with {
type: "json"
};
import { Interface, solidityPacked } from 'ethers';
import { CommandType, RoutePlanner, UniswapV2ForkNames, UniswapV3ForkNames } from './routerCommands.js';
import { getFewWrappedTokenAddress } from './fewTokenHelper.js';
import { RouterBase, resolveEncodeOptions } from '../routerBase.js';
const CONTRACT_BALANCE = 2n ** 255n;
const SENDER_AS_RECIPIENT = '0x0000000000000000000000000000000000000001';
const ROUTER_AS_RECIPIENT = '0x0000000000000000000000000000000000000002';
const universalRouterInterface = new Interface(universalRouterData.abi);
const encodeV3RouteToPath = (inputTokenAddress, outputTokenAddress, feeAmount)=>{
const types = [
'address',
'uint24',
'address'
];
const path = [
inputTokenAddress,
feeAmount,
outputTokenAddress
];
return solidityPacked(types, path);
};
const toV2ForkName = (protocol)=>{
if (protocol === "UniswapV2") {
return UniswapV2ForkNames.Uniswap;
} else if (protocol === "ThrusterV2-3k") {
return UniswapV2ForkNames.Thruster3k;
} else if (protocol === "ThrusterV2-10k") {
return UniswapV2ForkNames.Thruster10k;
} else if (protocol === "RingswapV2") {
return UniswapV2ForkNames.Ringswap;
}
throw new Error(`Invalid V2 protocol ${protocol}!`);
};
const toV3ForkName = (protocol)=>{
if (protocol === "UniswapV3") {
return UniswapV3ForkNames.Uniswap;
} else if (protocol === "ThrusterV3") {
return UniswapV3ForkNames.Thruster;
} else if (protocol === "RingswapV3") {
return UniswapV3ForkNames.Ringswap;
}
throw new Error(`Invalid V3 protocol ${protocol}!`);
};
export class UniversalRouter extends RouterBase {
constructor(_chainId, _routerAddress = undefined, _tokenProxyAddress = undefined){
super("universal router", _chainId, _routerAddress, _tokenProxyAddress);
}
encode(routingPlan, options) {
const { amountOutMinimum, wrapInput, unwrapOutput, deadline } = resolveEncodeOptions(routingPlan, options);
let { recipientAddress } = options;
if (recipientAddress === undefined) {
recipientAddress = SENDER_AS_RECIPIENT;
}
const { tokenOps, amountIn, fromToken, toToken } = routingPlan;
const planner = new RoutePlanner();
if (options.inputTokenPermit) {
const { permit, signature } = options.inputTokenPermit;
console.log(permit, signature, 'permit, signature');
planner.addCommand(CommandType.PERMIT2_PERMIT, [
permit,
signature
]);
}
if (wrapInput) {
planner.addCommand(CommandType.WRAP_ETH, [
ROUTER_AS_RECIPIENT,
amountIn
]);
} else {
planner.addCommand(CommandType.PERMIT2_TRANSFER_FROM, [
fromToken,
ROUTER_AS_RECIPIENT,
amountIn
]);
}
tokenOps.forEach((tokenOp)=>{
const { toSwaps } = tokenOp;
if (toSwaps.length === 0) {
return;
}
toSwaps.sort((r1, r2)=>Number(BigInt(r1.amountIn) - BigInt(r2.amountIn)));
toSwaps.forEach((toSwap, j)=>{
const fromToken = tokenOp.tokenInfo.address;
const toToken = toSwap.toTokenOp.tokenInfo.address;
const { liquidityInfo } = toSwap;
let { amountIn } = toSwap;
if (j === toSwaps.length - 1) {
amountIn = CONTRACT_BALANCE;
}
if (liquidityInfo.protocol === "UniswapV2" || liquidityInfo.protocol === "ThrusterV2-3k" || liquidityInfo.protocol === "ThrusterV2-10k" || liquidityInfo.protocol === "RingswapV2") {
let path = [
fromToken,
toToken
];
if (liquidityInfo.protocol === "RingswapV2") {
const fewWrappedFromToken = getFewWrappedTokenAddress(fromToken);
const fewWrappedToToken = getFewWrappedTokenAddress(toToken);
path = [
fewWrappedFromToken,
fewWrappedToToken
];
planner.addCommand(CommandType.WRAP_UNWRAP_FEW_TOKEN, [
fromToken,
ROUTER_AS_RECIPIENT,
amountIn,
true
]);
}
planner.addCommand(CommandType.V2_SWAP_EXACT_IN, [
ROUTER_AS_RECIPIENT,
amountIn,
0n,
path,
false,
toV2ForkName(liquidityInfo.protocol)
]);
if (liquidityInfo.protocol === "RingswapV2") {
const fewWrappedToToken = getFewWrappedTokenAddress(toToken);
planner.addCommand(CommandType.WRAP_UNWRAP_FEW_TOKEN, [
fewWrappedToToken,
ROUTER_AS_RECIPIENT,
0n,
false
]);
}
} else if (liquidityInfo.protocol === "UniswapV3" || liquidityInfo.protocol === "ThrusterV3" || liquidityInfo.protocol === "RingswapV3") {
let path;
if (liquidityInfo.protocol === "RingswapV3") {
const fewWrappedFromToken = getFewWrappedTokenAddress(fromToken);
const fewWrappedToToken = getFewWrappedTokenAddress(toToken);
path = encodeV3RouteToPath(fewWrappedFromToken, fewWrappedToToken, Number(liquidityInfo.fee));
planner.addCommand(CommandType.WRAP_UNWRAP_FEW_TOKEN, [
fromToken,
ROUTER_AS_RECIPIENT,
amountIn,
true
]);
} else {
path = encodeV3RouteToPath(fromToken, toToken, Number(liquidityInfo.fee));
}
planner.addCommand(CommandType.V3_SWAP_EXACT_IN, [
ROUTER_AS_RECIPIENT,
amountIn,
0n,
path,
false,
toV3ForkName(liquidityInfo.protocol)
]);
if (liquidityInfo.protocol === "RingswapV3") {
const fewWrappedToToken = getFewWrappedTokenAddress(toToken);
planner.addCommand(CommandType.WRAP_UNWRAP_FEW_TOKEN, [
fewWrappedToToken,
ROUTER_AS_RECIPIENT,
0n,
false
]);
}
} else if (liquidityInfo.protocol === "CurveV1") {
planner.addCommand(CommandType.CURVE_V1, [
liquidityInfo.address,
fromToken,
toToken,
amountIn,
0n
]);
} else {
throw new Error(`Unknown protocol ${liquidityInfo.protocol}!`);
}
});
});
if (unwrapOutput) {
planner.addCommand(CommandType.UNWRAP_WETH, [
recipientAddress,
amountOutMinimum
]);
} else {
planner.addCommand(CommandType.SWEEP, [
toToken,
recipientAddress,
amountOutMinimum
]);
}
const { commands, inputs } = planner;
let calldata;
if (deadline === undefined) {
calldata = universalRouterInterface.encodeFunctionData('execute(bytes,bytes[])', [
commands,
inputs
]);
} else {
calldata = universalRouterInterface.encodeFunctionData('execute(bytes,bytes[],uint256)', [
commands,
inputs,
options.deadline
]);
}
return calldata;
}
}