Skip to content
Merged
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
46 changes: 9 additions & 37 deletions scripts/e2e/bridgeViaRelay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ import {
import { execViaAH } from './utils/allowanceHolder';
import { encodeApprove, encodeTransfer, getWalletErc20Balance } from './utils/erc20';
import { ROUTER_ABI } from './utils/routerAbi';
import { ModularActionsBuilder } from './utils/modularActionsBuilder/index';
import type { ModularAction } from './utils/modularActionsBuilder/index';
import {
MonolithicExecution,
Action,
CallType,
NO_FEE,
NO_SWAP,
ZERO_ADDRESS,
Expand Down Expand Up @@ -207,7 +207,7 @@ function buildModularActions(
relaySpender: string,
depositTarget: string,
depositData: string,
): Action[] {
): ModularAction[] {
// AH.transferFrom(token, owner, recipient, amount) = 0x15dacbea
const ahIface = new ethers.Interface([
'function transferFrom(address token, address owner, address recipient, uint256 amount)',
Expand All @@ -219,40 +219,12 @@ function buildModularActions(
inputAmount,
]);

return [
// 0: pull AAVE from user via AllowanceHolder.transferFrom
{
callType: CallType.CALL,
target: ALLOWANCE_HOLDER,
value: 0n,
data: ahTransferFromData,
splices: [],
},
// 1: send pre-bridge fee to signer in AAVE
{
callType: CallType.CALL,
target: TOKENS.AAVE_ARB,
value: 0n,
data: encodeTransfer(signerAddress, feeAmount),
splices: [],
},
// 2: approve Relay spender for bridgeAmount
{
callType: CallType.CALL,
target: TOKENS.AAVE_ARB,
value: 0n,
data: encodeApprove(relaySpender, bridgeAmount),
splices: [],
},
// 3: call Relay deposit — amount already encoded in depositData
{
callType: CallType.CALL,
target: depositTarget,
value: 0n,
data: depositData,
splices: [],
},
];
const exec = new ModularActionsBuilder();
exec.call(ALLOWANCE_HOLDER, ahTransferFromData);
exec.call(TOKENS.AAVE_ARB, encodeTransfer(signerAddress, feeAmount));
exec.call(TOKENS.AAVE_ARB, encodeApprove(relaySpender, bridgeAmount));
exec.call(depositTarget, depositData);
return exec.toActions();
}

// ─── Main ─────────────────────────────────────────────────────────────────────
Expand Down
6 changes: 1 addition & 5 deletions scripts/e2e/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
* used across all e2e scripts.
*/
import * as dotenv from 'dotenv';
import { MaxUint256 } from 'ethers';
dotenv.config();

// ─── Chain IDs ───────────────────────────────────────────────────────────────
Expand All @@ -19,12 +18,9 @@ export const CHAIN_IDS = {
/** 0x AllowanceHolder — same address on every EVM chain */
export const ALLOWANCE_HOLDER = '0x0000000000001fF3684f28c67538d4D072C22734';

/** Deployed BungeeOpenRouterV2Unchecked instance (set via env after deployment) */
/** Deployed combined unchecked router instance (set via env after deployment) */
export const ROUTER_ADDRESS: string = '0x98381Fb4dC5c2046558236857181F4e34a9088dC';

/** Sentinel used in modular actions to forward address(this).balance as msg.value */
export const MAX_UINT256 = MaxUint256;

/** Standard ERC-20 "native" sentinel used by CurrencyLib */
export const NATIVE_TOKEN_ADDRESS =
'0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE';
Expand Down
69 changes: 16 additions & 53 deletions scripts/e2e/swapBridgeViaArbitrumNative.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
* - Monolithic: swap AAVE→ETH (balance delta on NATIVE), take ETH fee,
* call Arbitrum inbox with useFinalAmountAsValue=true so finalAmount
* becomes msg.value on the depositEth call.
* - Modular: pull → approve(oo) → swap(oo) → send ETH fee via low-level call
* depositEth with value=MAX_UINT256 sentinel (forwards address(this).balance).
* - Modular: pull → approve(oo) → swap(oo) → send ETH fee via CALL_WITH_NATIVE
* depositEth via CALL_WITH_NATIVE.
* 5. Call AllowanceHolder.exec with msg.value=0 (AAVE is the input token, not ETH).
*
* Uses the signer’s full AAVE balance on Ethereum mainnet as swap input.
Expand Down Expand Up @@ -50,13 +50,12 @@ import {
import { execViaAH, ensureAllowanceForAllowanceHolder } from './utils/allowanceHolder';
import { encodeApprove, getWalletErc20Balance } from './utils/erc20';
import { ROUTER_ABI } from './utils/routerAbi';
import { ModularActionsBuilder } from './utils/modularActionsBuilder/index';
import type { ModularAction } from './utils/modularActionsBuilder/index';
import {
MonolithicExecution,
Action,
CallType,
NO_FEE,
ZERO_ADDRESS,
USE_CONTRACT_BALANCE,
} from './utils/contractTypes';

// ─── Arbitrum retryable fee estimation ───────────────────────────────────────
Expand Down Expand Up @@ -227,18 +226,18 @@ function buildMonolithicExecution(
* [0] Pull AAVE via AH.transferFrom
* [1] Approve OpenOcean router for inputAmount
* [2] Call OpenOcean to swap AAVE → ETH (lands in router as ETH)
* [3] Send ETH fee to signer via low-level call (value=feeAmount)
* [4] Call Arbitrum inbox depositEth() with value=MAX_UINT256 sentinel
* so _dispatchAction forwards address(this).balance (all remaining ETH)
* [3] Send ETH fee to signer via CALL_WITH_NATIVE
* [4] Call Arbitrum inbox depositEth() via CALL_WITH_NATIVE
*/
function buildModularActions(
signerAddress: string,
routerAddress: string,
inputAmount: bigint,
feeAmount: bigint,
bridgeValue: bigint,
ooRouterAddress: string,
swapData: string,
): Action[] {
): ModularAction[] {
const ahIface = new ethers.Interface([
'function transferFrom(address token, address owner, address recipient, uint256 amount)',
]);
Expand All @@ -249,50 +248,13 @@ function buildModularActions(
inputAmount,
]);

return [
// 0: pull AAVE from user via AllowanceHolder
{
callType: CallType.CALL,
target: ALLOWANCE_HOLDER,
value: 0n,
data: ahTransferFromData,
splices: [],
},
// 1: approve OpenOcean to spend AAVE
{
callType: CallType.CALL,
target: TOKENS.AAVE_ETH,
value: 0n,
data: encodeApprove(ooRouterAddress, inputAmount),
splices: [],
},
// 2: swap AAVE → ETH via OpenOcean (ETH lands in the router)
{
callType: CallType.CALL,
target: ooRouterAddress,
value: 0n,
data: swapData,
splices: [],
},
// 3: send ETH fee to signer — target receives feeAmount as msg.value via CALL
// The signer EOA must be payable; any standard address accepts ETH.
{
callType: CallType.CALL,
target: signerAddress,
value: feeAmount,
data: '0x', // empty calldata = plain ETH transfer
splices: [],
},
// 4: deposit remaining ETH to Arbitrum via inbox.depositEth()
// USE_CONTRACT_BALANCE sentinel → _dispatchAction substitutes address(this).balance
{
callType: CallType.CALL,
target: ARBITRUM_INBOX,
value: USE_CONTRACT_BALANCE,
data: buildDepositEthCalldata(),
splices: [],
},
];
const exec = new ModularActionsBuilder();
exec.call(ALLOWANCE_HOLDER, ahTransferFromData);
exec.call(TOKENS.AAVE_ETH, encodeApprove(ooRouterAddress, inputAmount));
exec.call(ooRouterAddress, swapData);
exec.nativeCall(signerAddress, '0x', feeAmount);
exec.nativeCall(ARBITRUM_INBOX, buildDepositEthCalldata(), bridgeValue);
return exec.toActions();
}

// ─── Main ─────────────────────────────────────────────────────────────────────
Expand Down Expand Up @@ -366,6 +328,7 @@ async function main() {
ROUTER_ADDRESS,
inputAmount,
feeAmount,
minAmountOut > feeAmount ? minAmountOut - feeAmount : 0n,
ooRouterAddress,
swapData,
);
Expand Down
80 changes: 12 additions & 68 deletions scripts/e2e/swapBridgeViaCctp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@ import {
import { execViaAH, ensureAllowanceForAllowanceHolder } from './utils/allowanceHolder';
import { encodeApprove, encodeTransfer, encodeBalanceOf, getWalletErc20Balance } from './utils/erc20';
import { ROUTER_ABI } from './utils/routerAbi';
import { ModularActionsBuilder } from './utils/modularActionsBuilder/index';
import type { ModularAction } from './utils/modularActionsBuilder/index';
import {
MonolithicExecution,
Action,
CallType,
NO_FEE,
ZERO_ADDRESS,
} from './utils/contractTypes';
Expand Down Expand Up @@ -220,7 +220,7 @@ function buildModularActions(
swapData: string,
depositForBurnData: string,
tokenMessenger: string,
): Action[] {
): ModularAction[] {
const ahIface = new ethers.Interface([
'function transferFrom(address token, address owner, address recipient, uint256 amount)',
]);
Expand All @@ -231,71 +231,15 @@ function buildModularActions(
inputAmount,
]);

return [
// 0: pull AAVE from user via AH
{
callType: CallType.CALL,
target: ALLOWANCE_HOLDER,
value: 0n,
data: ahTransferFromData,
splices: [],
},
// 1: approve OpenOcean to spend AAVE
{
callType: CallType.CALL,
target: TOKENS.AAVE_ARB,
value: 0n,
data: encodeApprove(ooRouterAddress, inputAmount),
splices: [],
},
// 2: swap AAVE → USDC via OpenOcean
{
callType: CallType.CALL,
target: ooRouterAddress,
value: 0n,
data: swapData,
splices: [],
},
// 3: send post-swap fee in USDC to signer
{
callType: CallType.CALL,
target: TOKENS.USDC_ARB,
value: 0n,
data: encodeTransfer(signerAddress, feeAmount),
splices: [],
},
// 4: approve TOKEN_MESSENGER for unlimited USDC (router holds exact balance)
{
callType: CallType.CALL,
target: TOKENS.USDC_ARB,
value: 0n,
data: encodeApprove(tokenMessenger, ethers.MaxUint256),
splices: [],
},
// 5: staticcall USDC.balanceOf(router) → prevReturn = ABI-encoded uint256 balance
{
callType: CallType.STATICCALL,
target: TOKENS.USDC_ARB,
value: 0n,
data: encodeBalanceOf(routerAddress),
splices: [],
},
// 6: depositForBurn — splice the 32-byte balance from prevReturn into the
// amount field at dstOffset=4 (first param, after the 4-byte selector)
{
callType: CallType.CALL,
target: tokenMessenger,
value: 0n,
data: depositForBurnData,
splices: [
{
srcOffset: 0n, // read from start of prevReturn (the ABI uint256)
dstOffset: 4n, // write into depositForBurn calldata after selector
length: 32n, // uint256 = 32 bytes
},
],
},
];
const exec = new ModularActionsBuilder();
exec.call(ALLOWANCE_HOLDER, ahTransferFromData);
exec.call(TOKENS.AAVE_ARB, encodeApprove(ooRouterAddress, inputAmount));
exec.call(ooRouterAddress, swapData);
exec.call(TOKENS.USDC_ARB, encodeTransfer(signerAddress, feeAmount));
exec.call(TOKENS.USDC_ARB, encodeApprove(tokenMessenger, ethers.MaxUint256));
const balance = exec.staticCall(TOKENS.USDC_ARB, encodeBalanceOf(routerAddress));
exec.call(tokenMessenger, depositForBurnData).spliceArg(0, balance.returnWord());
return exec.toActions();
}

// ─── Main ─────────────────────────────────────────────────────────────────────
Expand Down
Loading
Loading