@uniswap/v4-sdk
Version:
⚒️ An SDK for building applications on top of Uniswap V4
266 lines • 13.7 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.V4Planner = exports.V4_SWAP_ACTIONS_V2_1_1 = exports.V4_BASE_ACTIONS_ABI_DEFINITION = exports.Subparser = exports.Actions = exports.isAtLeastV2_1_1 = exports.URVersion = void 0;
const tslib_1 = require("tslib");
const tiny_invariant_1 = tslib_1.__importDefault(require("tiny-invariant"));
const utils_1 = require("ethers/lib/utils");
const sdk_core_1 = require("@uniswap/sdk-core");
const internalConstants_1 = require("../internalConstants");
const encodeRouteToPath_1 = require("./encodeRouteToPath");
/**
* Actions
* @description Constants that define what action to perform
* Not all actions are supported yet.
* @enum {number}
*/
/**
* UniversalRouter version
* @description Version of the UniversalRouter contract
* @enum {string}
*/
var URVersion;
(function (URVersion) {
URVersion["V2_0"] = "2.0";
URVersion["V2_1_1"] = "2.1.1";
URVersion["V2_2_0"] = "2.2.0";
})(URVersion = exports.URVersion || (exports.URVersion = {}));
function isAtLeastV2_1_1(version) {
return !!version && version.localeCompare(URVersion.V2_1_1, undefined, { numeric: true }) >= 0;
}
exports.isAtLeastV2_1_1 = isAtLeastV2_1_1;
var Actions;
(function (Actions) {
// pool actions
// liquidity actions
Actions[Actions["INCREASE_LIQUIDITY"] = 0] = "INCREASE_LIQUIDITY";
Actions[Actions["DECREASE_LIQUIDITY"] = 1] = "DECREASE_LIQUIDITY";
Actions[Actions["MINT_POSITION"] = 2] = "MINT_POSITION";
Actions[Actions["BURN_POSITION"] = 3] = "BURN_POSITION";
// for fee on transfer tokens
// INCREASE_LIQUIDITY_FROM_DELTAS = 0x04,
// MINT_POSITION_FROM_DELTAS = 0x05,
// swapping
Actions[Actions["SWAP_EXACT_IN_SINGLE"] = 6] = "SWAP_EXACT_IN_SINGLE";
Actions[Actions["SWAP_EXACT_IN"] = 7] = "SWAP_EXACT_IN";
Actions[Actions["SWAP_EXACT_OUT_SINGLE"] = 8] = "SWAP_EXACT_OUT_SINGLE";
Actions[Actions["SWAP_EXACT_OUT"] = 9] = "SWAP_EXACT_OUT";
// closing deltas on the pool manager
// settling
Actions[Actions["SETTLE"] = 11] = "SETTLE";
Actions[Actions["SETTLE_ALL"] = 12] = "SETTLE_ALL";
Actions[Actions["SETTLE_PAIR"] = 13] = "SETTLE_PAIR";
// taking
Actions[Actions["TAKE"] = 14] = "TAKE";
Actions[Actions["TAKE_ALL"] = 15] = "TAKE_ALL";
Actions[Actions["TAKE_PORTION"] = 16] = "TAKE_PORTION";
Actions[Actions["TAKE_PAIR"] = 17] = "TAKE_PAIR";
Actions[Actions["CLOSE_CURRENCY"] = 18] = "CLOSE_CURRENCY";
// CLEAR_OR_TAKE = 0x13,
Actions[Actions["SWEEP"] = 20] = "SWEEP";
// for wrapping/unwrapping native
// WRAP = 0x15,
Actions[Actions["UNWRAP"] = 22] = "UNWRAP";
})(Actions = exports.Actions || (exports.Actions = {}));
var Subparser;
(function (Subparser) {
Subparser[Subparser["V4SwapExactInSingle"] = 0] = "V4SwapExactInSingle";
Subparser[Subparser["V4SwapExactIn"] = 1] = "V4SwapExactIn";
Subparser[Subparser["V4SwapExactOutSingle"] = 2] = "V4SwapExactOutSingle";
Subparser[Subparser["V4SwapExactOut"] = 3] = "V4SwapExactOut";
Subparser[Subparser["PoolKey"] = 4] = "PoolKey";
})(Subparser = exports.Subparser || (exports.Subparser = {}));
const POOL_KEY_STRUCT = '(address currency0,address currency1,uint24 fee,int24 tickSpacing,address hooks)';
const PATH_KEY_STRUCT = '(address intermediateCurrency,uint256 fee,int24 tickSpacing,address hooks,bytes hookData)';
// UR 2.0 swap structs (without minHopPriceX36)
const SWAP_EXACT_IN_SINGLE_STRUCT_V2_0 = '(' + POOL_KEY_STRUCT + ' poolKey,bool zeroForOne,uint128 amountIn,uint128 amountOutMinimum,bytes hookData)';
const SWAP_EXACT_IN_STRUCT_V2_0 = '(address currencyIn,' + PATH_KEY_STRUCT + '[] path,uint128 amountIn,uint128 amountOutMinimum)';
const SWAP_EXACT_OUT_SINGLE_STRUCT_V2_0 = '(' + POOL_KEY_STRUCT + ' poolKey,bool zeroForOne,uint128 amountOut,uint128 amountInMaximum,bytes hookData)';
const SWAP_EXACT_OUT_STRUCT_V2_0 = '(address currencyOut,' + PATH_KEY_STRUCT + '[] path,uint128 amountOut,uint128 amountInMaximum)';
// UR 2.1.1 swap structs (with minHopPriceX36)
const SWAP_EXACT_IN_SINGLE_STRUCT_V2_1_1 = '(' +
POOL_KEY_STRUCT +
' poolKey,bool zeroForOne,uint128 amountIn,uint128 amountOutMinimum,uint256 minHopPriceX36,bytes hookData)';
const SWAP_EXACT_IN_STRUCT_V2_1_1 = '(address currencyIn,' +
PATH_KEY_STRUCT +
'[] path,uint256[] minHopPriceX36,uint128 amountIn,uint128 amountOutMinimum)';
const SWAP_EXACT_OUT_SINGLE_STRUCT_V2_1_1 = '(' +
POOL_KEY_STRUCT +
' poolKey,bool zeroForOne,uint128 amountOut,uint128 amountInMaximum,uint256 minHopPriceX36,bytes hookData)';
const SWAP_EXACT_OUT_STRUCT_V2_1_1 = '(address currencyOut,' +
PATH_KEY_STRUCT +
'[] path,uint256[] minHopPriceX36,uint128 amountOut,uint128 amountInMaximum)';
// V4_BASE_ACTIONS_ABI_DEFINITION uses V2.0 structs (default, without minHopPriceX36)
const SWAP_EXACT_IN_SINGLE_STRUCT = SWAP_EXACT_IN_SINGLE_STRUCT_V2_0;
const SWAP_EXACT_IN_STRUCT = SWAP_EXACT_IN_STRUCT_V2_0;
const SWAP_EXACT_OUT_SINGLE_STRUCT = SWAP_EXACT_OUT_SINGLE_STRUCT_V2_0;
const SWAP_EXACT_OUT_STRUCT = SWAP_EXACT_OUT_STRUCT_V2_0;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
exports.V4_BASE_ACTIONS_ABI_DEFINITION = {
// Liquidity commands
[Actions.INCREASE_LIQUIDITY]: [
{ name: 'tokenId', type: 'uint256' },
{ name: 'liquidity', type: 'uint256' },
{ name: 'amount0Max', type: 'uint128' },
{ name: 'amount1Max', type: 'uint128' },
{ name: 'hookData', type: 'bytes' },
],
[Actions.DECREASE_LIQUIDITY]: [
{ name: 'tokenId', type: 'uint256' },
{ name: 'liquidity', type: 'uint256' },
{ name: 'amount0Min', type: 'uint128' },
{ name: 'amount1Min', type: 'uint128' },
{ name: 'hookData', type: 'bytes' },
],
[Actions.MINT_POSITION]: [
{ name: 'poolKey', type: POOL_KEY_STRUCT, subparser: Subparser.PoolKey },
{ name: 'tickLower', type: 'int24' },
{ name: 'tickUpper', type: 'int24' },
{ name: 'liquidity', type: 'uint256' },
{ name: 'amount0Max', type: 'uint128' },
{ name: 'amount1Max', type: 'uint128' },
{ name: 'owner', type: 'address' },
{ name: 'hookData', type: 'bytes' },
],
[Actions.BURN_POSITION]: [
{ name: 'tokenId', type: 'uint256' },
{ name: 'amount0Min', type: 'uint128' },
{ name: 'amount1Min', type: 'uint128' },
{ name: 'hookData', type: 'bytes' },
],
// Swapping commands
[Actions.SWAP_EXACT_IN_SINGLE]: [
{ name: 'swap', type: SWAP_EXACT_IN_SINGLE_STRUCT, subparser: Subparser.V4SwapExactInSingle },
],
[Actions.SWAP_EXACT_IN]: [{ name: 'swap', type: SWAP_EXACT_IN_STRUCT, subparser: Subparser.V4SwapExactIn }],
[Actions.SWAP_EXACT_OUT_SINGLE]: [
{ name: 'swap', type: SWAP_EXACT_OUT_SINGLE_STRUCT, subparser: Subparser.V4SwapExactOutSingle },
],
[Actions.SWAP_EXACT_OUT]: [{ name: 'swap', type: SWAP_EXACT_OUT_STRUCT, subparser: Subparser.V4SwapExactOut }],
// Payments commands
[Actions.SETTLE]: [
{ name: 'currency', type: 'address' },
{ name: 'amount', type: 'uint256' },
{ name: 'payerIsUser', type: 'bool' },
],
[Actions.SETTLE_ALL]: [
{ name: 'currency', type: 'address' },
{ name: 'maxAmount', type: 'uint256' },
],
[Actions.SETTLE_PAIR]: [
{ name: 'currency0', type: 'address' },
{ name: 'currency1', type: 'address' },
],
[Actions.TAKE]: [
{ name: 'currency', type: 'address' },
{ name: 'recipient', type: 'address' },
{ name: 'amount', type: 'uint256' },
],
[Actions.TAKE_ALL]: [
{ name: 'currency', type: 'address' },
{ name: 'minAmount', type: 'uint256' },
],
[Actions.TAKE_PORTION]: [
{ name: 'currency', type: 'address' },
{ name: 'recipient', type: 'address' },
{ name: 'bips', type: 'uint256' },
],
[Actions.TAKE_PAIR]: [
{ name: 'currency0', type: 'address' },
{ name: 'currency1', type: 'address' },
{ name: 'recipient', type: 'address' },
],
[Actions.CLOSE_CURRENCY]: [{ name: 'currency', type: 'address' }],
[Actions.SWEEP]: [
{ name: 'currency', type: 'address' },
{ name: 'recipient', type: 'address' },
],
[Actions.UNWRAP]: [{ name: 'amount', type: 'uint256' }],
};
// UR 2.1.1 specific ABI definitions for swap actions (with minHopPriceX36)
exports.V4_SWAP_ACTIONS_V2_1_1 = {
[Actions.SWAP_EXACT_IN_SINGLE]: [
{ name: 'swap', type: SWAP_EXACT_IN_SINGLE_STRUCT_V2_1_1, subparser: Subparser.V4SwapExactInSingle },
],
[Actions.SWAP_EXACT_IN]: [{ name: 'swap', type: SWAP_EXACT_IN_STRUCT_V2_1_1, subparser: Subparser.V4SwapExactIn }],
[Actions.SWAP_EXACT_OUT_SINGLE]: [
{ name: 'swap', type: SWAP_EXACT_OUT_SINGLE_STRUCT_V2_1_1, subparser: Subparser.V4SwapExactOutSingle },
],
[Actions.SWAP_EXACT_OUT]: [{ name: 'swap', type: SWAP_EXACT_OUT_STRUCT_V2_1_1, subparser: Subparser.V4SwapExactOut }],
};
const FULL_DELTA_AMOUNT = 0;
class V4Planner {
constructor() {
this.actions = internalConstants_1.EMPTY_BYTES;
this.params = [];
}
addAction(type, parameters, urVersion = URVersion.V2_0) {
let command = createAction(type, parameters, urVersion);
this.params.push(command.encodedInput);
this.actions = this.actions.concat(command.action.toString(16).padStart(2, '0'));
return this;
}
addTrade(trade, slippageTolerance, minHopPriceX36, urVersion = URVersion.V2_0) {
const exactOutput = trade.tradeType === sdk_core_1.TradeType.EXACT_OUTPUT;
// exactInput we sometimes perform aggregated slippage checks, but not with exactOutput
if (exactOutput)
(0, tiny_invariant_1.default)(!!slippageTolerance, 'ExactOut requires slippageTolerance');
(0, tiny_invariant_1.default)(trade.swaps.length === 1, 'Only accepts Trades with 1 swap (must break swaps into individual trades)');
(0, tiny_invariant_1.default)(urVersion === URVersion.V2_0 || !(minHopPriceX36 === null || minHopPriceX36 === void 0 ? void 0 : minHopPriceX36.length) || minHopPriceX36.length === trade.route.pools.length, `minHopPriceX36 length (${minHopPriceX36 === null || minHopPriceX36 === void 0 ? void 0 : minHopPriceX36.length}) must equal route.pools.length (${trade.route.pools.length})`);
const actionType = exactOutput ? Actions.SWAP_EXACT_OUT : Actions.SWAP_EXACT_IN;
const currencyIn = currencyAddress(trade.route.pathInput);
const currencyOut = currencyAddress(trade.route.pathOutput);
const swapStruct = exactOutput
? {
currencyOut,
path: (0, encodeRouteToPath_1.encodeRouteToPath)(trade.route, exactOutput),
...(isAtLeastV2_1_1(urVersion) && { minHopPriceX36: minHopPriceX36 !== null && minHopPriceX36 !== void 0 ? minHopPriceX36 : [] }),
amountOut: trade.outputAmount.quotient.toString(),
amountInMaximum: trade.maximumAmountIn(slippageTolerance !== null && slippageTolerance !== void 0 ? slippageTolerance : new sdk_core_1.Percent(0)).quotient.toString(),
}
: {
currencyIn,
path: (0, encodeRouteToPath_1.encodeRouteToPath)(trade.route, exactOutput),
...(isAtLeastV2_1_1(urVersion) && { minHopPriceX36: minHopPriceX36 !== null && minHopPriceX36 !== void 0 ? minHopPriceX36 : [] }),
amountIn: trade.inputAmount.quotient.toString(),
amountOutMinimum: slippageTolerance ? trade.minimumAmountOut(slippageTolerance).quotient.toString() : 0,
};
this.addSwapAction(actionType, [swapStruct], urVersion);
return this;
}
addSettle(currency, payerIsUser, amount) {
this.addAction(Actions.SETTLE, [currencyAddress(currency), amount !== null && amount !== void 0 ? amount : FULL_DELTA_AMOUNT, payerIsUser]);
return this;
}
addTake(currency, recipient, amount) {
const takeAmount = amount !== null && amount !== void 0 ? amount : FULL_DELTA_AMOUNT;
this.addAction(Actions.TAKE, [currencyAddress(currency), recipient, takeAmount]);
return this;
}
addUnwrap(amount) {
this.addAction(Actions.UNWRAP, [amount]);
return this;
}
finalize() {
return utils_1.defaultAbiCoder.encode(['bytes', 'bytes[]'], [this.actions, this.params]);
}
addSwapAction(type, parameters, urVersion) {
// Use V2.1.1 ABI (with minHopPriceX36) for V2.1.1, otherwise default to V2.0 ABI (without minHopPriceX36)
const abiDef = isAtLeastV2_1_1(urVersion) ? exports.V4_SWAP_ACTIONS_V2_1_1[type] : exports.V4_BASE_ACTIONS_ABI_DEFINITION[type];
const encodedInput = utils_1.defaultAbiCoder.encode(abiDef.map((v) => v.type), parameters);
this.params.push(encodedInput);
this.actions = this.actions.concat(type.toString(16).padStart(2, '0'));
return this;
}
}
exports.V4Planner = V4Planner;
function currencyAddress(currency) {
return currency.isNative ? internalConstants_1.ADDRESS_ZERO : currency.wrapped.address;
}
function createAction(action, parameters, urVersion = URVersion.V2_0) {
const abiDef = isAtLeastV2_1_1(urVersion) && action in exports.V4_SWAP_ACTIONS_V2_1_1
? exports.V4_SWAP_ACTIONS_V2_1_1[action]
: exports.V4_BASE_ACTIONS_ABI_DEFINITION[action];
const encodedInput = utils_1.defaultAbiCoder.encode(abiDef.map((v) => v.type), parameters);
return { action, encodedInput };
}
//# sourceMappingURL=v4Planner.js.map