@catalabs/catalyst-sdk
Version:
Catalyst AMM SDK
819 lines • 42.8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.depositWithGas = depositWithGas;
exports.depositWithGasViaPermit = depositWithGasViaPermit;
exports.withdrawWithGas = withdrawWithGas;
exports.withdrawWithGasViaPermit = withdrawWithGasViaPermit;
exports.swapByRoute = swapByRoute;
exports.swapByRouteViaPermit = swapByRouteViaPermit;
exports.depositWithLiquiditySwaps = depositWithLiquiditySwaps;
exports.prepareDepositWithLiquiditySwaps = prepareDepositWithLiquiditySwaps;
exports.withdrawWithLiquiditySwap = withdrawWithLiquiditySwap;
exports.prepareWithdrawWithLiquiditySwap = prepareWithdrawWithLiquiditySwap;
exports.prepareWithdrawSamePercentageWithLiquiditySwap = prepareWithdrawSamePercentageWithLiquiditySwap;
const catalyst_chain_lists_1 = require("@catalabs/catalyst-chain-lists");
const math_1 = require("../math");
const math_utils_1 = require("../math/math.utils");
const utils_1 = require("../utils");
const router_utils_1 = require("./router.utils");
function depositWithGas(vault, tokens, minOut, transferFroms = [[], []]) {
const depositRouterArguments = new router_utils_1.RouterArguments();
depositRouterArguments.wrapGas();
const [transferFromTokens, transferFromAmounts] = transferFroms;
for (let i = 0; i < transferFromTokens.length; i++) {
const tkn = transferFromTokens[i];
const amount = transferFromAmounts[i];
depositRouterArguments.transferFrom(tkn, amount);
}
const balances = tokens.map((_t) => router_utils_1.RouterArguments.BALANCE_THIS);
depositRouterArguments.deposit(vault, tokens, balances, minOut);
depositRouterArguments.sweep(vault, router_utils_1.RouterArguments.MSG_SENDER);
return depositRouterArguments.getParams(false);
}
function depositWithGasViaPermit(vault, tokens, minOut, transferFroms = [[], []], owner, permitBatchData) {
const depositRouterArguments = new router_utils_1.RouterArguments();
depositRouterArguments.wrapGas();
const [transferFromTokens, transferFromAmounts] = transferFroms;
const assets = [];
for (let i = 0; i < transferFromTokens.length; i++) {
assets.push({
token: transferFromTokens[i],
amount: transferFromAmounts[i],
});
}
depositRouterArguments.transferWithOptionalPermitBatch(assets, owner, permitBatchData);
const balances = tokens.map((_t) => router_utils_1.RouterArguments.BALANCE_THIS);
depositRouterArguments.deposit(vault, tokens, balances, minOut);
depositRouterArguments.sweep(vault, router_utils_1.RouterArguments.MSG_SENDER);
return depositRouterArguments.getParams(false);
}
function withdrawWithGas(vault, vaultTokenAmount, tokens, minOuts, gasToken = '') {
const withdrawRouterArguments = new router_utils_1.RouterArguments({ WGAS: gasToken });
withdrawRouterArguments.transferFrom(vault, vaultTokenAmount);
withdrawRouterArguments.withdrawEqual(vault, vaultTokenAmount, minOuts);
for (const token of tokens) {
if (token === gasToken) {
withdrawRouterArguments.unwrapGas(router_utils_1.RouterArguments.MSG_SENDER);
}
else {
withdrawRouterArguments.sweep(token, router_utils_1.RouterArguments.MSG_SENDER);
}
}
return withdrawRouterArguments.getParams();
}
function withdrawWithGasViaPermit(vault, vaultTokenAmount, tokens, minOuts, gasToken = '', permitData) {
const withdrawRouterArguments = new router_utils_1.RouterArguments({ WGAS: gasToken });
withdrawRouterArguments.transferWithOptionalPermit({
token: vault,
amount: vaultTokenAmount,
}, permitData);
withdrawRouterArguments.withdrawEqual(vault, vaultTokenAmount, minOuts);
for (const token of tokens) {
if (token === gasToken) {
withdrawRouterArguments.unwrapGas(router_utils_1.RouterArguments.MSG_SENDER);
}
else {
withdrawRouterArguments.sweep(token, router_utils_1.RouterArguments.MSG_SENDER);
}
}
return withdrawRouterArguments.getParams();
}
function swapByRoute(args) {
const { toAccount, amount, route, minOut, toAssetIndex, channelId, fromChainId, toChainId, messageVerifyGasCost, priceOfDeliveryGas, priceOfAckGas, refundGasTo = '', } = args;
const deadline = BigInt((0, utils_1.toDeadline)(14 * 24 * 60 * utils_1.ONE_MIN_MS));
let { fromAsset, toAsset, underwritingIncentive } = args;
if (underwritingIncentive === undefined) {
underwritingIncentive = 0n;
}
const fromChainGasToken = (0, catalyst_chain_lists_1.getChainGasToken)(fromChainId);
const [routeHost, routeCross, routeTarget] = router_utils_1.RouterArguments.splitRoute(fromAsset, route);
let gasFrom, gasTo = false;
if (fromAsset === utils_1.GAS_TOKEN_IDENTIFIER) {
fromAsset = fromChainGasToken;
gasFrom = true;
}
if (toAsset === utils_1.GAS_TOKEN_IDENTIFIER) {
toAsset = (0, catalyst_chain_lists_1.getChainGasToken)(toChainId);
gasTo = true;
}
const routerArgsHost = new router_utils_1.RouterArguments({ WGAS: fromChainGasToken });
const routerArgsTarget = new router_utils_1.RouterArguments({ WGAS: fromChainGasToken });
if (route[0].length === 0 && route[1].length === 0) {
if (gasFrom) {
routerArgsHost.wrapGas(toAccount);
}
else if (gasTo) {
routerArgsHost.transferFrom(fromAsset, amount);
routerArgsHost.unwrapGas(toAccount);
}
else {
throw new Error('No route but not wrapping gas?');
}
}
else if (gasFrom) {
routerArgsHost.wrapGas(router_utils_1.RouterArguments.ADDRESS_THIS, amount);
}
else {
routerArgsHost.transferFrom(fromAsset, amount);
}
if (routeHost['routeDetails'][0].length !== 0) {
routerArgsHost.localroute(routeHost.fromAsset, routeHost.routeDetails, routeCross.routeDetails[0].length === 0 ? minOut : 0n);
if (routeCross.routeDetails[0].length === 0) {
if (gasTo) {
routerArgsHost.unwrapGas(router_utils_1.RouterArguments.MSG_SENDER);
}
else {
routerArgsHost.sweep(toAsset, router_utils_1.RouterArguments.MSG_SENDER);
}
}
}
let routingIncentive = {
maxGasDelivery: 0n,
maxGasAck: 0n,
refundGasTo: refundGasTo,
priceOfDeliveryGas: 0n,
priceOfAckGas: 0n,
targetDelta: 0n,
messageVerifyGasCost: 0n,
};
let estimatedRefund = 0n;
if (routeCross.fromAsset !== '' && routeTarget.fromAsset !== '') {
let targetRoutingCalldata = '';
if (routeTarget['routeDetails'][0].length !== 0) {
routerArgsTarget.localroute(routeTarget['fromAsset'], routeTarget['routeDetails'], minOut);
targetRoutingCalldata = routerArgsTarget.getCalldata();
}
if (gasTo) {
routerArgsTarget.unwrapGas(toAccount);
}
else {
routerArgsTarget.sweep(toAsset, toAccount);
}
targetRoutingCalldata = routerArgsTarget.getCalldata();
if (refundGasTo === '') {
throw Error('refundGasTo cannot be "" or unset for cross-chain swaps');
}
const maxGasDelivery = routerArgsTarget.getGasCostEstimate({
additionalMargin: 0.2,
remote: true,
});
estimatedRefund = (maxGasDelivery * 2n) / 12n;
routingIncentive = {
maxGasDelivery: maxGasDelivery,
maxGasAck: router_utils_1.gasCostTable['ACK'],
refundGasTo: refundGasTo,
priceOfDeliveryGas: priceOfDeliveryGas,
priceOfAckGas: priceOfAckGas,
targetDelta: 0n,
messageVerifyGasCost,
};
const hostVault = routeCross['routeDetails'][0][0];
const targetVault = routeCross['routeDetails'][0][routeCross['routeDetails'][0].length - 1];
routerArgsHost.sendAsset(routeCross['fromAsset'] === utils_1.GAS_TOKEN_IDENTIFIER
? (0, catalyst_chain_lists_1.getChainGasToken)(fromChainId)
: routeCross['fromAsset'], toAssetIndex, hostVault, targetVault, channelId, minOut, router_utils_1.RouterArguments.ROUTER, router_utils_1.RouterArguments.BALANCE_THIS, routingIncentive, deadline, underwritingIncentive, targetRoutingCalldata);
}
const toCallParameters = routerArgsHost.getParams();
return {
executionInstructions: {
commands: toCallParameters[0],
inputs: toCallParameters[1],
gas: {
estimatedGasUsedOnLocal: routerArgsHost.getGasCostEstimate(),
estimatedGasUsedOnRemote: routingIncentive.maxGasDelivery,
estimatedGasUsedOnLocalAck: routingIncentive.maxGasAck,
estimatedRoutingPayment: router_utils_1.RouterArguments.getIncentiveCost(routingIncentive),
estimatedRefundOnAck: estimatedRefund,
},
},
};
}
function swapByRouteViaPermit(args) {
const { toAccount, amount, route, minOut, toAssetIndex, channelId, fromChainId, toChainId, messageVerifyGasCost, priceOfDeliveryGas, priceOfAckGas, refundGasTo = '', permitData, } = args;
const deadline = BigInt((0, utils_1.toDeadline)(14 * 24 * 60 * utils_1.ONE_MIN_MS));
let { fromAsset, toAsset, underwritingIncentive } = args;
if (underwritingIncentive === undefined) {
underwritingIncentive = 0n;
}
const fromChainGasToken = (0, catalyst_chain_lists_1.getChainGasToken)(fromChainId);
const [routeHost, routeCross, routeTarget] = router_utils_1.RouterArguments.splitRoute(fromAsset, route);
let gasFrom, gasTo = false;
if (fromAsset === utils_1.GAS_TOKEN_IDENTIFIER) {
fromAsset = fromChainGasToken;
gasFrom = true;
}
if (toAsset === utils_1.GAS_TOKEN_IDENTIFIER) {
toAsset = (0, catalyst_chain_lists_1.getChainGasToken)(toChainId);
gasTo = true;
}
const routerArgsHost = new router_utils_1.RouterArguments({ WGAS: fromChainGasToken });
const routerArgsTarget = new router_utils_1.RouterArguments({ WGAS: fromChainGasToken });
if (route[0].length === 0 && route[1].length === 0) {
if (gasFrom) {
routerArgsHost.wrapGas(toAccount);
}
else if (gasTo) {
routerArgsHost.transferWithOptionalPermit({
token: fromAsset,
amount,
}, permitData);
routerArgsHost.unwrapGas(toAccount);
}
else {
throw new Error('No route but not wrapping gas?');
}
}
else if (gasFrom) {
routerArgsHost.wrapGas(router_utils_1.RouterArguments.ADDRESS_THIS, amount);
}
else {
routerArgsHost.transferWithOptionalPermit({
token: fromAsset,
amount,
}, permitData);
}
if (routeHost['routeDetails'][0].length !== 0) {
routerArgsHost.localroute(routeHost.fromAsset, routeHost.routeDetails, routeCross.routeDetails[0].length === 0 ? minOut : 0n);
if (routeCross.routeDetails[0].length === 0) {
if (gasTo) {
routerArgsHost.unwrapGas(router_utils_1.RouterArguments.MSG_SENDER);
}
else {
routerArgsHost.sweep(toAsset, router_utils_1.RouterArguments.MSG_SENDER);
}
}
}
let routingIncentive = {
maxGasDelivery: 0n,
maxGasAck: 0n,
refundGasTo: refundGasTo,
priceOfDeliveryGas: 0n,
priceOfAckGas: 0n,
targetDelta: 0n,
messageVerifyGasCost,
};
let estimatedRefund = 0n;
if (routeCross.fromAsset !== '' && routeTarget.fromAsset !== '') {
let targetRoutingCalldata = '';
if (routeTarget['routeDetails'][0].length !== 0) {
routerArgsTarget.localroute(routeTarget['fromAsset'], routeTarget['routeDetails'], minOut);
targetRoutingCalldata = routerArgsTarget.getCalldata();
}
if (gasTo) {
routerArgsTarget.unwrapGas(toAccount);
}
else {
routerArgsTarget.sweep(toAsset, toAccount);
}
targetRoutingCalldata = routerArgsTarget.getCalldata();
if (refundGasTo === '') {
throw Error('refundGasTo cannot be "" or unset for cross-chain swaps');
}
const maxGasDelivery = routerArgsTarget.getGasCostEstimate({
additionalMargin: 0.2,
remote: true,
});
estimatedRefund = (maxGasDelivery * 2n) / 12n;
routingIncentive = {
maxGasDelivery: maxGasDelivery,
maxGasAck: router_utils_1.gasCostTable['ACK'],
refundGasTo: refundGasTo,
priceOfDeliveryGas: priceOfDeliveryGas,
priceOfAckGas: priceOfAckGas,
targetDelta: 0n,
messageVerifyGasCost,
};
const hostVault = routeCross['routeDetails'][0][0];
const targetVault = routeCross['routeDetails'][0][routeCross['routeDetails'][0].length - 1];
routerArgsHost.sendAsset(routeCross['fromAsset'] === utils_1.GAS_TOKEN_IDENTIFIER
? (0, catalyst_chain_lists_1.getChainGasToken)(fromChainId)
: routeCross['fromAsset'], toAssetIndex, hostVault, targetVault, channelId, minOut, router_utils_1.RouterArguments.ROUTER, router_utils_1.RouterArguments.BALANCE_THIS, routingIncentive, deadline, underwritingIncentive, targetRoutingCalldata);
}
const toCallParameters = routerArgsHost.getParams();
return {
executionInstructions: {
commands: toCallParameters[0],
inputs: toCallParameters[1],
gas: {
estimatedGasUsedOnLocal: routerArgsHost.getGasCostEstimate(),
estimatedGasUsedOnRemote: routingIncentive.maxGasDelivery,
estimatedGasUsedOnLocalAck: routingIncentive.maxGasAck,
estimatedRoutingPayment: router_utils_1.RouterArguments.getIncentiveCost(routingIncentive),
estimatedRefundOnAck: estimatedRefund,
},
},
};
}
function depositWithLiquiditySwaps(userDeposits, chains, vaults, vaultAddresses, assetsAddresses, userAddresses, messageVerifyGasCosts, priceOfDeliveryGas, priceOfAckGas, refundGasTo, slippage = math_1.WAD / 100n) {
if (chains.length !== vaults.length) {
throw new Error('Different length chains and vaults');
}
const { vaultTokenSwaps: allLiquiditySwaps } = (0, math_1.findOptimalLiquiditySwaps)(vaults, userDeposits);
const gasUsages = [];
const routerArgs = [];
for (let i = 0; i < chains.length; ++i) {
const chain = chains[i];
const vault = vaults[i];
const vaultAddress = vaultAddresses[i];
const userDeposit = userDeposits[i];
const vaultAssets = assetsAddresses[i];
const chainSpecificLiquiditySwaps = allLiquiditySwaps.filter((ls) => ls.from === i);
const chainArgs = new router_utils_1.RouterArguments({
WGAS: (0, catalyst_chain_lists_1.getChainGasToken)(chain.chainId),
});
for (let assetIndex = 0; assetIndex < userDeposit.length; ++assetIndex) {
chainArgs.transferFrom(vaultAssets[assetIndex], userDeposit[assetIndex]);
}
const estimatedOut = (0, math_1.depositMixed)(vault, userDeposit);
chainArgs.deposit(vaultAddress, vaultAssets, userDeposit, (estimatedOut * (math_1.WAD - slippage)) / math_1.WAD);
const routingIncentives = [];
for (let j = 0; j < chainSpecificLiquiditySwaps.length; ++j) {
const liquiditySwap = chainSpecificLiquiditySwaps[j];
const toChain = chains[liquiditySwap.to];
const toVaultAddress = vaultAddresses[liquiditySwap.to];
const deliveryGasPrice = priceOfDeliveryGas[liquiditySwap.from][liquiditySwap.to];
const ackGasPrice = priceOfAckGas[liquiditySwap.from];
const targetUserAddress = userAddresses[liquiditySwap.to];
const messageVerifyGasCost = messageVerifyGasCosts[liquiditySwap.from][liquiditySwap.to];
const amount = liquiditySwap.vaultTokensSent;
const routingIncentive = {
maxGasDelivery: router_utils_1.gasCostTable['RECEIVE'] + router_utils_1.gasCostTable['SENDLIQUIDITY'],
maxGasAck: router_utils_1.gasCostTable['ACK'],
refundGasTo: refundGasTo,
priceOfDeliveryGas: deliveryGasPrice,
priceOfAckGas: ackGasPrice,
targetDelta: 0n,
messageVerifyGasCost,
};
routingIncentives.push(routingIncentive);
const gasTokenAmount = chainSpecificLiquiditySwaps.length - 1 === j
? router_utils_1.RouterArguments.BALANCE_THIS
: router_utils_1.RouterArguments.getIncentiveCost(routingIncentive);
const deadline = BigInt((0, utils_1.toDeadline)(14 * 24 * 60 * utils_1.ONE_MIN_MS));
chainArgs.liquiditySwap(vaultAddress, toVaultAddress, toChain.channelId, [0n, 0n], targetUserAddress, gasTokenAmount, routingIncentive, amount, deadline);
}
chainArgs.sweep(vaultAddress, router_utils_1.RouterArguments.MSG_SENDER);
gasUsages.push({
estimatedGasUsedOnRemote: (0, math_utils_1.bigNumberSum)(routingIncentives.map((ri) => ri.maxGasDelivery)),
estimatedGasUsedOnLocalAck: (0, math_utils_1.bigNumberSum)(routingIncentives.map((ri) => ri.maxGasAck)),
estimatedRoutingPayment: (0, math_utils_1.bigNumberSum)(routingIncentives.map((ri) => router_utils_1.RouterArguments.getIncentiveCost(ri))),
});
routerArgs.push(chainArgs);
}
return routerArgs.map((ra, rai) => {
const estimatedGasUsedOnLocal = ra.getGasCostEstimate();
const toCallParameters = ra.getParams();
const gasUsage = gasUsages[rai];
const estimatedRefund = (gasUsage.estimatedGasUsedOnRemote * 2n) / 12n;
return {
executionInstructions: {
commands: toCallParameters[0],
inputs: toCallParameters[1],
gas: {
...gasUsage,
estimatedGasUsedOnLocal: estimatedGasUsedOnLocal,
estimatedRefundOnAck: estimatedRefund,
},
},
};
});
}
function prepareDepositWithLiquiditySwaps(userDeposits, chains, vaults, vaultAddresses, assetsAddresses, userAddresses, messageVerifyGasCosts, priceOfDeliveryGas, priceOfAckGas, refundGasTo, slippage = math_1.WAD / 100n, wrapGasValues = []) {
if (chains.length !== vaults.length) {
throw new Error('Different length chains and vaults');
}
const { vaultTokenSwaps: allLiquiditySwaps } = (0, math_1.findOptimalLiquiditySwaps)(vaults, userDeposits);
const gasUsages = [];
const routerArgs = [];
for (let i = 0; i < chains.length; ++i) {
const chain = chains[i];
const vault = vaults[i];
const vaultAddress = vaultAddresses[i];
const userDeposit = userDeposits[i];
const vaultAssets = assetsAddresses[i];
const chainSpecificLiquiditySwaps = allLiquiditySwaps.filter((ls) => ls.from === i);
const chainArgs = new router_utils_1.RouterArguments({
WGAS: (0, catalyst_chain_lists_1.getChainGasToken)(chain.chainId),
});
const wrapGasAmount = wrapGasValues[i];
if (wrapGasAmount) {
chainArgs.wrapGas(router_utils_1.RouterArguments.ADDRESS_THIS, wrapGasAmount);
}
const estimatedOut = (0, math_1.depositMixed)(vault, userDeposit);
chainArgs.deposit(vaultAddress, vaultAssets, userDeposit, (estimatedOut * (math_1.WAD - slippage)) / math_1.WAD);
const routingIncentives = [];
for (let j = 0; j < chainSpecificLiquiditySwaps.length; ++j) {
const liquiditySwap = chainSpecificLiquiditySwaps[j];
const toChain = chains[liquiditySwap.to];
const toVaultAddress = vaultAddresses[liquiditySwap.to];
const deliveryGasPrice = priceOfDeliveryGas[liquiditySwap.from][liquiditySwap.to];
const ackGasPrice = priceOfAckGas[liquiditySwap.from];
const targetUserAddress = userAddresses[liquiditySwap.to];
const messageVerifyGasCost = messageVerifyGasCosts[liquiditySwap.from][liquiditySwap.to];
const amount = liquiditySwap.vaultTokensSent;
const routingIncentive = {
maxGasDelivery: router_utils_1.gasCostTable['RECEIVE'] + router_utils_1.gasCostTable['SENDLIQUIDITY'],
maxGasAck: router_utils_1.gasCostTable['ACK'],
refundGasTo: refundGasTo,
priceOfDeliveryGas: deliveryGasPrice,
priceOfAckGas: ackGasPrice,
targetDelta: 0n,
messageVerifyGasCost,
};
routingIncentives.push(routingIncentive);
const gasTokenAmount = chainSpecificLiquiditySwaps.length - 1 === j
? router_utils_1.RouterArguments.BALANCE_THIS
: router_utils_1.RouterArguments.getIncentiveCost(routingIncentive);
const deadline = BigInt((0, utils_1.toDeadline)(14 * 24 * 60 * utils_1.ONE_MIN_MS));
chainArgs.liquiditySwap(vaultAddress, toVaultAddress, toChain.channelId, [0n, 0n], targetUserAddress, gasTokenAmount, routingIncentive, amount, deadline);
}
chainArgs.sweep(vaultAddress, router_utils_1.RouterArguments.MSG_SENDER);
gasUsages.push({
estimatedGasUsedOnRemote: (0, math_utils_1.bigNumberSum)(routingIncentives.map((ri) => ri.maxGasDelivery)),
estimatedGasUsedOnLocalAck: (0, math_utils_1.bigNumberSum)(routingIncentives.map((ri) => ri.maxGasAck)),
estimatedRoutingPayment: (0, math_utils_1.bigNumberSum)(routingIncentives.map((ri) => router_utils_1.RouterArguments.getIncentiveCost(ri))),
});
routerArgs.push(chainArgs);
}
return routerArgs.map((chainArgs, chainArgsIndex) => {
const gasUsage = gasUsages[chainArgsIndex];
return {
routerArgs: chainArgs,
gasUsage,
transferDetails: userDeposits[chainArgsIndex].map((amount, assetIndex) => ({
token: assetsAddresses[chainArgsIndex][assetIndex],
amount,
})),
};
});
}
function withdrawWithLiquiditySwap(userVaultTokens, userWithdrawals, chains, vaults, vaultAddresses, assetsAddresses, userAddresses, messageVerifyGasCosts, priceOfDeliveryGas, priceOfAckGas, refundGasTo, routerAddresses, unwrapGas = [], withdrawRatios = undefined) {
if (chains.length !== vaults.length) {
throw new Error('Different length chains and vaults');
}
const { vaultTokenSwaps: allLiquiditySwaps, boughtUnits } = (0, math_1.findOptimalWithdrawLiquiditySwaps)(vaults, userVaultTokens, userWithdrawals);
const gasUsages = [];
const routerArgs = [];
for (let i = 0; i < chains.length; ++i) {
const chain = chains[i];
const vaultAddress = vaultAddresses[i];
const vaultAssets = assetsAddresses[i];
const chainSpecificLiquiditySwaps = allLiquiditySwaps.filter((ls) => ls.from === i);
const chainArgs = new router_utils_1.RouterArguments({
WGAS: (0, catalyst_chain_lists_1.getChainGasToken)(chain.chainId),
});
const routingIncentives = [];
let vaultTokensToCollect = 0n;
for (const liquiditySwap of chainSpecificLiquiditySwaps) {
vaultTokensToCollect = vaultTokensToCollect + liquiditySwap.vaultTokens;
}
if (vaultTokensToCollect === 0n) {
routerArgs.push(undefined);
gasUsages.push({
estimatedGasUsedOnRemote: 0n,
estimatedGasUsedOnLocalAck: 0n,
estimatedRoutingPayment: 0n,
});
continue;
}
chainArgs.transferFrom(vaultAddress, vaultTokensToCollect);
const vaultUnits = boughtUnits[i];
let allUnits = (0, math_utils_1.bigNumberSum)(vaultUnits);
const withdrawRatio = vaultUnits.map((unitsForToken, i) => {
if (allUnits === 0n) {
return i === 0 ? math_1.WAD : 0n;
}
const percentage = (unitsForToken * math_1.WAD) / allUnits;
allUnits = allUnits - unitsForToken;
return percentage;
});
chainSpecificLiquiditySwaps.sort((a, b) => {
return Number(a.to === i) - Number(b.to === i);
});
for (let j = 0; j < chainSpecificLiquiditySwaps.length; ++j) {
const liquiditySwap = chainSpecificLiquiditySwaps[j];
const toChain = chains[liquiditySwap.to];
if (i === liquiditySwap.to) {
chainArgs.withdrawMixed(vaultAddress, router_utils_1.RouterArguments.BALANCE_THIS, withdrawRatios ? withdrawRatios[liquiditySwap.to] : withdrawRatio, [0n, 0n, 0n]);
if (unwrapGas[liquiditySwap.to]) {
chainArgs.unwrapGas(router_utils_1.RouterArguments.MSG_SENDER);
}
for (let withdrawRatioIndex = 0; withdrawRatioIndex < withdrawRatio.length; ++withdrawRatioIndex) {
if ((withdrawRatios ? withdrawRatios[liquiditySwap.to] : withdrawRatio)[withdrawRatioIndex] > 0n) {
chainArgs.sweep(vaultAssets[withdrawRatioIndex], router_utils_1.RouterArguments.MSG_SENDER);
}
}
continue;
}
const toVaultAssets = assetsAddresses[liquiditySwap.to];
const toVaultAddress = vaultAddresses[liquiditySwap.to];
const deliveryGasPrice = priceOfDeliveryGas[liquiditySwap.from][liquiditySwap.to];
const ackGasPrice = priceOfAckGas[liquiditySwap.from];
const targetUserAddress = userAddresses[liquiditySwap.to];
const messageVerifyGasCost = messageVerifyGasCosts[liquiditySwap.from][liquiditySwap.to];
const amount = liquiditySwap.vaultTokens;
const remoteChainArgs = new router_utils_1.RouterArguments({
WGAS: (0, catalyst_chain_lists_1.getChainGasToken)(chain.chainId),
});
remoteChainArgs.withdrawMixed(vaultAddress, router_utils_1.RouterArguments.BALANCE_THIS, withdrawRatios ? withdrawRatios[liquiditySwap.to] : withdrawRatio, [0n, 0n, 0n]);
if (unwrapGas[liquiditySwap.to]) {
remoteChainArgs.unwrapGas(targetUserAddress);
}
for (let withdrawRatioIndex = 0; withdrawRatioIndex < withdrawRatio.length; ++withdrawRatioIndex) {
if ((withdrawRatios ? withdrawRatios[liquiditySwap.to] : withdrawRatio)[withdrawRatioIndex] > 0n) {
remoteChainArgs.sweep(toVaultAssets[withdrawRatioIndex], targetUserAddress);
}
}
const routingIncentive = {
maxGasDelivery: router_utils_1.gasCostTable['SENDLIQUIDITY'] +
remoteChainArgs.getGasCostEstimate({ remote: true }),
maxGasAck: router_utils_1.gasCostTable['ACK'],
refundGasTo: refundGasTo,
priceOfDeliveryGas: deliveryGasPrice,
priceOfAckGas: ackGasPrice,
targetDelta: 0n,
messageVerifyGasCost,
};
routingIncentives.push(routingIncentive);
const gasTokenAmount = chainSpecificLiquiditySwaps.length - 1 === j
? router_utils_1.RouterArguments.BALANCE_THIS
: router_utils_1.RouterArguments.getIncentiveCost(routingIncentive);
const targetRoutingCalldata = remoteChainArgs.getCalldata();
const deadline = BigInt((0, utils_1.toDeadline)(14 * 24 * 60 * utils_1.ONE_MIN_MS));
chainArgs.liquiditySwap(vaultAddress, toVaultAddress, toChain.channelId, [0n, 0n], typeof routerAddresses === 'string'
? routerAddresses
: routerAddresses[liquiditySwap.to], gasTokenAmount, routingIncentive, amount, deadline, targetRoutingCalldata);
}
gasUsages.push({
estimatedGasUsedOnRemote: (0, math_utils_1.bigNumberSum)(routingIncentives.map((ri) => ri.maxGasDelivery)),
estimatedGasUsedOnLocalAck: (0, math_utils_1.bigNumberSum)(routingIncentives.map((ri) => ri.maxGasAck)),
estimatedRoutingPayment: (0, math_utils_1.bigNumberSum)(routingIncentives.map((ri) => router_utils_1.RouterArguments.getIncentiveCost(ri))),
});
routerArgs.push(chainArgs);
}
return routerArgs.map((ra, rai) => {
if (ra) {
const estimatedGasUsedOnLocal = ra.getGasCostEstimate();
const toCallParameters = ra.getParams();
const gasUsage = gasUsages[rai];
const estimatedRefund = (gasUsage.estimatedGasUsedOnRemote * 2n) / 12n;
return {
executionInstructions: {
commands: toCallParameters[0],
inputs: toCallParameters[1],
gas: {
...gasUsage,
estimatedGasUsedOnLocal: estimatedGasUsedOnLocal,
estimatedRefundOnAck: estimatedRefund,
},
},
};
}
else {
return {
executionInstructions: undefined,
};
}
});
}
function prepareWithdrawWithLiquiditySwap(userVaultTokens, userWithdrawals, chains, vaults, vaultAddresses, assetsAddresses, userAddresses, messageVerifyGasCosts, priceOfDeliveryGas, priceOfAckGas, refundGasTo, routerAddresses, unwrapGas = [], withdrawRatios = undefined) {
if (chains.length !== vaults.length) {
throw new Error('Different length chains and vaults');
}
const { vaultTokenSwaps: allLiquiditySwaps, boughtUnits } = (0, math_1.findOptimalWithdrawLiquiditySwaps)(vaults, userVaultTokens, userWithdrawals);
const gasUsages = [];
const routerArgs = [];
const transferDetailsArray = [];
for (let i = 0; i < chains.length; ++i) {
const chain = chains[i];
const vaultAddress = vaultAddresses[i];
const vaultAssets = assetsAddresses[i];
const chainSpecificLiquiditySwaps = allLiquiditySwaps.filter((ls) => ls.from === i);
const chainArgs = new router_utils_1.RouterArguments({
WGAS: (0, catalyst_chain_lists_1.getChainGasToken)(chain.chainId),
});
const routingIncentives = [];
let vaultTokensToCollect = 0n;
for (const liquiditySwap of chainSpecificLiquiditySwaps) {
vaultTokensToCollect = vaultTokensToCollect + liquiditySwap.vaultTokens;
}
transferDetailsArray.push({
token: vaultAddress,
amount: vaultTokensToCollect,
});
if (vaultTokensToCollect === 0n) {
routerArgs.push(undefined);
gasUsages.push({
estimatedGasUsedOnRemote: 0n,
estimatedGasUsedOnLocalAck: 0n,
estimatedRoutingPayment: 0n,
});
continue;
}
const vaultUnits = boughtUnits[i];
let allUnits = (0, math_utils_1.bigNumberSum)(vaultUnits);
const withdrawRatio = vaultUnits.map((unitsForToken, i) => {
if (allUnits === 0n) {
return i === 0 ? math_1.WAD : 0n;
}
const percentage = (unitsForToken * math_1.WAD) / allUnits;
allUnits = allUnits - unitsForToken;
return percentage;
});
chainSpecificLiquiditySwaps.sort((a, b) => {
return Number(a.to === i) - Number(b.to === i);
});
for (let j = 0; j < chainSpecificLiquiditySwaps.length; ++j) {
const liquiditySwap = chainSpecificLiquiditySwaps[j];
const toChain = chains[liquiditySwap.to];
if (i === liquiditySwap.to) {
chainArgs.withdrawMixed(vaultAddress, router_utils_1.RouterArguments.BALANCE_THIS, withdrawRatios ? withdrawRatios[liquiditySwap.to] : withdrawRatio, [0n, 0n, 0n]);
if (unwrapGas[liquiditySwap.to]) {
chainArgs.unwrapGas(router_utils_1.RouterArguments.MSG_SENDER);
}
for (let withdrawRatioIndex = 0; withdrawRatioIndex < withdrawRatio.length; ++withdrawRatioIndex) {
if ((withdrawRatios ? withdrawRatios[liquiditySwap.to] : withdrawRatio)[withdrawRatioIndex] > 0n) {
chainArgs.sweep(vaultAssets[withdrawRatioIndex], router_utils_1.RouterArguments.MSG_SENDER);
}
}
continue;
}
const toVaultAssets = assetsAddresses[liquiditySwap.to];
const toVaultAddress = vaultAddresses[liquiditySwap.to];
const deliveryGasPrice = priceOfDeliveryGas[liquiditySwap.from][liquiditySwap.to];
const ackGasPrice = priceOfAckGas[liquiditySwap.from];
const targetUserAddress = userAddresses[liquiditySwap.to];
const messageVerifyGasCost = messageVerifyGasCosts[liquiditySwap.from][liquiditySwap.to];
const amount = liquiditySwap.vaultTokens;
const remoteChainArgs = new router_utils_1.RouterArguments({
WGAS: (0, catalyst_chain_lists_1.getChainGasToken)(chain.chainId),
});
remoteChainArgs.withdrawMixed(vaultAddress, router_utils_1.RouterArguments.BALANCE_THIS, withdrawRatios ? withdrawRatios[liquiditySwap.to] : withdrawRatio, [0n, 0n, 0n]);
if (unwrapGas[liquiditySwap.to]) {
remoteChainArgs.unwrapGas(targetUserAddress);
}
for (let withdrawRatioIndex = 0; withdrawRatioIndex < withdrawRatio.length; ++withdrawRatioIndex) {
if ((withdrawRatios ? withdrawRatios[liquiditySwap.to] : withdrawRatio)[withdrawRatioIndex] > 0n) {
remoteChainArgs.sweep(toVaultAssets[withdrawRatioIndex], targetUserAddress);
}
}
const routingIncentive = {
maxGasDelivery: router_utils_1.gasCostTable['SENDLIQUIDITY'] +
remoteChainArgs.getGasCostEstimate({ remote: true }),
maxGasAck: router_utils_1.gasCostTable['ACK'],
refundGasTo: refundGasTo,
priceOfDeliveryGas: deliveryGasPrice,
priceOfAckGas: ackGasPrice,
targetDelta: 0n,
messageVerifyGasCost,
};
routingIncentives.push(routingIncentive);
const gasTokenAmount = chainSpecificLiquiditySwaps.length - 1 === j
? router_utils_1.RouterArguments.BALANCE_THIS
: router_utils_1.RouterArguments.getIncentiveCost(routingIncentive);
const targetRoutingCalldata = remoteChainArgs.getCalldata();
const deadline = BigInt((0, utils_1.toDeadline)(14 * 24 * 60 * utils_1.ONE_MIN_MS));
chainArgs.liquiditySwap(vaultAddress, toVaultAddress, toChain.channelId, [0n, 0n], typeof routerAddresses === 'string'
? routerAddresses
: routerAddresses[liquiditySwap.to], gasTokenAmount, routingIncentive, amount, deadline, targetRoutingCalldata);
}
gasUsages.push({
estimatedGasUsedOnRemote: (0, math_utils_1.bigNumberSum)(routingIncentives.map((ri) => ri.maxGasDelivery)),
estimatedGasUsedOnLocalAck: (0, math_utils_1.bigNumberSum)(routingIncentives.map((ri) => ri.maxGasAck)),
estimatedRoutingPayment: (0, math_utils_1.bigNumberSum)(routingIncentives.map((ri) => router_utils_1.RouterArguments.getIncentiveCost(ri))),
});
routerArgs.push(chainArgs);
}
return routerArgs.map((chainArgs, chainArgsIndex) => {
const gasUsage = gasUsages[chainArgsIndex];
return {
routerArgs: chainArgs,
gasUsage,
transferDetails: transferDetailsArray[chainArgsIndex],
};
});
}
function prepareWithdrawSamePercentageWithLiquiditySwap(userVaultTokens, percentage, targetVaultIndex, targetAssetIndex, chains, vaults, vaultAddresses, assetsAddresses, userAddresses, messageVerifyGasCosts, priceOfDeliveryGas, priceOfAckGas, refundGasTo, routerAddresses, unwrapGas = [], withdrawRatios = undefined) {
if (chains.length !== vaults.length) {
throw new Error('Different length chains and vaults');
}
const { vaultTokenSwaps: allLiquiditySwaps } = (0, math_1.findMeExpectedWithdrawnUnits)(vaults, userVaultTokens, percentage, targetVaultIndex);
const gasUsages = [];
const routerArgs = [];
const transferDetailsArray = [];
for (let i = 0; i < chains.length; ++i) {
const chain = chains[i];
const vaultAddress = vaultAddresses[i];
const vaultAssets = assetsAddresses[i];
const chainSpecificLiquiditySwaps = allLiquiditySwaps.filter((ls) => ls.from === i);
const chainArgs = new router_utils_1.RouterArguments({
WGAS: (0, catalyst_chain_lists_1.getChainGasToken)(chain.chainId),
});
const routingIncentives = [];
let vaultTokensToCollect = 0n;
for (const liquiditySwap of chainSpecificLiquiditySwaps) {
vaultTokensToCollect = vaultTokensToCollect + liquiditySwap.vaultTokens;
}
transferDetailsArray.push({
token: vaultAddress,
amount: vaultTokensToCollect,
});
if (vaultTokensToCollect === 0n) {
routerArgs.push(undefined);
gasUsages.push({
estimatedGasUsedOnRemote: 0n,
estimatedGasUsedOnLocalAck: 0n,
estimatedRoutingPayment: 0n,
});
continue;
}
const withdrawRatio = vaults[i].balances.map((_, l) => {
if (i !== targetVaultIndex) {
if (l === 0) {
return math_1.WAD;
}
else {
return 0n;
}
}
if (l === targetAssetIndex) {
return math_1.WAD;
}
else {
return 0n;
}
});
chainSpecificLiquiditySwaps.sort((a, b) => {
return Number(a.to === i) - Number(b.to === i);
});
for (let j = 0; j < chainSpecificLiquiditySwaps.length; ++j) {
const liquiditySwap = chainSpecificLiquiditySwaps[j];
const toChain = chains[liquiditySwap.to];
if (i === liquiditySwap.to) {
chainArgs.withdrawMixed(vaultAddress, router_utils_1.RouterArguments.BALANCE_THIS, withdrawRatios ? withdrawRatios[liquiditySwap.to] : withdrawRatio, [0n, 0n, 0n]);
if (unwrapGas[liquiditySwap.to]) {
chainArgs.unwrapGas(router_utils_1.RouterArguments.MSG_SENDER);
}
for (let withdrawRatioIndex = 0; withdrawRatioIndex < withdrawRatio.length; ++withdrawRatioIndex) {
if ((withdrawRatios ? withdrawRatios[liquiditySwap.to] : withdrawRatio)[withdrawRatioIndex] > 0n) {
chainArgs.sweep(vaultAssets[withdrawRatioIndex], router_utils_1.RouterArguments.MSG_SENDER);
}
}
continue;
}
const toVaultAssets = assetsAddresses[liquiditySwap.to];
const toVaultAddress = vaultAddresses[liquiditySwap.to];
const deliveryGasPrice = priceOfDeliveryGas[liquiditySwap.from][liquiditySwap.to];
const ackGasPrice = priceOfAckGas[liquiditySwap.from];
const targetUserAddress = userAddresses[liquiditySwap.to];
const messageVerifyGasCost = messageVerifyGasCosts[liquiditySwap.from][liquiditySwap.to];
const amount = liquiditySwap.vaultTokens;
const remoteChainArgs = new router_utils_1.RouterArguments({
WGAS: (0, catalyst_chain_lists_1.getChainGasToken)(chain.chainId),
});
remoteChainArgs.withdrawMixed(vaultAddress, router_utils_1.RouterArguments.BALANCE_THIS, withdrawRatios ? withdrawRatios[liquiditySwap.to] : withdrawRatio, [0n, 0n, 0n]);
if (unwrapGas[liquiditySwap.to]) {
remoteChainArgs.unwrapGas(targetUserAddress);
}
for (let withdrawRatioIndex = 0; withdrawRatioIndex < withdrawRatio.length; ++withdrawRatioIndex) {
if ((withdrawRatios ? withdrawRatios[liquiditySwap.to] : withdrawRatio)[withdrawRatioIndex] > 0n) {
remoteChainArgs.sweep(toVaultAssets[withdrawRatioIndex], targetUserAddress);
}
}
const routingIncentive = {
maxGasDelivery: router_utils_1.gasCostTable['SENDLIQUIDITY'] +
remoteChainArgs.getGasCostEstimate({ remote: true }),
maxGasAck: router_utils_1.gasCostTable['ACK'],
refundGasTo: refundGasTo,
priceOfDeliveryGas: deliveryGasPrice,
priceOfAckGas: ackGasPrice,
targetDelta: 0n,
messageVerifyGasCost,
};
routingIncentives.push(routingIncentive);
const gasTokenAmount = chainSpecificLiquiditySwaps.length - 1 === j
? router_utils_1.RouterArguments.BALANCE_THIS
: router_utils_1.RouterArguments.getIncentiveCost(routingIncentive);
const targetRoutingCalldata = remoteChainArgs.getCalldata();
const deadline = BigInt((0, utils_1.toDeadline)(14 * 24 * 60 * utils_1.ONE_MIN_MS));
chainArgs.liquiditySwap(vaultAddress, toVaultAddress, toChain.channelId, [0n, 0n], typeof routerAddresses === 'string'
? routerAddresses
: routerAddresses[liquiditySwap.to], gasTokenAmount, routingIncentive, amount, deadline, targetRoutingCalldata);
}
gasUsages.push({
estimatedGasUsedOnRemote: (0, math_utils_1.bigNumberSum)(routingIncentives.map((ri) => ri.maxGasDelivery)),
estimatedGasUsedOnLocalAck: (0, math_utils_1.bigNumberSum)(routingIncentives.map((ri) => ri.maxGasAck)),
estimatedRoutingPayment: (0, math_utils_1.bigNumberSum)(routingIncentives.map((ri) => router_utils_1.RouterArguments.getIncentiveCost(ri))),
});
routerArgs.push(chainArgs);
}
return routerArgs.map((chainArgs, chainArgsIndex) => {
const gasUsage = gasUsages[chainArgsIndex];
return {
routerArgs: chainArgs,
gasUsage,
transferDetails: transferDetailsArray[chainArgsIndex],
};
});
}
//# sourceMappingURL=router.macro.js.map