From 643967290a911c9c4b3d3268ce97c70e6dc22105 Mon Sep 17 00:00:00 2001 From: ashwinrava <213675439+ashwinrava@users.noreply.github.com> Date: Fri, 19 Dec 2025 14:38:34 -0500 Subject: [PATCH 1/4] Add custom USDC address for Lighter spot and perps --- .../cctp-sponsored/utils/quote-builder.ts | 4 +- api/_constants.ts | 18 ++++++++ api/_lighter.ts | 45 +++++++++---------- 3 files changed, 41 insertions(+), 26 deletions(-) diff --git a/api/_bridges/cctp-sponsored/utils/quote-builder.ts b/api/_bridges/cctp-sponsored/utils/quote-builder.ts index 4a452c0c5..ef498737c 100644 --- a/api/_bridges/cctp-sponsored/utils/quote-builder.ts +++ b/api/_bridges/cctp-sponsored/utils/quote-builder.ts @@ -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"; diff --git a/api/_constants.ts b/api/_constants.ts index 5a16f43d2..6d1877c42 100644 --- a/api/_constants.ts +++ b/api/_constants.ts @@ -87,6 +87,22 @@ 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]: "0x0000000000000000000000000000000000000031", + }, + }, + "USDC-PERPS-LIGHTER": { + name: "USDC", + symbol: "USDC-PERPS-LIGHTER", + decimals: 6, + addresses: { + [CHAIN_IDs.LIGHTER]: "0x0000000000000000000000000000000000000030", + }, + }, }; export const CHAINS = { @@ -113,6 +129,8 @@ export const TOKEN_EQUIVALENCE_REMAPPING: Record = { "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; diff --git a/api/_lighter.ts b/api/_lighter.ts index 82b632f16..ed8328333 100644 --- a/api/_lighter.ts +++ b/api/_lighter.ts @@ -1,10 +1,11 @@ -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"; const ZK_LIGHTER_ADDRESSES = { // https://etherscan.io/address/0x3B4D794a66304F130a4Db8F2551B0070dfCf5ca7 @@ -15,10 +16,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: [ @@ -58,32 +55,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}` ); } @@ -93,15 +79,17 @@ 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] + TOKEN_SYMBOLS_MAP[outputToken.symbol as keyof typeof TOKEN_SYMBOLS_MAP] ?.addresses[intermediaryChainId]; if (!intermediaryTokenAddress) { throw new Error( `Lighter 'intermediaryTokenAddress' not found for token symbol ${ - outputTokenSymbol + outputToken.symbol } on chain ${intermediaryChainId}` ); } @@ -113,6 +101,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( @@ -155,3 +147,10 @@ export async function buildLighterDepositActionData(params: { [compressedCalls] ); } + +export function decodeLighterTokenAddress(tokenAddress: string) { + const tokenAddressBytes = ethers.utils.arrayify(tokenAddress); + const assetIndex = tokenAddressBytes.slice(17, 19); // uint16 from bytes 17-18 + const routeType = tokenAddressBytes.slice(19, 20); // uint8 from byte 19 + return { assetIndex, routeType }; +} From 2f89f00c797ade38102d506976ccfaead66d6113 Mon Sep 17 00:00:00 2001 From: ashwinrava <213675439+ashwinrava@users.noreply.github.com> Date: Fri, 19 Dec 2025 14:54:21 -0500 Subject: [PATCH 2/4] Fix intermediary chain and token --- api/_lighter.ts | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/api/_lighter.ts b/api/_lighter.ts index ed8328333..57b3d6c6e 100644 --- a/api/_lighter.ts +++ b/api/_lighter.ts @@ -6,6 +6,7 @@ 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 @@ -35,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) { @@ -83,16 +85,8 @@ export async function buildLighterDepositActionData(params: { outputToken.chainId ); - const intermediaryTokenAddress = - TOKEN_SYMBOLS_MAP[outputToken.symbol as keyof typeof TOKEN_SYMBOLS_MAP] - ?.addresses[intermediaryChainId]; - if (!intermediaryTokenAddress) { - throw new Error( - `Lighter 'intermediaryTokenAddress' not found for token symbol ${ - outputToken.symbol - } on chain ${intermediaryChainId}` - ); - } + const intermediaryToken = await getIntermediaryToken(intermediaryChainId); + const intermediaryTokenAddress = intermediaryToken.address; const lighterAddress = ZK_LIGHTER_ADDRESSES[intermediaryChainId]; if (!lighterAddress) { @@ -154,3 +148,23 @@ export function decodeLighterTokenAddress(tokenAddress: string) { const routeType = tokenAddressBytes.slice(19, 20); // uint8 from byte 19 return { assetIndex, routeType }; } + +async function getIntermediaryToken( + intermediaryChainId: number +): Promise { + 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; +} From 4ce35ccca8b036a7aa9bdc17df04325c0985abf6 Mon Sep 17 00:00:00 2001 From: ashwinrava <213675439+ashwinrava@users.noreply.github.com> Date: Fri, 19 Dec 2025 15:50:37 -0500 Subject: [PATCH 3/4] More fixes --- api/_bridges/cctp-sponsored/strategy.ts | 22 ++++++++++--------- .../cctp-sponsored/utils/constants.ts | 5 +++++ api/_bridges/cctp/utils/constants.ts | 2 ++ api/_constants.ts | 8 +++++-- api/_lighter.ts | 14 ++++++++---- api/_sponsorship-eligibility.ts | 3 ++- 6 files changed, 37 insertions(+), 17 deletions(-) diff --git a/api/_bridges/cctp-sponsored/strategy.ts b/api/_bridges/cctp-sponsored/strategy.ts index 19881a806..f2d471c8e 100644 --- a/api/_bridges/cctp-sponsored/strategy.ts +++ b/api/_bridges/cctp-sponsored/strategy.ts @@ -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; @@ -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"; diff --git a/api/_bridges/cctp-sponsored/utils/constants.ts b/api/_bridges/cctp-sponsored/utils/constants.ts index 4024ad4a9..d4c362bee 100644 --- a/api/_bridges/cctp-sponsored/utils/constants.ts +++ b/api/_bridges/cctp-sponsored/utils/constants.ts @@ -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) ); @@ -74,6 +75,8 @@ export const SPONSORED_CCTP_OUTPUT_TOKENS = [ "USDC-SPOT", "USDH-SPOT", "USDT-SPOT", + "USDC-SPOT-LIGHTER", + "USDC-PERPS-LIGHTER", "USDC", ]; @@ -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, }; diff --git a/api/_bridges/cctp/utils/constants.ts b/api/_bridges/cctp/utils/constants.ts index fc6ee1541..009672911 100644 --- a/api/_bridges/cctp/utils/constants.ts +++ b/api/_bridges/cctp/utils/constants.ts @@ -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 => { diff --git a/api/_constants.ts b/api/_constants.ts index 6d1877c42..cdcb24103 100644 --- a/api/_constants.ts +++ b/api/_constants.ts @@ -92,7 +92,9 @@ export const TOKEN_SYMBOLS_MAP = { symbol: "USDC-SPOT-LIGHTER", decimals: 6, addresses: { - [CHAIN_IDs.LIGHTER]: "0x0000000000000000000000000000000000000031", + [CHAIN_IDs.LIGHTER]: "0x0000000000000000000000000000000000000301", + [CHAIN_IDs.MAINNET]: + constants.TOKEN_SYMBOLS_MAP.USDC.addresses[CHAIN_IDs.MAINNET], }, }, "USDC-PERPS-LIGHTER": { @@ -100,7 +102,9 @@ export const TOKEN_SYMBOLS_MAP = { symbol: "USDC-PERPS-LIGHTER", decimals: 6, addresses: { - [CHAIN_IDs.LIGHTER]: "0x0000000000000000000000000000000000000030", + [CHAIN_IDs.LIGHTER]: "0x0000000000000000000000000000000000000300", + [CHAIN_IDs.MAINNET]: + constants.TOKEN_SYMBOLS_MAP.USDC.addresses[CHAIN_IDs.MAINNET], }, }, }; diff --git a/api/_lighter.ts b/api/_lighter.ts index 57b3d6c6e..a6a9510f9 100644 --- a/api/_lighter.ts +++ b/api/_lighter.ts @@ -143,10 +143,16 @@ export async function buildLighterDepositActionData(params: { } export function decodeLighterTokenAddress(tokenAddress: string) { - const tokenAddressBytes = ethers.utils.arrayify(tokenAddress); - const assetIndex = tokenAddressBytes.slice(17, 19); // uint16 from bytes 17-18 - const routeType = tokenAddressBytes.slice(19, 20); // uint8 from byte 19 - return { assetIndex, routeType }; + 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( diff --git a/api/_sponsorship-eligibility.ts b/api/_sponsorship-eligibility.ts index 15763119b..4db04efe6 100644 --- a/api/_sponsorship-eligibility.ts +++ b/api/_sponsorship-eligibility.ts @@ -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 From 2c4b80f903f018dd966cad74b4ba29c39c190306 Mon Sep 17 00:00:00 2001 From: ashwinrava <213675439+ashwinrava@users.noreply.github.com> Date: Fri, 19 Dec 2025 16:56:19 -0500 Subject: [PATCH 4/4] Fix for TOKEN_SYMBOLS_MAP --- api/_constants.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/api/_constants.ts b/api/_constants.ts index cdcb24103..e8feacbbf 100644 --- a/api/_constants.ts +++ b/api/_constants.ts @@ -96,6 +96,7 @@ export const TOKEN_SYMBOLS_MAP = { [CHAIN_IDs.MAINNET]: constants.TOKEN_SYMBOLS_MAP.USDC.addresses[CHAIN_IDs.MAINNET], }, + coingeckoId: constants.TOKEN_SYMBOLS_MAP.USDC.coingeckoId, }, "USDC-PERPS-LIGHTER": { name: "USDC", @@ -106,6 +107,7 @@ export const TOKEN_SYMBOLS_MAP = { [CHAIN_IDs.MAINNET]: constants.TOKEN_SYMBOLS_MAP.USDC.addresses[CHAIN_IDs.MAINNET], }, + coingeckoId: constants.TOKEN_SYMBOLS_MAP.USDC.coingeckoId, }, };