Skip to content

Commit f9ff531

Browse files
committed
cctp: gas drop-off fix
- use wormhole circle relayer instead of token bridge relayer to compute max swap and gas drop-off amounts - the connect interface specifies a percentage that is applied to the the max swap amount to get the gas drop-off amount
1 parent 74eeb75 commit f9ff531

File tree

4 files changed

+42
-9
lines changed

4 files changed

+42
-9
lines changed

connect/src/protocols/cctp/cctpTransfer.ts

+12-6
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,13 @@ import type {
3434
TransferQuote,
3535
TransferReceipt as _TransferReceipt,
3636
} from "../../types.js";
37-
import { TransferState, isAttested, isSourceFinalized, isSourceInitiated } from "../../types.js";
37+
import {
38+
TransferState,
39+
isAttested,
40+
isRedeemed,
41+
isSourceFinalized,
42+
isSourceInitiated,
43+
} from "../../types.js";
3844
import { Wormhole } from "../../wormhole.js";
3945
import type { WormholeTransfer } from "../wormholeTransfer.js";
4046
import { finality } from "@wormhole-foundation/sdk-base";
@@ -537,7 +543,7 @@ export namespace CircleTransfer {
537543
// Fall back to asking the destination chain if this VAA has been redeemed
538544
// assuming we have the full attestation
539545

540-
if (isAttested(receipt)) {
546+
if (isAttested(receipt) || isRedeemed(receipt)) {
541547
const isComplete = await CircleTransfer.isTransferComplete(
542548
_toChain,
543549
receipt.attestation.attestation,
@@ -605,15 +611,15 @@ export namespace CircleTransfer {
605611

606612
// The fee is also removed from the amount transferred
607613
// quoted on the source chain
608-
const stb = await srcChain.getAutomaticCircleBridge();
609-
const fee = await stb.getRelayerFee(dstChain.chain);
614+
const scb = await srcChain.getAutomaticCircleBridge();
615+
const fee = await scb.getRelayerFee(dstChain.chain);
610616
dstAmount -= fee;
611617

612618
// The expected destination gas can be pulled from the destination token bridge
613619
let destinationNativeGas = 0n;
614620
if (transfer.nativeGas) {
615-
const dtb = await dstChain.getAutomaticTokenBridge();
616-
destinationNativeGas = await dtb.nativeTokenAmount(dstToken.address, _nativeGas);
621+
const dcb = await dstChain.getAutomaticCircleBridge();
622+
destinationNativeGas = await dcb.nativeTokenAmount(_nativeGas);
617623
}
618624

619625
return {

connect/src/routes/cctp/automatic.ts

+15-3
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ export class AutomaticCCTPRoute<N extends Network>
168168
);
169169
}
170170

171-
const transferableAmount = amount.units(amt) - fee;
171+
const redeemableAmount = amount.units(amt) - fee;
172172

173173
const options = params.options ?? this.getDefaultOptions();
174174

@@ -179,9 +179,21 @@ export class AutomaticCCTPRoute<N extends Network>
179179
let nativeGasAmount = 0n;
180180

181181
if (nativeGasPerc > 0.0) {
182+
const dcb = await request.toChain.getAutomaticCircleBridge();
183+
let maxSwapAmount = await dcb.maxSwapAmount();
184+
if (redeemableAmount < maxSwapAmount) {
185+
// can't swap more than the receivable amount
186+
maxSwapAmount = redeemableAmount;
187+
}
188+
182189
const scale = 10000;
183-
const scaledGas = BigInt(nativeGasPerc * scale);
184-
nativeGasAmount = (transferableAmount * scaledGas) / BigInt(scale);
190+
const scaledGas = BigInt(Math.floor(nativeGasPerc * scale));
191+
// the native gas percentage is applied to the max swap amount
192+
nativeGasAmount = (maxSwapAmount * scaledGas) / BigInt(scale);
193+
if (nativeGasAmount === redeemableAmount && nativeGasAmount > 0n) {
194+
// edge case: transfer will revert if the native gas amount is equal to the redeemable amount
195+
nativeGasAmount -= 1n;
196+
}
185197
}
186198

187199
return {

core/definitions/src/protocols/circleBridge/circleBridge.ts

+4
Original file line numberDiff line numberDiff line change
@@ -187,4 +187,8 @@ export interface AutomaticCircleBridge<N extends Network = Network, C extends Ch
187187
amount: bigint,
188188
nativeGas?: bigint,
189189
): AsyncGenerator<UnsignedTransaction<N, C>>;
190+
/** Amount of native tokens a user would receive by swapping x amount of sending tokens */
191+
nativeTokenAmount(amount: bigint): Promise<bigint>;
192+
/** Maximum amount of sending tokens that can be swapped for native tokens */
193+
maxSwapAmount(): Promise<bigint>;
190194
}

platforms/evm/protocols/cctp/src/automaticCircleBridge.ts

+11
Original file line numberDiff line numberDiff line change
@@ -163,4 +163,15 @@ export class EvmAutomaticCircleBridge<N extends Network, C extends EvmChains>
163163
parallelizable,
164164
);
165165
}
166+
167+
async nativeTokenAmount(amount: bigint): Promise<bigint> {
168+
return await this.circleRelayer.calculateNativeSwapAmountOut(
169+
this.tokenAddr,
170+
amount,
171+
);
172+
}
173+
174+
async maxSwapAmount(): Promise<bigint> {
175+
return await this.circleRelayer.calculateMaxSwapAmountIn(this.tokenAddr);
176+
}
166177
}

0 commit comments

Comments
 (0)