diff --git a/src/builder/actions/Actions.sol b/src/builder/actions/Actions.sol index 0b86f10..03a5b78 100644 --- a/src/builder/actions/Actions.sol +++ b/src/builder/actions/Actions.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause pragma solidity ^0.8.27; -import {console} from "../console.sol"; +import {console} from "src/builder/console.sol"; import {Accounts} from "src/builder/Accounts.sol"; import {Across, BridgeRoutes, CCTP} from "src/builder/BridgeRoutes.sol"; diff --git a/test/builder/BridgingLogic.t.sol b/test/builder/BridgingLogic.t.sol index a5f2a34..2146ac7 100644 --- a/test/builder/BridgingLogic.t.sol +++ b/test/builder/BridgingLogic.t.sol @@ -207,4 +207,161 @@ contract BridgingLogicTest is Test, QuarkBuilderTest { assertNotEq(result.eip712Data.domainSeparator, hex"", "non-empty domain separator"); assertNotEq(result.eip712Data.hashStruct, hex"", "non-empty hashStruct"); } + + function testSimpleBridgeWithAcrossTransferSucceeds() public { + QuarkBuilder builder = new QuarkBuilder(); + // Note: There are 3e6 USDC on each chain, so the Builder should attempt to bridge 2 USDC to chain 8453 + QuarkBuilder.BuilderResult memory result = builder.transfer( + TransferActionsBuilder.TransferIntent({ + assetSymbol: "USDC", + chainId: 8453, + amount: 5e6, + sender: address(0xb0b), + recipient: address(0xceecee), + blockTimestamp: BLOCK_TIMESTAMP, + preferAcross: true + }), // transfer 5 USDC on chain 8453 to 0xceecee + chainAccountsList_(6e6), // holding 6 USDC in total across chains 1, 8453 + paymentUsd_() + ); + + assertEq(result.paymentCurrency, "usd", "usd currency"); + + // Check the quark operations + assertEq(result.quarkOperations.length, 2, "two operations"); + assertEq( + result.quarkOperations[0].scriptAddress, + address( + uint160( + uint256( + keccak256( + abi.encodePacked( + bytes1(0xff), + /* codeJar address */ + address(CodeJarHelper.CODE_JAR_ADDRESS), + uint256(0), + /* script bytecode */ + keccak256(type(AcrossActions).creationCode) + ) + ) + ) + ) + ), + "script address for bridge action is correct given the code jar address" + ); + assertEq( + result.quarkOperations[0].scriptCalldata, + abi.encodeCall( + AcrossActions.depositV3, + ( + 0x5c7BCd6E7De5423a257D81B442095A1a6ced35C5, // spokePool + address(0xb0b), // depositor + address(0xb0b), // recipient + address(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48), // inputToken + address(0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913), // outputToken + 0x2e14e0, // inputAmount + 0x1e8480, // outputAmount + 8453, // destinationChainId + address(0), // exclusiveRelayer + uint32(BLOCK_TIMESTAMP) - 30 seconds, // quoteTimestamp + uint32(BLOCK_TIMESTAMP + 10 minutes), // fillDeadline + 0, // exclusivityDeadline + new bytes(0), // message + false // useNativeToken + ) + ), + "calldata is AcrossActions.depositV3(...);" + ); + assertEq( + result.quarkOperations[0].expiry, BLOCK_TIMESTAMP + 7 days, "expiry is current blockTimestamp + 7 days" + ); + assertEq(result.quarkOperations[0].nonce, ALICE_DEFAULT_SECRET, "unexpected nonce"); + assertEq(result.quarkOperations[0].isReplayable, false, "isReplayable is false"); + + assertEq( + result.quarkOperations[1].scriptAddress, + address( + uint160( + uint256( + keccak256( + abi.encodePacked( + bytes1(0xff), + /* codeJar address */ + address(CodeJarHelper.CODE_JAR_ADDRESS), + uint256(0), + /* script bytecode */ + keccak256(type(TransferActions).creationCode) + ) + ) + ) + ) + ), + "script address for transfer is correct given the code jar address" + ); + assertEq( + result.quarkOperations[1].scriptCalldata, + abi.encodeCall(TransferActions.transferERC20Token, (usdc_(8453), address(0xceecee), 5e6)), + "calldata is TransferActions.transferERC20Token(USDC_8453, address(0xceecee), 5e6);" + ); + assertEq( + result.quarkOperations[1].expiry, BLOCK_TIMESTAMP + 7 days, "expiry is current blockTimestamp + 7 days" + ); + assertEq(result.quarkOperations[1].nonce, BOB_DEFAULT_SECRET, "unexpected nonce"); + assertEq(result.quarkOperations[1].isReplayable, false, "isReplayable is false"); + + // Check the actions + assertEq(result.actions.length, 2, "two actions"); + assertEq(result.actions[0].chainId, 1, "operation is on chainid 1"); + assertEq(result.actions[0].quarkAccount, address(0xa11ce), "0xa11ce sends the funds"); + assertEq(result.actions[0].actionType, "BRIDGE", "action type is 'BRIDGE'"); + assertEq(result.actions[0].paymentMethod, "OFFCHAIN", "payment method is 'OFFCHAIN'"); + assertEq(result.actions[0].paymentToken, address(0), "payment token is null"); + assertEq(result.actions[0].paymentMaxCost, 0, "payment has no max cost, since 'OFFCHAIN'"); + assertEq(result.actions[0].nonceSecret, ALICE_DEFAULT_SECRET, "unexpected nonce secret"); + assertEq(result.actions[0].totalPlays, 1, "total plays is 1"); + assertEq( + result.actions[0].actionContext, + abi.encode( + Actions.BridgeActionContext({ + price: USDC_PRICE, + token: USDC_1, + assetSymbol: "USDC", + inputAmount: 0x2e14e0, + outputAmount: 0x1e8480, + chainId: 1, + recipient: address(0xb0b), + destinationChainId: 8453, + bridgeType: Actions.BRIDGE_TYPE_ACROSS + }) + ), + "action context encoded from BridgeActionContext" + ); + assertEq(result.actions[1].chainId, 8453, "operation is on chainid 8453"); + assertEq(result.actions[1].quarkAccount, address(0xb0b), "0xb0b sends the funds"); + assertEq(result.actions[1].actionType, "TRANSFER", "action type is 'TRANSFER'"); + assertEq(result.actions[1].paymentMethod, "OFFCHAIN", "payment method is 'OFFCHAIN'"); + assertEq(result.actions[1].paymentToken, address(0), "payment token is null"); + assertEq(result.actions[1].paymentMaxCost, 0, "payment has no max cost, since 'OFFCHAIN'"); + assertEq(result.actions[1].nonceSecret, BOB_DEFAULT_SECRET, "unexpected nonce secret"); + assertEq(result.actions[1].totalPlays, 1, "total plays is 1"); + assertEq( + result.actions[1].actionContext, + abi.encode( + Actions.TransferActionContext({ + amount: 5e6, + price: USDC_PRICE, + token: USDC_8453, + assetSymbol: "USDC", + chainId: 8453, + recipient: address(0xceecee) + }) + ), + "action context encoded from TransferActionContext" + ); + + // TODO: Check the contents of the EIP712 data + assertNotEq(result.eip712Data.digest, hex"", "non-empty digest"); + assertNotEq(result.eip712Data.domainSeparator, hex"", "non-empty domain separator"); + assertNotEq(result.eip712Data.hashStruct, hex"", "non-empty hashStruct"); + } } diff --git a/test/builder/QuarkBuilderTransfer.t.sol b/test/builder/QuarkBuilderTransfer.t.sol index 185d847..e77b983 100644 --- a/test/builder/QuarkBuilderTransfer.t.sol +++ b/test/builder/QuarkBuilderTransfer.t.sol @@ -26,12 +26,6 @@ import {FFI} from "src/builder/FFI.sol"; import {AcrossFFI} from "test/builder/mocks/AcrossFFI.sol"; contract QuarkBuilderTransferTest is Test, QuarkBuilderTest { - function setUp() public { - // Deploy mock FFI for calling Across API - AcrossFFI mockFFI = new AcrossFFI(); - vm.etch(FFI.ACROSS_FFI_ADDRESS, address(mockFFI).code); - } - function transferUsdc_(uint256 chainId, uint256 amount, address recipient, uint256 blockTimestamp) internal pure @@ -455,163 +449,6 @@ contract QuarkBuilderTransferTest is Test, QuarkBuilderTest { assertNotEq(result.eip712Data.hashStruct, hex"", "non-empty hashStruct"); } - function testSimpleBridgeWithAcrossTransferSucceeds() public { - QuarkBuilder builder = new QuarkBuilder(); - // Note: There are 3e6 USDC on each chain, so the Builder should attempt to bridge 2 USDC to chain 8453 - QuarkBuilder.BuilderResult memory result = builder.transfer( - transferToken_({ - assetSymbol: "USDC", - chainId: 8453, - amount: 5e6, - sender: address(0xb0b), - recipient: address(0xceecee), - blockTimestamp: BLOCK_TIMESTAMP, - preferAcross: true - }), // transfer 5 USDC on chain 8453 to 0xceecee - chainAccountsList_(6e6), // holding 6 USDC in total across chains 1, 8453 - paymentUsd_() - ); - - assertEq(result.paymentCurrency, "usd", "usd currency"); - - // Check the quark operations - assertEq(result.quarkOperations.length, 2, "two operations"); - assertEq( - result.quarkOperations[0].scriptAddress, - address( - uint160( - uint256( - keccak256( - abi.encodePacked( - bytes1(0xff), - /* codeJar address */ - address(CodeJarHelper.CODE_JAR_ADDRESS), - uint256(0), - /* script bytecode */ - keccak256(type(AcrossActions).creationCode) - ) - ) - ) - ) - ), - "script address for bridge action is correct given the code jar address" - ); - assertEq( - result.quarkOperations[0].scriptCalldata, - abi.encodeCall( - AcrossActions.depositV3, - ( - 0x5c7BCd6E7De5423a257D81B442095A1a6ced35C5, // spokePool - address(0xb0b), // depositor - address(0xb0b), // recipient - address(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48), // inputToken - address(0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913), // outputToken - 0x2e14e0, // inputAmount - 0x1e8480, // outputAmount - 8453, // destinationChainId - address(0), // exclusiveRelayer - uint32(BLOCK_TIMESTAMP) - 30 seconds, // quoteTimestamp - uint32(BLOCK_TIMESTAMP + 10 minutes), // fillDeadline - 0, // exclusivityDeadline - new bytes(0), // message - false // useNativeToken - ) - ), - "calldata is AcrossActions.depositV3(...);" - ); - assertEq( - result.quarkOperations[0].expiry, BLOCK_TIMESTAMP + 7 days, "expiry is current blockTimestamp + 7 days" - ); - assertEq(result.quarkOperations[0].nonce, ALICE_DEFAULT_SECRET, "unexpected nonce"); - assertEq(result.quarkOperations[0].isReplayable, false, "isReplayable is false"); - - assertEq( - result.quarkOperations[1].scriptAddress, - address( - uint160( - uint256( - keccak256( - abi.encodePacked( - bytes1(0xff), - /* codeJar address */ - address(CodeJarHelper.CODE_JAR_ADDRESS), - uint256(0), - /* script bytecode */ - keccak256(type(TransferActions).creationCode) - ) - ) - ) - ) - ), - "script address for transfer is correct given the code jar address" - ); - assertEq( - result.quarkOperations[1].scriptCalldata, - abi.encodeCall(TransferActions.transferERC20Token, (usdc_(8453), address(0xceecee), 5e6)), - "calldata is TransferActions.transferERC20Token(USDC_8453, address(0xceecee), 5e6);" - ); - assertEq( - result.quarkOperations[1].expiry, BLOCK_TIMESTAMP + 7 days, "expiry is current blockTimestamp + 7 days" - ); - assertEq(result.quarkOperations[1].nonce, BOB_DEFAULT_SECRET, "unexpected nonce"); - assertEq(result.quarkOperations[1].isReplayable, false, "isReplayable is false"); - - // Check the actions - assertEq(result.actions.length, 2, "two actions"); - assertEq(result.actions[0].chainId, 1, "operation is on chainid 1"); - assertEq(result.actions[0].quarkAccount, address(0xa11ce), "0xa11ce sends the funds"); - assertEq(result.actions[0].actionType, "BRIDGE", "action type is 'BRIDGE'"); - assertEq(result.actions[0].paymentMethod, "OFFCHAIN", "payment method is 'OFFCHAIN'"); - assertEq(result.actions[0].paymentToken, address(0), "payment token is null"); - assertEq(result.actions[0].paymentMaxCost, 0, "payment has no max cost, since 'OFFCHAIN'"); - assertEq(result.actions[0].nonceSecret, ALICE_DEFAULT_SECRET, "unexpected nonce secret"); - assertEq(result.actions[0].totalPlays, 1, "total plays is 1"); - assertEq( - result.actions[0].actionContext, - abi.encode( - Actions.BridgeActionContext({ - price: USDC_PRICE, - token: USDC_1, - assetSymbol: "USDC", - inputAmount: 0x2e14e0, - outputAmount: 0x1e8480, - chainId: 1, - recipient: address(0xb0b), - destinationChainId: 8453, - bridgeType: Actions.BRIDGE_TYPE_ACROSS - }) - ), - "action context encoded from BridgeActionContext" - ); - assertEq(result.actions[1].chainId, 8453, "operation is on chainid 8453"); - assertEq(result.actions[1].quarkAccount, address(0xb0b), "0xb0b sends the funds"); - assertEq(result.actions[1].actionType, "TRANSFER", "action type is 'TRANSFER'"); - assertEq(result.actions[1].paymentMethod, "OFFCHAIN", "payment method is 'OFFCHAIN'"); - assertEq(result.actions[1].paymentToken, address(0), "payment token is null"); - assertEq(result.actions[1].paymentMaxCost, 0, "payment has no max cost, since 'OFFCHAIN'"); - assertEq(result.actions[1].nonceSecret, BOB_DEFAULT_SECRET, "unexpected nonce secret"); - assertEq(result.actions[1].totalPlays, 1, "total plays is 1"); - assertEq( - result.actions[1].actionContext, - abi.encode( - Actions.TransferActionContext({ - amount: 5e6, - price: USDC_PRICE, - token: USDC_8453, - assetSymbol: "USDC", - chainId: 8453, - recipient: address(0xceecee) - }) - ), - "action context encoded from TransferActionContext" - ); - - // TODO: Check the contents of the EIP712 data - assertNotEq(result.eip712Data.digest, hex"", "non-empty digest"); - assertNotEq(result.eip712Data.domainSeparator, hex"", "non-empty domain separator"); - assertNotEq(result.eip712Data.hashStruct, hex"", "non-empty hashStruct"); - } - function testSimpleBridgeTransferWithPaycallSucceeds() public { QuarkBuilder builder = new QuarkBuilder(); PaymentInfo.PaymentMaxCost[] memory maxCosts = new PaymentInfo.PaymentMaxCost[](2);