Skip to content

Commit 4426aec

Browse files
authored
Merge branch 'master' into pxrl/proposerLog
2 parents 21174fb + 410e460 commit 4426aec

File tree

14 files changed

+368
-121
lines changed

14 files changed

+368
-121
lines changed

src/adapter/bridges/SolanaUsdcCCTPBridge.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,14 @@ import { getCctpTokenMessenger, isCctpV2L2ChainId } from "../../utils/CCTPUtils"
2424
import { CCTP_NO_DOMAIN } from "@across-protocol/constants";
2525
import { arch } from "@across-protocol/sdk";
2626
import { TokenMessengerMinterIdl } from "@across-protocol/contracts";
27+
import { CCTP_MAX_SEND_AMOUNT } from "../../common";
2728

2829
type MintAndWithdrawData = {
2930
mintRecipient: string;
3031
amount: bigint;
3132
};
3233

3334
export class SolanaUsdcCCTPBridge extends BaseBridgeAdapter {
34-
private CCTP_MAX_SEND_AMOUNT = toBN(1_000_000_000_000); // 1MM USDC.
3535
private IS_CCTP_V2 = false;
3636
private readonly l1UsdcTokenAddress: EvmAddress;
3737
private readonly l2UsdcTokenAddress: SvmAddress;
@@ -92,7 +92,7 @@ export class SolanaUsdcCCTPBridge extends BaseBridgeAdapter {
9292
const signer = await this.l1Signer.getAddress();
9393
assert(compareAddressesSimple(signer, toAddress.toEvmAddress()), "Cannot rebalance to a non-signer address");
9494
const associatedTokenAddress = await this._getAssociatedTokenAddress();
95-
amount = amount.gt(this.CCTP_MAX_SEND_AMOUNT) ? this.CCTP_MAX_SEND_AMOUNT : amount;
95+
amount = amount.gt(CCTP_MAX_SEND_AMOUNT) ? CCTP_MAX_SEND_AMOUNT : amount;
9696
return Promise.resolve({
9797
contract: this.getL1Bridge(),
9898
method: "depositForBurn",

src/adapter/bridges/UsdcCCTPBridge.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import {
77
TOKEN_SYMBOLS_MAP,
88
compareAddressesSimple,
99
assert,
10-
toBN,
1110
getCctpDomainForChainId,
1211
Address,
1312
EvmAddress,
@@ -18,9 +17,9 @@ import {
1817
import { processEvent } from "../utils";
1918
import { getCctpTokenMessenger, isCctpV2L2ChainId } from "../../utils/CCTPUtils";
2019
import { CCTP_NO_DOMAIN } from "@across-protocol/constants";
20+
import { CCTP_MAX_SEND_AMOUNT } from "../../common";
2121

2222
export class UsdcCCTPBridge extends BaseBridgeAdapter {
23-
private CCTP_MAX_SEND_AMOUNT = toBN(1_000_000_000_000); // 1MM USDC.
2423
private IS_CCTP_V2 = false;
2524
private readonly l1UsdcTokenAddress: EvmAddress;
2625

@@ -66,7 +65,7 @@ export class UsdcCCTPBridge extends BaseBridgeAdapter {
6665
amount: BigNumber
6766
): Promise<BridgeTransactionDetails> {
6867
assert(l1Token.eq(this.l1UsdcTokenAddress));
69-
amount = amount.gt(this.CCTP_MAX_SEND_AMOUNT) ? this.CCTP_MAX_SEND_AMOUNT : amount;
68+
amount = amount.gt(CCTP_MAX_SEND_AMOUNT) ? CCTP_MAX_SEND_AMOUNT : amount;
7069
return Promise.resolve({
7170
contract: this.getL1Bridge(),
7271
method: "depositForBurn",

src/adapter/l2Bridges/UsdcCCTPBridge.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ import {
2121
} from "../../utils";
2222
import { BaseL2BridgeAdapter } from "./BaseL2BridgeAdapter";
2323
import { AugmentedTransaction } from "../../clients/TransactionClient";
24+
import { CCTP_MAX_SEND_AMOUNT } from "../../common";
2425

2526
export class UsdcCCTPBridge extends BaseL2BridgeAdapter {
26-
private CCTP_MAX_SEND_AMOUNT = toBN(1_000_000_000_000); // 1MM USDC.
2727
private IS_CCTP_V2 = false;
2828
private readonly l1UsdcTokenAddress: EvmAddress;
2929
private readonly l2UsdcTokenAddress: EvmAddress;
@@ -63,7 +63,7 @@ export class UsdcCCTPBridge extends BaseL2BridgeAdapter {
6363
const { decimals } = getTokenInfo(l2Token, this.l2chainId);
6464
const formatter = createFormatFunction(2, 4, false, decimals);
6565

66-
amount = amount.gt(this.CCTP_MAX_SEND_AMOUNT) ? this.CCTP_MAX_SEND_AMOUNT : amount;
66+
amount = amount.gt(CCTP_MAX_SEND_AMOUNT) ? CCTP_MAX_SEND_AMOUNT : amount;
6767
return Promise.resolve([
6868
{
6969
contract: this.l2Bridge,

src/clients/ProfitClient.ts

Lines changed: 59 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -71,13 +71,16 @@ export type FillProfit = {
7171
outputAmountUsd: BigNumber;
7272
grossRelayerFeePct: BigNumber; // Max of relayerFeePct and newRelayerFeePct from Deposit.
7373
grossRelayerFeeUsd: BigNumber; // USD value of the relay fee paid by the user.
74+
auxiliaryNativeTokenCost: BigNumber; // Amount of native tokens forwarded to the user in the units of native token.
75+
auxiliaryNativeTokenCostUsd: BigNumber; // Same as above, but in USD
7476
nativeGasCost: BigNumber; // Cost of completing the fill in the units of gas.
7577
tokenGasCost: BigNumber; // Cost of completing the fill in the relevant gas token.
7678
gasPrice: BigNumber; // Gas price in wei.
7779
gasPadding: BigNumber; // Positive padding applied to nativeGasCost and tokenGasCost before profitability.
7880
gasMultiplier: BigNumber; // Gas multiplier applied to fill cost estimates before profitability.
7981
gasTokenPriceUsd: BigNumber; // Price paid per unit of gas the gas token in USD.
80-
gasCostUsd: BigNumber; // Estimated cost of completing the fill in USD.
82+
gasCostUsd: BigNumber; // Estimated gas cost of completing the fill in USD.
83+
nativeTokenFillCostUsd: BigNumber; // Estimated native token cost of completing the fill in USD: gas + auxiliary
8184
netRelayerFeePct: BigNumber; // Relayer fee after gas costs as a portion of relayerCapitalUsd.
8285
netRelayerFeeUsd: BigNumber; // Relayer fee in USD after paying for gas costs.
8386
totalFeePct: BigNumber; // Total fee as a portion of the fill amount.
@@ -168,11 +171,11 @@ export class ProfitClient {
168171
return isMessageEmpty(resolveDepositMessage(deposit)) ? this.gasMultiplier : this.gasMessageMultiplier;
169172
}
170173

171-
resolveGasToken(chainId: number): L1Token {
174+
resolveNativeToken(chainId: number): L1Token {
172175
const symbol = getNativeTokenSymbol(chainId);
173176
const token = TOKEN_SYMBOLS_MAP[symbol];
174177
if (!isDefined(symbol) || !isDefined(token)) {
175-
throw new Error(`Unable to resolve gas token for chain ID ${chainId}`);
178+
throw new Error(`Unable to resolve native token for chain ID ${chainId}`);
176179
}
177180

178181
const { decimals, addresses } = token;
@@ -181,6 +184,11 @@ export class ProfitClient {
181184
return { symbol, address, decimals };
182185
}
183186

187+
resolveGasToken(chainId: number): L1Token {
188+
// Note for future: gas token and native token may not always be the same
189+
return this.resolveNativeToken(chainId);
190+
}
191+
184192
getAllPrices(): { [address: string]: BigNumber } {
185193
return this.tokenPrices;
186194
}
@@ -265,6 +273,10 @@ export class ProfitClient {
265273
}
266274
}
267275

276+
getAuxiliaryNativeTokenCost(deposit: Deposit): BigNumber {
277+
return this.relayerFeeQueries[deposit.destinationChainId].getAuxiliaryNativeTokenCost(deposit);
278+
}
279+
268280
async getTotalGasCost(deposit: Deposit): Promise<TransactionCostEstimate> {
269281
const { destinationChainId: chainId } = deposit;
270282

@@ -289,7 +301,19 @@ export class ProfitClient {
289301
// Estimate the gas cost of filling this relay.
290302
async estimateFillCost(
291303
deposit: Deposit
292-
): Promise<Pick<FillProfit, "nativeGasCost" | "tokenGasCost" | "gasTokenPriceUsd" | "gasCostUsd" | "gasPrice">> {
304+
): Promise<
305+
Pick<
306+
FillProfit,
307+
| "nativeGasCost"
308+
| "tokenGasCost"
309+
| "gasTokenPriceUsd"
310+
| "gasCostUsd"
311+
| "gasPrice"
312+
| "auxiliaryNativeTokenCost"
313+
| "auxiliaryNativeTokenCostUsd"
314+
| "nativeTokenFillCostUsd"
315+
>
316+
> {
293317
const { destinationChainId: chainId } = deposit;
294318

295319
const gasToken = this.resolveGasToken(chainId);
@@ -321,12 +345,23 @@ export class ProfitClient {
321345

322346
const gasCostUsd = tokenGasCost.mul(gasTokenPriceUsd).div(bn10.pow(gasToken.decimals));
323347

348+
const auxiliaryNativeTokenCost = this.getAuxiliaryNativeTokenCost(deposit);
349+
const nativeToken = this.resolveNativeToken(chainId);
350+
const nativeTokenPriceUsd = this.getPriceOfToken(nativeToken.symbol);
351+
const auxiliaryNativeTokenCostUsd = auxiliaryNativeTokenCost
352+
.mul(nativeTokenPriceUsd)
353+
.div(bn10.pow(nativeToken.decimals));
354+
355+
const nativeTokenFillCostUsd = gasCostUsd.add(auxiliaryNativeTokenCostUsd);
324356
return {
325357
nativeGasCost,
326358
tokenGasCost,
327359
gasPrice,
328360
gasTokenPriceUsd,
329361
gasCostUsd,
362+
auxiliaryNativeTokenCost,
363+
auxiliaryNativeTokenCostUsd,
364+
nativeTokenFillCostUsd,
330365
};
331366
}
332367

@@ -409,14 +444,23 @@ export class ProfitClient {
409444
? grossRelayerFeeUsd.mul(fixedPoint).div(inputAmountUsd)
410445
: bnZero;
411446

412-
// Estimate the gas cost of filling this relay.
413-
const { nativeGasCost, tokenGasCost, gasTokenPriceUsd, gasCostUsd, gasPrice } = await this.estimateFillCost(
414-
deposit
415-
);
447+
const {
448+
// Estimated gas cost of filling this relay
449+
nativeGasCost,
450+
tokenGasCost,
451+
gasTokenPriceUsd,
452+
gasCostUsd,
453+
gasPrice,
454+
// Estimated auxiliary native token cost
455+
auxiliaryNativeTokenCost,
456+
auxiliaryNativeTokenCostUsd,
457+
// Estimated total native token cost in USD
458+
nativeTokenFillCostUsd,
459+
} = await this.estimateFillCost(deposit);
416460

417461
// Determine profitability. netRelayerFeePct effectively represents the capital cost to the relayer;
418462
// i.e. how much it pays out to the recipient vs. the net fee that it receives for doing so.
419-
const netRelayerFeeUsd = grossRelayerFeeUsd.sub(gasCostUsd);
463+
const netRelayerFeeUsd = grossRelayerFeeUsd.sub(nativeTokenFillCostUsd);
420464
const netRelayerFeePct = outputAmountUsd.gt(bnZero)
421465
? netRelayerFeeUsd.mul(fixedPoint).div(outputAmountUsd)
422466
: bnZero;
@@ -433,13 +477,16 @@ export class ProfitClient {
433477
outputAmountUsd,
434478
grossRelayerFeePct,
435479
grossRelayerFeeUsd,
480+
auxiliaryNativeTokenCost,
481+
auxiliaryNativeTokenCostUsd,
436482
nativeGasCost,
437483
tokenGasCost,
438484
gasPrice,
439485
gasPadding: this.gasPadding,
440486
gasMultiplier: this.resolveGasMultiplier(deposit),
441487
gasTokenPriceUsd,
442488
gasCostUsd,
489+
nativeTokenFillCostUsd,
443490
netRelayerFeePct,
444491
netRelayerFeeUsd,
445492
profitable,
@@ -493,6 +540,8 @@ export class ProfitClient {
493540
totalFeePct: `${formatFeePct(fill.totalFeePct)}%`,
494541
lpFeePct: `${formatFeePct(lpFeePct)}%`,
495542
grossRelayerFeePct: `${formatFeePct(fill.grossRelayerFeePct)}%`,
543+
auxiliaryNativeTokenCost: fill.auxiliaryNativeTokenCost,
544+
auxiliaryNativeTokenCostUsd: formatEther(fill.auxiliaryNativeTokenCostUsd),
496545
nativeGasCost: fill.nativeGasCost,
497546
tokenGasCost: formatEther(fill.tokenGasCost),
498547
gasPrice: formatGwei(fill.gasPrice.toString()),
@@ -501,6 +550,7 @@ export class ProfitClient {
501550
gasTokenPriceUsd: formatEther(fill.gasTokenPriceUsd),
502551
grossRelayerFeeUsd: formatEther(fill.grossRelayerFeeUsd),
503552
gasCostUsd: formatEther(fill.gasCostUsd),
553+
nativeTokenFillCostUsd: formatEther(fill.nativeTokenFillCostUsd),
504554
netRelayerFeeUsd: formatEther(fill.netRelayerFeeUsd),
505555
netRelayerFeePct: `${formatFeePct(fill.netRelayerFeePct)}%`,
506556
minRelayerFeePct: `${formatFeePct(minRelayerFeePct)}%`,

src/common/Constants.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
toWei,
1313
BigNumber,
1414
winston,
15+
toBN,
1516
} from "../utils";
1617
import {
1718
BaseBridgeAdapter,
@@ -61,6 +62,9 @@ export const CONFIG_STORE_VERSION = 6;
6162

6263
export const RELAYER_MIN_FEE_PCT = 0.0001;
6364

65+
// The maximum amount of USDC permitted to be sent over CCTP in a single transaction.
66+
export const CCTP_MAX_SEND_AMOUNT = toBN(1_000_000_000_000); // 1MM USDC.
67+
6468
// max(uint256) - 1
6569
export const INFINITE_FILL_DEADLINE = bnUint32Max;
6670

src/finalizer/utils/cctp/l2ToL1.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,21 @@ import {
1919
getStatePda,
2020
toAddressType,
2121
createFormatFunction,
22+
getKitKeypairFromEvmSigner,
23+
isSVMSpokePoolClient,
2224
} from "../../../utils";
2325
import {
2426
AttestedCCTPDeposit,
2527
CCTPMessageStatus,
2628
getAttestedCCTPDeposits,
2729
getCctpMessageTransmitter,
2830
} from "../../../utils/CCTPUtils";
31+
import { bridgeTokensToHubPool } from "./svm";
2932
import { FinalizerPromise, CrossChainMessage, AddressesToFinalize } from "../../types";
3033

3134
export async function cctpL2toL1Finalizer(
3235
logger: winston.Logger,
33-
_signer: Signer,
36+
signer: Signer,
3437
hubPoolClient: HubPoolClient,
3538
spokePoolClient: SpokePoolClient,
3639
_l1SpokePoolClient: SpokePoolClient,
@@ -48,6 +51,18 @@ export async function cctpL2toL1Finalizer(
4851
? await augmentSendersListForSolana(senderAddresses, spokePoolClient)
4952
: senderAddresses;
5053

54+
// Solana has a two step withdrawal process, where the funds in the spoke pool's transfer liability PDA must be manually withdrawn after
55+
// a refund leaf has been executed.
56+
if (finalizingFromSolana) {
57+
assert(isSVMSpokePoolClient(spokePoolClient));
58+
const svmSigner = await getKitKeypairFromEvmSigner(signer);
59+
const bridgeTokens = await bridgeTokensToHubPool(spokePoolClient, svmSigner, logger, hubPoolClient.chainId);
60+
logger[isDefined(bridgeTokens.signature) ? "info" : "debug"]({
61+
at: `Finalizer#CCTPL2ToL1Finalizer:${spokePoolClient.chainId}`,
62+
message: bridgeTokens.message,
63+
signature: bridgeTokens.signature,
64+
});
65+
}
5166
const outstandingDeposits = await getAttestedCCTPDeposits(
5267
augmentedSenderAddresses,
5368
spokePoolClient.chainId,
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
export * from "./l1Tol2";
2+
export * from "./l2Tol1";

0 commit comments

Comments
 (0)