Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 12 additions & 10 deletions api/_bridges/cctp-sponsored/strategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ import {
MAX_BPS_TO_SPONSOR_LIMIT,
} from "../../_sponsorship-eligibility";
import { TOKEN_SYMBOLS_MAP } from "../../_constants";
import { isToLighter } from "../../_lighter";

const name = "sponsored-cctp" as const;

Expand Down Expand Up @@ -184,16 +185,17 @@ export async function getQuoteForExactInput(
useForwardFee: false,
}).getQuoteForExactInput({
...params,
outputToken: isSwapPair
? {
...TOKEN_SYMBOLS_MAP["USDC-SPOT"],
address:
TOKEN_SYMBOLS_MAP["USDC-SPOT"].addresses[
params.outputToken.chainId
],
chainId: params.outputToken.chainId,
}
: params.outputToken,
outputToken:
isSwapPair && !isToLighter(params.outputToken.chainId)
? {
...TOKEN_SYMBOLS_MAP["USDC-SPOT"],
address:
TOKEN_SYMBOLS_MAP["USDC-SPOT"].addresses[
params.outputToken.chainId
],
chainId: params.outputToken.chainId,
}
: params.outputToken,
});
outputAmount = unsponsoredOutputAmount;
provider = "cctp";
Expand Down
5 changes: 5 additions & 0 deletions api/_bridges/cctp-sponsored/utils/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ export const SPONSORED_CCTP_ORIGIN_CHAINS = CCTP_SUPPORTED_CHAINS.filter(
CHAIN_IDs.HYPERCORE_TESTNET,
CHAIN_IDs.HYPEREVM,
CHAIN_IDs.HYPEREVM_TESTNET,
CHAIN_IDs.LIGHTER,
].includes(chainId)
);

Expand All @@ -74,6 +75,8 @@ export const SPONSORED_CCTP_OUTPUT_TOKENS = [
"USDC-SPOT",
"USDH-SPOT",
"USDT-SPOT",
"USDC-SPOT-LIGHTER",
"USDC-PERPS-LIGHTER",
"USDC",
];

Expand All @@ -84,6 +87,8 @@ export const SPONSORED_CCTP_FINAL_TOKEN_PER_OUTPUT_TOKEN: Record<
"USDC-SPOT": TOKEN_SYMBOLS_MAP.USDC,
"USDH-SPOT": TOKEN_SYMBOLS_MAP.USDH,
"USDT-SPOT": TOKEN_SYMBOLS_MAP.USDT,
"USDC-SPOT-LIGHTER": TOKEN_SYMBOLS_MAP["USDC-SPOT-LIGHTER"],
"USDC-PERPS-LIGHTER": TOKEN_SYMBOLS_MAP["USDC-PERPS-LIGHTER"],
USDC: TOKEN_SYMBOLS_MAP.USDC,
};

Expand Down
4 changes: 1 addition & 3 deletions api/_bridges/cctp-sponsored/utils/quote-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,8 @@ export async function buildSponsoredCCTPQuote(
const actionData = isDestinationLighter
? await buildLighterDepositActionData({
recipient,
outputTokenSymbol: outputToken.symbol,
routeType: 0,
outputAmount,
destinationChainId: outputToken.chainId,
outputToken,
sponsoredCCTPDstPeripheryAddress,
})
: "0x";
Expand Down
2 changes: 2 additions & 0 deletions api/_bridges/cctp/utils/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ export const CCTP_SUPPORTED_CHAINS = [
export const CCTP_SUPPORTED_TOKENS = [
TOKEN_SYMBOLS_MAP.USDC,
TOKEN_SYMBOLS_MAP["USDC-SPOT"],
TOKEN_SYMBOLS_MAP["USDC-SPOT-LIGHTER"],
TOKEN_SYMBOLS_MAP["USDC-PERPS-LIGHTER"],
];

export const getCctpDomainId = (chainId: number): number => {
Expand Down
24 changes: 24 additions & 0 deletions api/_constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,28 @@ export const TOKEN_SYMBOLS_MAP = {
},
coingeckoId: constants.TOKEN_SYMBOLS_MAP.USDC.coingeckoId,
},
"USDC-SPOT-LIGHTER": {
name: "USDC",
symbol: "USDC-SPOT-LIGHTER",
decimals: 6,
addresses: {
[CHAIN_IDs.LIGHTER]: "0x0000000000000000000000000000000000000301",
[CHAIN_IDs.MAINNET]:
constants.TOKEN_SYMBOLS_MAP.USDC.addresses[CHAIN_IDs.MAINNET],
},
coingeckoId: constants.TOKEN_SYMBOLS_MAP.USDC.coingeckoId,
},
"USDC-PERPS-LIGHTER": {
name: "USDC",
symbol: "USDC-PERPS-LIGHTER",
decimals: 6,
addresses: {
[CHAIN_IDs.LIGHTER]: "0x0000000000000000000000000000000000000300",
[CHAIN_IDs.MAINNET]:
constants.TOKEN_SYMBOLS_MAP.USDC.addresses[CHAIN_IDs.MAINNET],
},
coingeckoId: constants.TOKEN_SYMBOLS_MAP.USDC.coingeckoId,
},
};

export const CHAINS = {
Expand All @@ -113,6 +135,8 @@ export const TOKEN_EQUIVALENCE_REMAPPING: Record<string, string> = {
"USDH-SPOT": "USDH",
"USDC-SPOT": "USDC",
"USDT-SPOT": "USDT",
"USDC-SPOT-LIGHTER": "USDC",
"USDC-PERPS-LIGHTER": "USDC",
};
export const CCTP_NO_DOMAIN = constants.CCTP_NO_DOMAIN;

Expand Down
83 changes: 51 additions & 32 deletions api/_lighter.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { utils, BigNumber } from "ethers";
import { utils, BigNumber, ethers } from "ethers";
import { SponsoredCCTPDstPeriphery__factory } from "@across-protocol/contracts/dist/typechain";

import { CHAIN_IDs, TOKEN_SYMBOLS_MAP } from "./_constants";
import { encodeMakeCallWithBalanceCalldata } from "./_multicall-handler";
import { getProvider } from "./_providers";
import { AmountTooLowError } from "./_errors";
import { Token } from "./_dexes/types";
import { getCachedTokenInfo } from "./_utils";

const ZK_LIGHTER_ADDRESSES = {
// https://etherscan.io/address/0x3B4D794a66304F130a4Db8F2551B0070dfCf5ca7
Expand All @@ -15,10 +17,6 @@ const LIGHTER_INTERMEDIARY_CHAIN_IDS = {
[CHAIN_IDs.LIGHTER]: CHAIN_IDs.MAINNET,
};

const LIGHTER_ASSET_INDICES_PER_TOKEN = {
[TOKEN_SYMBOLS_MAP.USDC.symbol]: 3,
};

const LIGHTER_DEPOSIT_ABI = [
{
inputs: [
Expand All @@ -38,7 +36,8 @@ const LIGHTER_DEPOSIT_ABI = [
// - https://mainnet.zklighter.elliot.ai/api/v1/assetDetails
// - https://etherscan.io/address/0xe5fb592ef1b620909000af0d5fb55a3593026142#code#F1#L132
const LIGHTER_MIN_DEPOSIT_AMOUNT = {
[TOKEN_SYMBOLS_MAP.USDC.symbol]: BigNumber.from(1000000),
[TOKEN_SYMBOLS_MAP["USDC-SPOT-LIGHTER"].symbol]: BigNumber.from(1000000),
[TOKEN_SYMBOLS_MAP["USDC-PERPS-LIGHTER"].symbol]: BigNumber.from(1000000),
};

export function isToLighter(chainId: number) {
Expand All @@ -58,32 +57,21 @@ export function getLighterIntermediaryChainId(destinationChainId: number) {

export async function buildLighterDepositActionData(params: {
recipient: string;
outputTokenSymbol: string;
routeType: number;
outputAmount: BigNumber;
destinationChainId: number;
outputToken: Token;
sponsoredCCTPDstPeripheryAddress: string;
}) {
const {
recipient,
outputTokenSymbol,
routeType,
outputAmount,
destinationChainId,
outputToken,
sponsoredCCTPDstPeripheryAddress,
} = params;

const assetIndex = LIGHTER_ASSET_INDICES_PER_TOKEN[outputTokenSymbol];
if (!assetIndex) {
throw new Error(
`Lighter 'assetIndex' not found for token symbol ${outputTokenSymbol}`
);
}

const minDepositAmount = LIGHTER_MIN_DEPOSIT_AMOUNT[outputTokenSymbol];
const minDepositAmount = LIGHTER_MIN_DEPOSIT_AMOUNT[outputToken.symbol];
if (!minDepositAmount) {
throw new Error(
`Lighter 'minDepositAmount' not found for token symbol ${outputTokenSymbol}`
`Lighter 'minDepositAmount' not found for token symbol ${outputToken.symbol}`
);
}

Expand All @@ -93,18 +81,12 @@ export async function buildLighterDepositActionData(params: {
});
}

const intermediaryChainId = getLighterIntermediaryChainId(destinationChainId);
const intermediaryChainId = getLighterIntermediaryChainId(
outputToken.chainId
);

const intermediaryTokenAddress =
TOKEN_SYMBOLS_MAP[outputTokenSymbol as keyof typeof TOKEN_SYMBOLS_MAP]
?.addresses[intermediaryChainId];
if (!intermediaryTokenAddress) {
throw new Error(
`Lighter 'intermediaryTokenAddress' not found for token symbol ${
outputTokenSymbol
} on chain ${intermediaryChainId}`
);
}
const intermediaryToken = await getIntermediaryToken(intermediaryChainId);
const intermediaryTokenAddress = intermediaryToken.address;

const lighterAddress = ZK_LIGHTER_ADDRESSES[intermediaryChainId];
if (!lighterAddress) {
Expand All @@ -113,6 +95,10 @@ export async function buildLighterDepositActionData(params: {
);
}

const { assetIndex, routeType } = decodeLighterTokenAddress(
outputToken.address
);

// Calldata for calling the Lighter's 'deposit' function on the intermediary chain
const lighterDepositInterface = new utils.Interface(LIGHTER_DEPOSIT_ABI);
const lighterDepositCalldata = lighterDepositInterface.encodeFunctionData(
Expand Down Expand Up @@ -155,3 +141,36 @@ export async function buildLighterDepositActionData(params: {
[compressedCalls]
);
}

export function decodeLighterTokenAddress(tokenAddress: string) {
const assetIndex = ethers.utils.hexlify(
ethers.utils.arrayify(tokenAddress).slice(17, 19)
);
const routeType = ethers.utils.hexlify(
ethers.utils.arrayify(tokenAddress).slice(19)
);
return {
assetIndex: parseInt(assetIndex, 16),
routeType: parseInt(routeType, 16),
};
}

async function getIntermediaryToken(
intermediaryChainId: number
): Promise<Token> {
const intermediaryUsdcAddress =
TOKEN_SYMBOLS_MAP["USDC"].addresses[intermediaryChainId];

if (!intermediaryUsdcAddress) {
throw new Error(
`Intermediary USDC address not found for chain ${intermediaryChainId}`
);
}

const tokenInfo = await getCachedTokenInfo({
address: intermediaryUsdcAddress,
chainId: intermediaryChainId,
});

return tokenInfo;
}
3 changes: 2 additions & 1 deletion api/_sponsorship-eligibility.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,8 @@ export const INPUT_AMOUNT_LIMITS_PER_TOKEN_PAIR: {
"USDH-SPOT": utils.parseUnits("1000000", 6), // 1M USDC
"USDT-SPOT": utils.parseUnits("1000000", 6), // 1M USDT
"USDC-SPOT": utils.parseUnits("10000000", 6), // 10M USDC
USDC: utils.parseUnits("1000000", 6), // 1M USDC
"USDC-SPOT-LIGHTER": utils.parseUnits("10000000", 6), // 10M USDC
"USDC-PERPS-LIGHTER": utils.parseUnits("10000000", 6), // 10M USDC
},
USDT: {
"USDC-SPOT": utils.parseUnits("1000000", 6), // 1M USDT
Expand Down
Loading