From dc032394d04d48d3ffad563555e766c9620da8c2 Mon Sep 17 00:00:00 2001 From: Leandro Faria Date: Wed, 30 Oct 2024 15:44:20 -0500 Subject: [PATCH 01/93] add cross-chain module, contracts compile --- src/templates/modules/IPP_Template_v1.sol | 34 ++ .../tests/unit/Interfaces/IEverClearSpoke.sol | 114 +++++ src/templates/tests/unit/PP_Template_v1.t.sol | 388 ++++++++++-------- .../tests/unit/PP_Template_v1_Exposed.sol | 65 +++ 4 files changed, 435 insertions(+), 166 deletions(-) create mode 100644 src/templates/tests/unit/Interfaces/IEverClearSpoke.sol diff --git a/src/templates/modules/IPP_Template_v1.sol b/src/templates/modules/IPP_Template_v1.sol index 61a8757c8..a8ca0d9ec 100644 --- a/src/templates/modules/IPP_Template_v1.sol +++ b/src/templates/modules/IPP_Template_v1.sol @@ -36,6 +36,15 @@ interface IPP_Template_v1 is IPaymentProcessor_v1 { //-------------------------------------------------------------------------- // Structs + /// @notice Struct to hold cross-chain message data + struct CrossChainMessage { + uint messageId; + address sourceChain; + address targetChain; + bytes payload; + bool executed; + } + //-------------------------------------------------------------------------- // Events @@ -46,6 +55,20 @@ interface IPP_Template_v1 is IPaymentProcessor_v1 { uint indexed oldPayoutAmount_, uint indexed newPayoutAmount_ ); + /// @notice Emitted when a cross-chain message is sent + /// @param messageId Unique identifier for the message + /// @param targetChain Address of the target chain + event CrossChainMessageSent( + uint indexed messageId, address indexed targetChain + ); + + /// @notice Emitted when a cross-chain message is received + /// @param messageId Unique identifier for the message + /// @param sourceChain Address of the source chain + event CrossChainMessageReceived( + uint indexed messageId, address indexed sourceChain + ); + //-------------------------------------------------------------------------- // Errors @@ -55,6 +78,17 @@ interface IPP_Template_v1 is IPaymentProcessor_v1 { /// @notice Client is not valid. error Module__PP_Template__ClientNotValid(); + error Module__PP_CrossChain__NotValidClient(); + + error Module__PP_CrossChain__InvalidAmount(); + + /// @notice Message has already been executed + error Module__PP_CrossChain_MessageAlreadyExecuted(); + /// @notice Invalid chain ID provided + error Module__PP_CrossChain_InvalidChainId(); + /// @notice Message verification failed + error Module__PP_CrossChain_MessageVerificationFailed(); + //-------------------------------------------------------------------------- // Public (Getter) diff --git a/src/templates/tests/unit/Interfaces/IEverClearSpoke.sol b/src/templates/tests/unit/Interfaces/IEverClearSpoke.sol new file mode 100644 index 000000000..6897d45cc --- /dev/null +++ b/src/templates/tests/unit/Interfaces/IEverClearSpoke.sol @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +/** + * @title IEverclearSpoke + * @notice Interface for the Everclear spoke contract + */ +interface IEverclearSpoke { + /*/////////////////////////////////////////////////////////////// + STRUCTS + //////////////////////////////////////////////////////////////*/ + struct Intent { + bytes32 initiator; + bytes32 receiver; + bytes32 inputAsset; + bytes32 outputAsset; + uint256 amount; + uint24 maxFee; + uint32 origin; + uint32[] destinations; + uint256 nonce; + uint48 timestamp; + uint48 ttl; + bytes data; + } + + struct FillMessage { + bytes32 intentId; + bytes32 initiator; + bytes32 solver; + uint48 executionTimestamp; + uint24 fee; + } + + struct Permit2Params { + uint256 nonce; + uint256 deadline; + bytes signature; + } + + struct SpokeInitializationParams { + address gateway; + address messageReceiver; + address lighthouse; + address watchtower; + address callExecutor; + uint32 hubDomain; + address owner; + } + + /*/////////////////////////////////////////////////////////////// + FUNCTIONS + //////////////////////////////////////////////////////////////*/ + function initialize(SpokeInitializationParams calldata init) external; + function pause() external; + function unpause() external; + function updateSecurityModule(address newSecurityModule) external; + function newIntent( + uint32[] memory destinations, + address to, + address inputAsset, + address outputAsset, + uint256 amount, + uint24 maxFee, + uint48 ttl, + bytes calldata data + ) external returns (bytes32 intentId, Intent memory intent); + function newIntent( + uint32[] memory destinations, + address to, + address inputAsset, + address outputAsset, + uint256 amount, + uint24 maxFee, + uint48 ttl, + bytes calldata data, + Permit2Params calldata permit2Params + ) external returns (bytes32 intentId, Intent memory intent); + function fillIntent(Intent calldata intent, uint24 fee) external returns (FillMessage memory fillMessage); + function fillIntentForSolver( + address solver, + Intent calldata intent, + uint256 nonce, + uint24 fee, + bytes calldata signature + ) external returns (FillMessage memory fillMessage); + function processIntentQueue(Intent[] calldata intents) external payable; + function processFillQueue(uint32 amount) external payable; + function processIntentQueueViaRelayer( + uint32 domain, + Intent[] calldata intents, + address relayer, + uint256 ttl, + uint256 nonce, + uint256 bufferBPS, + bytes calldata signature + ) external; + function processFillQueueViaRelayer( + uint32 domain, + uint32 amount, + address relayer, + uint256 ttl, + uint256 nonce, + uint256 bufferBPS, + bytes calldata signature + ) external; + function deposit(address asset, uint256 amount) external; + function withdraw(address asset, uint256 amount) external; + function updateGateway(address newGateway) external; + function updateMessageReceiver(address newMessageReceiver) external; + function authorizeGasReceiver(address receiver, bool authorized) external; + function updateMessageGasLimit(uint256 newGasLimit) external; + function executeIntentCalldata(Intent calldata intent) external; +} \ No newline at end of file diff --git a/src/templates/tests/unit/PP_Template_v1.t.sol b/src/templates/tests/unit/PP_Template_v1.t.sol index a1ddfd6df..3d3d6850e 100644 --- a/src/templates/tests/unit/PP_Template_v1.t.sol +++ b/src/templates/tests/unit/PP_Template_v1.t.sol @@ -1,6 +1,7 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.0; +<<<<<<< HEAD // Internal import { ModuleTest, @@ -11,16 +12,30 @@ import {OZErrors} from "test/utils/errors/OZErrors.sol"; // External import {Clones} from "@oz/proxy/Clones.sol"; - -// Tests and Mocks -import {PP_Template_v1_Exposed} from - "src/templates/tests/unit/PP_Template_v1_Exposed.sol"; -import { - IERC20PaymentClientBase_v1, - ERC20PaymentClientBaseV1Mock, - ERC20Mock -} from "test/utils/mocks/modules/paymentClient/ERC20PaymentClientBaseV1Mock.sol"; - +======= +// // Internal Dependencies +// import { +// ModuleTest, +// IModule_v1, +// IOrchestrator_v1 +// } from "test/modules/ModuleTest.sol"; +// import {OZErrors} from "test/utils/errors/OZErrors.sol"; + +// // External Dependencies +// import {Clones} from "@oz/proxy/Clones.sol"; +>>>>>>> 79edbd43 (add cross-chain module, contracts compile) + +// // Tests and Mocks +// //import cr +// import { +// IERC20PaymentClientBase_v1, +// ERC20PaymentClientBaseV1Mock, +// ERC20Mock +// } from "test/utils/mocks/modules/paymentClient/ERC20PaymentClientBaseV1Mock.sol"; +// //import exposed +// import {PP_CrossChain_v1_Exposed} from "./PP_Template_v1_Exposed.sol"; + +<<<<<<< HEAD // System under Test (SuT) import { IPP_Template_v1, @@ -51,163 +66,204 @@ contract PP_Template_v1_Test is ModuleTest { //-------------------------------------------------------------------------- // Constants uint internal constant _payoutAmountMultiplier = 2; - - //-------------------------------------------------------------------------- - // State - - // System under test (SuT) - PP_Template_v1_Exposed paymentProcessor; - // Mocks - ERC20PaymentClientBaseV1Mock paymentClient; - - //-------------------------------------------------------------------------- - // Setup - function setUp() public { - // This function is used to setup the unit test - // Deploy the SuT - address impl = address(new PP_Template_v1_Exposed()); - paymentProcessor = PP_Template_v1_Exposed(Clones.clone(impl)); - - // Setup the module to test - _setUpOrchestrator(paymentProcessor); - - // General setup for other contracts in the workflow - _authorizer.setIsAuthorized(address(this), true); - - // Initiate the PP with the medata and config data - paymentProcessor.init( - _orchestrator, _METADATA, abi.encode(_payoutAmountMultiplier) - ); - - // Setup other modules needed in the unit tests. - // In this case a payment client is needed to test the PP_Template_v1. - impl = address(new ERC20PaymentClientBaseV1Mock()); - paymentClient = ERC20PaymentClientBaseV1Mock(Clones.clone(impl)); - // Adding the payment client is done through a timelock mechanism - _orchestrator.initiateAddModuleWithTimelock(address(paymentClient)); - vm.warp(block.timestamp + _orchestrator.MODULE_UPDATE_TIMELOCK()); - _orchestrator.executeAddModule(address(paymentClient)); - // Init payment client - paymentClient.init(_orchestrator, _METADATA, bytes("")); - paymentClient.setIsAuthorized(address(paymentProcessor), true); - paymentClient.setToken(_token); - } - - //-------------------------------------------------------------------------- - // Test: Initialization - - // Test if the orchestrator is correctly set - function testInit() public override(ModuleTest) { - assertEq( - address(paymentProcessor.orchestrator()), address(_orchestrator) - ); - } - - // Test the interface support - function testSupportsInterface() public { - assertTrue( - paymentProcessor.supportsInterface( - type(IPaymentProcessor_v1).interfaceId - ) - ); - assertTrue( - paymentProcessor.supportsInterface( - type(IPP_Template_v1).interfaceId - ) - ); - } - - // Test the reinit function - function testReinitFails() public override(ModuleTest) { - vm.expectRevert(OZErrors.Initializable__InvalidInitialization); - paymentProcessor.init( - _orchestrator, _METADATA, abi.encode(_payoutAmountMultiplier) - ); - } - - //-------------------------------------------------------------------------- - // Test: Modifiers - - /* Test validClient modifier in place (extensive testing done through internal modifier functions) - └── Given the modifier is in place - └── When the function processPayment() is called - └── Then it should revert - */ - function testProcessPayments_modifierInPlace() public { - ERC20PaymentClientBaseV1Mock nonRegisteredClient = - new ERC20PaymentClientBaseV1Mock(); - +======= +// // System under test (SuT) +// import { +// IPP_CrossChain_v1, +// PP_CrossChain_v1, +// IPaymentProcessor_v1 +// } from "src/templates/modules/PP_Template_v1.sol"; + +// /** +// * @title Inverter Template Payment Processor +// * +// * @notice Basic template payment processor used to showcase the unit testing setup +// * +// * @dev Not all functions are tested in this template. Placeholders of the functions that are not tested are added +// * into the contract. This test showcases the following: +// * - Inherit from the ModuleTest contract to enable interaction with the Inverter workflow. +// * - Showcases the setup of the workflow, uses in test unit tests. +// * - Pre-defined layout for all setup and functions to be tested. +// * - Shows the use of Gherkin for documenting the testing. VS Code extension used for formatting is recommended. +// * - Shows the use of the modifierInPlace pattern to test the modifier placement. +// * +// * @custom:security-contact security@inverter.network +// * In case of any concerns or findings, please refer to our Security Policy +// * at security.inverter.network or email us directly! +// * +// * @author Inverter Network +// */ +// contract PP_Template_v1_Test is ModuleTest { +// //-------------------------------------------------------------------------- +// // Constants +// uint internal constant _payoutAmountMultiplier = 2; +>>>>>>> 79edbd43 (add cross-chain module, contracts compile) + +// //-------------------------------------------------------------------------- +// // State + +// // Mocks +// ERC20PaymentClientBaseV1Mock paymentClient; + +// // System under test (SuT) +// PP_CrossChain_v1 public paymentProcessor; + +// //-------------------------------------------------------------------------- +// // Setup +// function setUp() public { +// // This function is used to setup the unit test +// // Deploy the SuT +// address impl = address(new PP_CrossChain_v1_Exposed()); +// paymentProcessor = PP_CrossChain_v1_Exposed(Clones.clone(impl)); + +// // Setup the module to test +// _setUpOrchestrator(paymentProcessor); + +// // General setup for other contracts in the workflow +// _authorizer.setIsAuthorized(address(this), true); + +// // Initiate the PP with the medata and config data +// paymentProcessor.init( +// _orchestrator, _METADATA, abi.encode(_payoutAmountMultiplier) +// ); + +// // Setup other modules needed in the unit tests. +// // In this case a payment client is needed to test the PP_Template_v1. +// impl = address(new ERC20PaymentClientBaseV1Mock()); +// paymentClient = ERC20PaymentClientBaseV1Mock(Clones.clone(impl)); +// // Adding the payment client is done through a timelock mechanism +// _orchestrator.initiateAddModuleWithTimelock(address(paymentClient)); +// vm.warp(block.timestamp + _orchestrator.MODULE_UPDATE_TIMELOCK()); +// _orchestrator.executeAddModule(address(paymentClient)); +// // Init payment client +// paymentClient.init(_orchestrator, _METADATA, bytes("")); +// paymentClient.setIsAuthorized(address(paymentProcessor), true); +// paymentClient.setToken(_token); +// } + +// //-------------------------------------------------------------------------- +// // Test: Initialization + +// //Test if the orchestrator is correctly set +// function testInit() public override(ModuleTest) { +// assertEq( +// address(paymentProcessor.orchestrator()), address(_orchestrator) +// ); +// } + +// // // Test the interface support +// // function testSupportsInterface() public { +// // assertTrue( +// // paymentProcessor.supportsInterface( +// // type(IPaymentProcessor_v1).interfaceId +// // ) +// // ); +// // assertTrue( +// // paymentProcessor.supportsInterface( +// // type(IPP_CrossChain_v1).interfaceId +// // ) +// // ); +// // } + +// // Test the reinit function +// function testReinitFails() public override(ModuleTest) { +// vm.expectRevert(OZErrors.Initializable__InvalidInitialization); +// paymentProcessor.init( +// _orchestrator, _METADATA, abi.encode(_payoutAmountMultiplier) +// ); +// } + +// // //-------------------------------------------------------------------------- +// // // Test: Modifiers + +<<<<<<< HEAD vm.expectRevert( IPP_Template_v1.Module__PP_Template__ClientNotValid.selector ); paymentProcessor.processPayments(nonRegisteredClient); } - - //-------------------------------------------------------------------------- - // Test: External (public & external) - - // Test external processPayments() function - - // Test external cancelRunningPayments() function - - // Test external unclaimable() function - - // Test external claimPreviouslyUnclaimable() function - - // Test external validPaymentOrder() function - - //-------------------------------------------------------------------------- - // Test: Internal (tested through exposed_functions) - - /* test internal _setPayoutAmountMultiplier() - ├── Given the newPayoutAmount == 0 - │ └── When the function _setPayoutAmountMultiplier() is called - │ └── Then it should revert - └── Given the newPayoutAmount != 0 - └── When the function _setPayoutAmountMultiplier() is called - └── Then it should emit the event - └── And it should set the state correctly - */ - - function testInternalSetPayoutAmountMultiplier_FailsGivenZero() public { - vm.expectRevert( - IPP_Template_v1.Module__PP_Template_InvalidAmount.selector - ); - paymentProcessor.exposed_setPayoutAmountMultiplier(0); - } - - function testInternalSetPayoutAmountMultiplier_FailsGivenZeroAfter( - uint newPayoutAmountMultiplier_ - ) public { - // Set up assumption - vm.assume(newPayoutAmountMultiplier_ > 0); - - // Check initial state - assertEq( - paymentProcessor.getPayoutAmountMultiplier(), - _payoutAmountMultiplier - ); - - // Test internal function through mock exposed function - vm.expectEmit(true, true, true, true); - emit IPP_Template_v1.NewPayoutAmountMultiplierSet( - _payoutAmountMultiplier, newPayoutAmountMultiplier_ - ); - paymentProcessor.exposed_setPayoutAmountMultiplier( - newPayoutAmountMultiplier_ - ); - - // Test final state - assertEq( - paymentProcessor.getPayoutAmountMultiplier(), - newPayoutAmountMultiplier_ - ); - } - - // Test the internal _validPaymentReceiver() function - - // Test the internal _validClientModifier() function - - //-------------------------------------------------------------------------- - // Helper Functions -} +======= +// // /* Test validClient modifier in place (extensive testing done through internal modifier functions) +// // └── Given the modifier is in place +// // └── When the function processPayment() is called +// // └── Then it should revert +// // */ +// // function testProcessPayments_modifierInPlace() public { +// // // ERC20PaymentClientBaseV1Mock nonRegisteredClient = +// // // new ERC20PaymentClientBaseV1Mock(); +>>>>>>> 79edbd43 (add cross-chain module, contracts compile) + +// // // vm.expectRevert( +// // // IPP_CrossChain_v1.Module__PP_CrossChain__NotValidClient.selector +// // // ); +// // // paymentProcessor.processPayments(nonRegisteredClient); +// // } + +// // //-------------------------------------------------------------------------- +// // // Test: External (public & external) + +// // // Test external processPayments() function + +// // // Test external cancelRunningPayments() function + +// // // Test external unclaimable() function + +// // // Test external claimPreviouslyUnclaimable() function + +// // // Test external validPaymentOrder() function + +// // //-------------------------------------------------------------------------- +// // // Test: Internal (tested through exposed_functions) + +// // /* test internal _setPayoutAmountMultiplier() +// // ├── Given the newPayoutAmount == 0 +// // │ └── When the function _setPayoutAmountMultiplier() is called +// // │ └── Then it should revert +// // └── Given the newPayoutAmount != 0 +// // └── When the function _setPayoutAmountMultiplier() is called +// // └── Then it should emit the event +// // └── And it should set the state correctly +// // */ + +// // function testInternalSetPayoutAmountMultiplier_FailsGivenZero() public { +// // // vm.expectRevert( +// // // IPP_CrossChain_v1.Module__PP_CrossChain__InvalidAmount.selector +// // // ); +// // // paymentProcessor.exposed_setPayoutAmountMultiplier(0); +// // } + +// // function testInternalSetPayoutAmountMultiplier_FailsGivenZeroAfter( +// // uint newPayoutAmountMultiplier_ +// // ) public { +// // // Set up assumption +// // vm.assume(newPayoutAmountMultiplier_ > 0); + +// // // // Check initial state +// // // assertEq( +// // // paymentProcessor.getPayoutAmountMultiplier(), +// // // _payoutAmountMultiplier +// // // ); + +// // // // Test internal function through mock exposed function +// // // vm.expectEmit(true, true, true, true); +// // // emit IPP_CrossChain_v1.NewPayoutAmountMultiplierSet( +// // // _payoutAmountMultiplier, newPayoutAmountMultiplier_ +// // // ); +// // // paymentProcessor.exposed_setPayoutAmountMultiplier( +// // // newPayoutAmountMultiplier_ +// // // ); + +// // // // Test final state +// // // assertEq( +// // // paymentProcessor.getPayoutAmountMultiplier(), +// // // newPayoutAmountMultiplier_ +// // // ); +// // } + +// // // Test the internal _validPaymentReceiver() function + +// // // Test the internal _validClientModifier() function + +// // //-------------------------------------------------------------------------- +// // // Helper Functions +// } diff --git a/src/templates/tests/unit/PP_Template_v1_Exposed.sol b/src/templates/tests/unit/PP_Template_v1_Exposed.sol index fe3b748ad..d61d977f3 100644 --- a/src/templates/tests/unit/PP_Template_v1_Exposed.sol +++ b/src/templates/tests/unit/PP_Template_v1_Exposed.sol @@ -1,4 +1,5 @@ // SPDX-License-Identifier: LGPL-3.0-only +<<<<<<< HEAD pragma solidity ^0.8.0; // Internal @@ -27,3 +28,67 @@ contract PP_Template_v1_Exposed is PP_Template_v1 { _ensureValidClient(client_); } } +======= +pragma solidity 0.8.23; + +// // Internal Dependencies +// import { PP_CrossChain_v1 } from "src/templates/modules/PP_Template_v1.sol"; +// import { IERC20PaymentClientBase_v1 } from "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; +// import { IEverclearSpoke } from "./interfaces/IEverclearSpoke.sol"; + +// interface IWETH { +// function deposit() external payable; +// function approve(address spender, uint256 amount) external returns (bool); +// } + +// contract PP_CrossChain_v1_Exposed is PP_CrossChain_v1 { + +// IEverclearSpoke public everClearSpoke; +// IWETH public weth; + +// /// @notice Implementation of the bridge transfer logic using EverClear +// /// @inheritdoc PP_CrossChain_v1 +// function _executeBridgeTransfer( +// IERC20PaymentClientBase_v1.PaymentOrder memory order, +// bytes memory executionData +// ) internal override returns (bytes memory) { +// (uint256 maxFee, uint256 ttl, address inputAsset, address outputAsset) = +// abi.decode(executionData, (uint256, uint256, address, address)); + +// // Wrap ETH into WETH to send with the xcall +// weth.deposit{value: msg.value}(); + +// // This contract approves transfer to EverClear +// weth.approve(address(everClearSpoke), order.amount); + +// // Create destinations array with the target chain +// uint32[] memory destinations = new uint32[](1); +// destinations[0] = 1; + +// // Call newIntent on the EverClearSpoke contract +// (bytes32 intentId,) = everClearSpoke.newIntent( +// destinations, +// order.recipient, // Changed from msg.sender to order.recipient +// inputAsset, +// outputAsset, +// order.amount, +// uint24(maxFee), +// uint48(ttl), +// "" // empty data field +// ); + +// return abi.encode(intentId); +// } + +// function processPayments(IERC20PaymentClientBase_v1 client_, bytes memory executionData) +// external +// override +// validClient(address(client_)) +// { +// super.processPayments(client_, executionData); +// } + + + +// } +>>>>>>> 79edbd43 (add cross-chain module, contracts compile) From b4b0c1fe109df0b793b2e53da6c5fa7d0d317118 Mon Sep 17 00:00:00 2001 From: Leandro Faria Date: Fri, 1 Nov 2024 11:06:07 -0500 Subject: [PATCH 02/93] refactor tempalte module --- src/templates/tests/unit/PP_Template_v1.t.sol | 57 --------- .../tests/unit/PP_Template_v1_Exposed.sol | 108 ++++++++---------- 2 files changed, 49 insertions(+), 116 deletions(-) diff --git a/src/templates/tests/unit/PP_Template_v1.t.sol b/src/templates/tests/unit/PP_Template_v1.t.sol index 3d3d6850e..c1ab2ea8a 100644 --- a/src/templates/tests/unit/PP_Template_v1.t.sol +++ b/src/templates/tests/unit/PP_Template_v1.t.sol @@ -1,7 +1,6 @@ // SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.0; -<<<<<<< HEAD // Internal import { ModuleTest, @@ -12,18 +11,6 @@ import {OZErrors} from "test/utils/errors/OZErrors.sol"; // External import {Clones} from "@oz/proxy/Clones.sol"; -======= -// // Internal Dependencies -// import { -// ModuleTest, -// IModule_v1, -// IOrchestrator_v1 -// } from "test/modules/ModuleTest.sol"; -// import {OZErrors} from "test/utils/errors/OZErrors.sol"; - -// // External Dependencies -// import {Clones} from "@oz/proxy/Clones.sol"; ->>>>>>> 79edbd43 (add cross-chain module, contracts compile) // // Tests and Mocks // //import cr @@ -35,7 +22,6 @@ import {Clones} from "@oz/proxy/Clones.sol"; // //import exposed // import {PP_CrossChain_v1_Exposed} from "./PP_Template_v1_Exposed.sol"; -<<<<<<< HEAD // System under Test (SuT) import { IPP_Template_v1, @@ -66,38 +52,6 @@ contract PP_Template_v1_Test is ModuleTest { //-------------------------------------------------------------------------- // Constants uint internal constant _payoutAmountMultiplier = 2; -======= -// // System under test (SuT) -// import { -// IPP_CrossChain_v1, -// PP_CrossChain_v1, -// IPaymentProcessor_v1 -// } from "src/templates/modules/PP_Template_v1.sol"; - -// /** -// * @title Inverter Template Payment Processor -// * -// * @notice Basic template payment processor used to showcase the unit testing setup -// * -// * @dev Not all functions are tested in this template. Placeholders of the functions that are not tested are added -// * into the contract. This test showcases the following: -// * - Inherit from the ModuleTest contract to enable interaction with the Inverter workflow. -// * - Showcases the setup of the workflow, uses in test unit tests. -// * - Pre-defined layout for all setup and functions to be tested. -// * - Shows the use of Gherkin for documenting the testing. VS Code extension used for formatting is recommended. -// * - Shows the use of the modifierInPlace pattern to test the modifier placement. -// * -// * @custom:security-contact security@inverter.network -// * In case of any concerns or findings, please refer to our Security Policy -// * at security.inverter.network or email us directly! -// * -// * @author Inverter Network -// */ -// contract PP_Template_v1_Test is ModuleTest { -// //-------------------------------------------------------------------------- -// // Constants -// uint internal constant _payoutAmountMultiplier = 2; ->>>>>>> 79edbd43 (add cross-chain module, contracts compile) // //-------------------------------------------------------------------------- // // State @@ -176,22 +130,11 @@ contract PP_Template_v1_Test is ModuleTest { // // //-------------------------------------------------------------------------- // // // Test: Modifiers -<<<<<<< HEAD vm.expectRevert( IPP_Template_v1.Module__PP_Template__ClientNotValid.selector ); paymentProcessor.processPayments(nonRegisteredClient); } -======= -// // /* Test validClient modifier in place (extensive testing done through internal modifier functions) -// // └── Given the modifier is in place -// // └── When the function processPayment() is called -// // └── Then it should revert -// // */ -// // function testProcessPayments_modifierInPlace() public { -// // // ERC20PaymentClientBaseV1Mock nonRegisteredClient = -// // // new ERC20PaymentClientBaseV1Mock(); ->>>>>>> 79edbd43 (add cross-chain module, contracts compile) // // // vm.expectRevert( // // // IPP_CrossChain_v1.Module__PP_CrossChain__NotValidClient.selector diff --git a/src/templates/tests/unit/PP_Template_v1_Exposed.sol b/src/templates/tests/unit/PP_Template_v1_Exposed.sol index d61d977f3..8eceb1c1a 100644 --- a/src/templates/tests/unit/PP_Template_v1_Exposed.sol +++ b/src/templates/tests/unit/PP_Template_v1_Exposed.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: LGPL-3.0-only -<<<<<<< HEAD + pragma solidity ^0.8.0; // Internal @@ -31,64 +31,54 @@ contract PP_Template_v1_Exposed is PP_Template_v1 { ======= pragma solidity 0.8.23; -// // Internal Dependencies -// import { PP_CrossChain_v1 } from "src/templates/modules/PP_Template_v1.sol"; -// import { IERC20PaymentClientBase_v1 } from "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; -// import { IEverclearSpoke } from "./interfaces/IEverclearSpoke.sol"; - -// interface IWETH { -// function deposit() external payable; -// function approve(address spender, uint256 amount) external returns (bool); -// } - -// contract PP_CrossChain_v1_Exposed is PP_CrossChain_v1 { - -// IEverclearSpoke public everClearSpoke; -// IWETH public weth; - -// /// @notice Implementation of the bridge transfer logic using EverClear -// /// @inheritdoc PP_CrossChain_v1 -// function _executeBridgeTransfer( -// IERC20PaymentClientBase_v1.PaymentOrder memory order, -// bytes memory executionData -// ) internal override returns (bytes memory) { -// (uint256 maxFee, uint256 ttl, address inputAsset, address outputAsset) = -// abi.decode(executionData, (uint256, uint256, address, address)); - -// // Wrap ETH into WETH to send with the xcall -// weth.deposit{value: msg.value}(); - -// // This contract approves transfer to EverClear -// weth.approve(address(everClearSpoke), order.amount); - -// // Create destinations array with the target chain -// uint32[] memory destinations = new uint32[](1); -// destinations[0] = 1; - -// // Call newIntent on the EverClearSpoke contract -// (bytes32 intentId,) = everClearSpoke.newIntent( -// destinations, -// order.recipient, // Changed from msg.sender to order.recipient -// inputAsset, -// outputAsset, -// order.amount, -// uint24(maxFee), -// uint48(ttl), -// "" // empty data field -// ); - -// return abi.encode(intentId); -// } - -// function processPayments(IERC20PaymentClientBase_v1 client_, bytes memory executionData) -// external -// override -// validClient(address(client_)) -// { -// super.processPayments(client_, executionData); -// } +// Internal Dependencies +import { PP_CrossChain_v1 } from "src/templates/modules/PP_Template_v1.sol"; +import { IERC20PaymentClientBase_v1 } from "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; +import { IEverclearSpoke } from "./interfaces/IEverclearSpoke.sol"; + +interface IWETH { + function deposit() external payable; + function approve(address spender, uint256 amount) external returns (bool); +} +contract PP_CrossChain_v1_Exposed is PP_CrossChain_v1 { + + IEverclearSpoke public everClearSpoke; + IWETH public weth; + + /// @notice Implementation of the bridge transfer logic using EverClear + /// @inheritdoc PP_CrossChain_v1 + function exposed_executeBridgeTransfer( + IERC20PaymentClientBase_v1.PaymentOrder memory order, + bytes memory executionData + ) external payable override returns (bytes memory) { + (uint256 maxFee, uint256 ttl, address inputAsset, address outputAsset) = + abi.decode(executionData, (uint256, uint256, address, address)); + + // Wrap ETH into WETH to send with the xcall + weth.deposit{value: msg.value}(); + + // This contract approves transfer to EverClear + weth.approve(address(everClearSpoke), order.amount); + + // Create destinations array with the target chain + uint32[] memory destinations = new uint32[](1); + destinations[0] = 1; + + // Call newIntent on the EverClearSpoke contract + (bytes32 intentId,) = everClearSpoke.newIntent( + destinations, + order.recipient, // Changed from msg.sender to order.recipient + inputAsset, + outputAsset, + order.amount, + uint24(maxFee), + uint48(ttl), + "" // empty data field + ); + + return abi.encode(intentId); + } -// } ->>>>>>> 79edbd43 (add cross-chain module, contracts compile) +} From 7041fa45b7330d4d36654328a4a151c2bd13d2ff Mon Sep 17 00:00:00 2001 From: Zuhaib Mohammed Date: Mon, 4 Nov 2024 08:39:19 -0800 Subject: [PATCH 03/93] fix compile issues --- src/templates/tests/unit/PP_Template_v1.t.sol | 343 ++++++++++-------- .../tests/unit/PP_Template_v1_Exposed.sol | 54 --- 2 files changed, 183 insertions(+), 214 deletions(-) diff --git a/src/templates/tests/unit/PP_Template_v1.t.sol b/src/templates/tests/unit/PP_Template_v1.t.sol index c1ab2ea8a..de5a2a5b3 100644 --- a/src/templates/tests/unit/PP_Template_v1.t.sol +++ b/src/templates/tests/unit/PP_Template_v1.t.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: LGPL-3.0-only +//SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.0; // Internal @@ -12,15 +12,15 @@ import {OZErrors} from "test/utils/errors/OZErrors.sol"; // External import {Clones} from "@oz/proxy/Clones.sol"; -// // Tests and Mocks -// //import cr -// import { -// IERC20PaymentClientBase_v1, -// ERC20PaymentClientBaseV1Mock, -// ERC20Mock -// } from "test/utils/mocks/modules/paymentClient/ERC20PaymentClientBaseV1Mock.sol"; -// //import exposed -// import {PP_CrossChain_v1_Exposed} from "./PP_Template_v1_Exposed.sol"; +//Tests and Mocks +// import cr +import { + IERC20PaymentClientBase_v1, + ERC20PaymentClientBaseV1Mock, + ERC20Mock +} from "test/utils/mocks/modules/paymentClient/ERC20PaymentClientBaseV1Mock.sol"; +//import exposed +import {PP_CrossChain_v1_Exposed} from "./PP_Template_v1_Exposed.sol"; // System under Test (SuT) import { @@ -53,82 +53,85 @@ contract PP_Template_v1_Test is ModuleTest { // Constants uint internal constant _payoutAmountMultiplier = 2; -// //-------------------------------------------------------------------------- -// // State - -// // Mocks -// ERC20PaymentClientBaseV1Mock paymentClient; - -// // System under test (SuT) -// PP_CrossChain_v1 public paymentProcessor; - -// //-------------------------------------------------------------------------- -// // Setup -// function setUp() public { -// // This function is used to setup the unit test -// // Deploy the SuT -// address impl = address(new PP_CrossChain_v1_Exposed()); -// paymentProcessor = PP_CrossChain_v1_Exposed(Clones.clone(impl)); - -// // Setup the module to test -// _setUpOrchestrator(paymentProcessor); - -// // General setup for other contracts in the workflow -// _authorizer.setIsAuthorized(address(this), true); - -// // Initiate the PP with the medata and config data -// paymentProcessor.init( -// _orchestrator, _METADATA, abi.encode(_payoutAmountMultiplier) -// ); - -// // Setup other modules needed in the unit tests. -// // In this case a payment client is needed to test the PP_Template_v1. -// impl = address(new ERC20PaymentClientBaseV1Mock()); -// paymentClient = ERC20PaymentClientBaseV1Mock(Clones.clone(impl)); -// // Adding the payment client is done through a timelock mechanism -// _orchestrator.initiateAddModuleWithTimelock(address(paymentClient)); -// vm.warp(block.timestamp + _orchestrator.MODULE_UPDATE_TIMELOCK()); -// _orchestrator.executeAddModule(address(paymentClient)); -// // Init payment client -// paymentClient.init(_orchestrator, _METADATA, bytes("")); -// paymentClient.setIsAuthorized(address(paymentProcessor), true); -// paymentClient.setToken(_token); -// } - -// //-------------------------------------------------------------------------- -// // Test: Initialization - -// //Test if the orchestrator is correctly set -// function testInit() public override(ModuleTest) { -// assertEq( -// address(paymentProcessor.orchestrator()), address(_orchestrator) -// ); -// } - -// // // Test the interface support -// // function testSupportsInterface() public { -// // assertTrue( -// // paymentProcessor.supportsInterface( -// // type(IPaymentProcessor_v1).interfaceId -// // ) -// // ); -// // assertTrue( -// // paymentProcessor.supportsInterface( -// // type(IPP_CrossChain_v1).interfaceId -// // ) -// // ); -// // } - -// // Test the reinit function -// function testReinitFails() public override(ModuleTest) { -// vm.expectRevert(OZErrors.Initializable__InvalidInitialization); -// paymentProcessor.init( -// _orchestrator, _METADATA, abi.encode(_payoutAmountMultiplier) -// ); -// } - -// // //-------------------------------------------------------------------------- -// // // Test: Modifiers +contract PP_Template_v1_Test is ModuleTest { + //-------------------------------------------------------------------------- + //Constants + uint internal constant _payoutAmountMultiplier = 2; + + //-------------------------------------------------------------------------- + //State + + //Mocks + ERC20PaymentClientBaseV1Mock paymentClient; + + //System under test (SuT) + //PP_CrossChain_v1 public paymentProcessor; + PP_CrossChain_v1_Exposed public paymentProcessor; + + //-------------------------------------------------------------------------- + //Setup + function setUp() public { + //This function is used to setup the unit test + //Deploy the SuT + address impl = address(new PP_CrossChain_v1_Exposed()); + paymentProcessor = PP_CrossChain_v1_Exposed(Clones.clone(impl)); + + //Setup the module to test + _setUpOrchestrator(paymentProcessor); + + //General setup for other contracts in the workflow + _authorizer.setIsAuthorized(address(this), true); + + //Initiate the PP with the medata and config data + paymentProcessor.init( + _orchestrator, _METADATA, abi.encode(_payoutAmountMultiplier) + ); + + //Setup other modules needed in the unit tests. + //In this case a payment client is needed to test the PP_Template_v1. + impl = address(new ERC20PaymentClientBaseV1Mock()); + paymentClient = ERC20PaymentClientBaseV1Mock(Clones.clone(impl)); + //Adding the payment client is done through a timelock mechanism + _orchestrator.initiateAddModuleWithTimelock(address(paymentClient)); + vm.warp(block.timestamp + _orchestrator.MODULE_UPDATE_TIMELOCK()); + _orchestrator.executeAddModule(address(paymentClient)); + //Init payment client + paymentClient.init(_orchestrator, _METADATA, bytes("")); + paymentClient.setIsAuthorized(address(paymentProcessor), true); + paymentClient.setToken(_token); + } + + //-------------------------------------------------------------------------- + //Test: Initialization + + //Test if the orchestrator is correctly set + function testInit() public override(ModuleTest) { + assertEq( + address(paymentProcessor.orchestrator()), address(_orchestrator) + ); + } + + //Test the interface support + function testSupportsInterface() public { + assertTrue( + paymentProcessor.supportsInterface( + type(IPaymentProcessor_v1).interfaceId + ) + ); + assertTrue( + paymentProcessor.supportsInterface( + type(IPP_CrossChain_v1).interfaceId + ) + ); + } + + //Test the reinit function + function testReinitFails() public override(ModuleTest) { + vm.expectRevert(OZErrors.Initializable__InvalidInitialization); + paymentProcessor.init( + _orchestrator, _METADATA, abi.encode(_payoutAmountMultiplier) + ); + } vm.expectRevert( IPP_Template_v1.Module__PP_Template__ClientNotValid.selector @@ -136,77 +139,97 @@ contract PP_Template_v1_Test is ModuleTest { paymentProcessor.processPayments(nonRegisteredClient); } -// // // vm.expectRevert( -// // // IPP_CrossChain_v1.Module__PP_CrossChain__NotValidClient.selector -// // // ); -// // // paymentProcessor.processPayments(nonRegisteredClient); -// // } - -// // //-------------------------------------------------------------------------- -// // // Test: External (public & external) - -// // // Test external processPayments() function - -// // // Test external cancelRunningPayments() function - -// // // Test external unclaimable() function - -// // // Test external claimPreviouslyUnclaimable() function - -// // // Test external validPaymentOrder() function - -// // //-------------------------------------------------------------------------- -// // // Test: Internal (tested through exposed_functions) - -// // /* test internal _setPayoutAmountMultiplier() -// // ├── Given the newPayoutAmount == 0 -// // │ └── When the function _setPayoutAmountMultiplier() is called -// // │ └── Then it should revert -// // └── Given the newPayoutAmount != 0 -// // └── When the function _setPayoutAmountMultiplier() is called -// // └── Then it should emit the event -// // └── And it should set the state correctly -// // */ - -// // function testInternalSetPayoutAmountMultiplier_FailsGivenZero() public { -// // // vm.expectRevert( -// // // IPP_CrossChain_v1.Module__PP_CrossChain__InvalidAmount.selector -// // // ); -// // // paymentProcessor.exposed_setPayoutAmountMultiplier(0); -// // } - -// // function testInternalSetPayoutAmountMultiplier_FailsGivenZeroAfter( -// // uint newPayoutAmountMultiplier_ -// // ) public { -// // // Set up assumption -// // vm.assume(newPayoutAmountMultiplier_ > 0); - -// // // // Check initial state -// // // assertEq( -// // // paymentProcessor.getPayoutAmountMultiplier(), -// // // _payoutAmountMultiplier -// // // ); - -// // // // Test internal function through mock exposed function -// // // vm.expectEmit(true, true, true, true); -// // // emit IPP_CrossChain_v1.NewPayoutAmountMultiplierSet( -// // // _payoutAmountMultiplier, newPayoutAmountMultiplier_ -// // // ); -// // // paymentProcessor.exposed_setPayoutAmountMultiplier( -// // // newPayoutAmountMultiplier_ -// // // ); - -// // // // Test final state -// // // assertEq( -// // // paymentProcessor.getPayoutAmountMultiplier(), -// // // newPayoutAmountMultiplier_ -// // // ); -// // } - -// // // Test the internal _validPaymentReceiver() function - -// // // Test the internal _validClientModifier() function - -// // //-------------------------------------------------------------------------- -// // // Helper Functions -// } + /* Test validClient modifier in place (extensive testing done through internal modifier functions) + └── Given the modifier is in place + └── When the function processPayment() is called + └── Then it should revert + */ + function testProcessPayments_modifierInPlace() public { + ERC20PaymentClientBaseV1Mock nonRegisteredClient = + new ERC20PaymentClientBaseV1Mock(); + + vm.expectRevert( + IPP_CrossChain_v1.Module__PP_Template__NotValidClient.selector + ); + paymentProcessor.processPayments(nonRegisteredClient); + } + + //-------------------------------------------------------------------------- + //Test: External (public & external) + + function testExposed_executeBridgeTransfer() public { + bytes memory bridgeData = paymentProcessor.exposed_executeBridgeTransfer( + IERC20PaymentClientBase_v1.PaymentOrder( + address(paymentClient), address(0xabcd), 25 ether, 0, 0, 0 + ), + "" + ); + console2.logBytes(bridgeData); + assertEq(bridgeData.length, 0); + } + + //Test external processPayments() function + + //Test external cancelRunningPayments() function + + //Test external unclaimable() function + + //Test external claimPreviouslyUnclaimable() function + + //Test external validPaymentOrder() function + + //-------------------------------------------------------------------------- + //Test: Internal (tested through exposed_functions) + + /* test internal _setPayoutAmountMultiplier() + ├── Given the newPayoutAmount == 0 + │ └── When the function _setPayoutAmountMultiplier() is called + │ └── Then it should revert + └── Given the newPayoutAmount != 0 + └── When the function _setPayoutAmountMultiplier() is called + └── Then it should emit the event + └── And it should set the state correctly + */ + + //function testInternalSetPayoutAmountMultiplier_FailsGivenZero() public { + // vm.expectRevert( + // IPP_CrossChain_v1.Module__PP_CrossChain__InvalidAmount.selector + // ); + // paymentProcessor.exposed_setPayoutAmountMultiplier(0); + //} + + // function testInternalSetPayoutAmountMultiplier_FailsGivenZeroAfter( + // uint newPayoutAmountMultiplier_ + // ) public { + // //Set up assumption + // vm.assume(newPayoutAmountMultiplier_ > 0); + + // // Check initial state + // assertEq( + // paymentProcessor.getPayoutAmountMultiplier(), + // _payoutAmountMultiplier + // ); + + // // Test internal function through mock exposed function + // vm.expectEmit(true, true, true, true); + // emit IPP_CrossChain_v1.NewPayoutAmountMultiplierSet( + // _payoutAmountMultiplier, newPayoutAmountMultiplier_ + // ); + // paymentProcessor.exposed_setPayoutAmountMultiplier( + // newPayoutAmountMultiplier_ + // ); + + // // Test final state + // assertEq( + // paymentProcessor.getPayoutAmountMultiplier(), + // newPayoutAmountMultiplier_ + // ); + // } + + //Test the internal _validPaymentReceiver() function + + //Test the internal _validClientModifier() function + + //-------------------------------------------------------------------------- + //Helper Functions +} diff --git a/src/templates/tests/unit/PP_Template_v1_Exposed.sol b/src/templates/tests/unit/PP_Template_v1_Exposed.sol index 8eceb1c1a..74afd80b5 100644 --- a/src/templates/tests/unit/PP_Template_v1_Exposed.sol +++ b/src/templates/tests/unit/PP_Template_v1_Exposed.sol @@ -28,57 +28,3 @@ contract PP_Template_v1_Exposed is PP_Template_v1 { _ensureValidClient(client_); } } -======= -pragma solidity 0.8.23; - -// Internal Dependencies -import { PP_CrossChain_v1 } from "src/templates/modules/PP_Template_v1.sol"; -import { IERC20PaymentClientBase_v1 } from "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; -import { IEverclearSpoke } from "./interfaces/IEverclearSpoke.sol"; - -interface IWETH { - function deposit() external payable; - function approve(address spender, uint256 amount) external returns (bool); -} - -contract PP_CrossChain_v1_Exposed is PP_CrossChain_v1 { - - IEverclearSpoke public everClearSpoke; - IWETH public weth; - - /// @notice Implementation of the bridge transfer logic using EverClear - /// @inheritdoc PP_CrossChain_v1 - function exposed_executeBridgeTransfer( - IERC20PaymentClientBase_v1.PaymentOrder memory order, - bytes memory executionData - ) external payable override returns (bytes memory) { - (uint256 maxFee, uint256 ttl, address inputAsset, address outputAsset) = - abi.decode(executionData, (uint256, uint256, address, address)); - - // Wrap ETH into WETH to send with the xcall - weth.deposit{value: msg.value}(); - - // This contract approves transfer to EverClear - weth.approve(address(everClearSpoke), order.amount); - - // Create destinations array with the target chain - uint32[] memory destinations = new uint32[](1); - destinations[0] = 1; - - // Call newIntent on the EverClearSpoke contract - (bytes32 intentId,) = everClearSpoke.newIntent( - destinations, - order.recipient, // Changed from msg.sender to order.recipient - inputAsset, - outputAsset, - order.amount, - uint24(maxFee), - uint48(ttl), - "" // empty data field - ); - - return abi.encode(intentId); - } - - -} From 6cd983cbc2632858b6de5c2a4919edd7bc1820f2 Mon Sep 17 00:00:00 2001 From: Leandro Faria Date: Tue, 5 Nov 2024 01:31:29 -0500 Subject: [PATCH 04/93] add todo's for implementation contract --- .../bridging/PP_BR_Connext_v1.sol | 217 ++++++++++++++++++ src/templates/tests/unit/PP_Template_v1.t.sol | 5 +- 2 files changed, 218 insertions(+), 4 deletions(-) create mode 100644 src/modules/paymentProcessor/bridging/PP_BR_Connext_v1.sol diff --git a/src/modules/paymentProcessor/bridging/PP_BR_Connext_v1.sol b/src/modules/paymentProcessor/bridging/PP_BR_Connext_v1.sol new file mode 100644 index 000000000..d470a6a35 --- /dev/null +++ b/src/modules/paymentProcessor/bridging/PP_BR_Connext_v1.sol @@ -0,0 +1,217 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity 0.8.23; + +// Internal Interfaces +import {IOrchestrator_v1} from + "src/orchestrator/interfaces/IOrchestrator_v1.sol"; +import { + IPaymentProcessor_v1, I + IERC20PaymentClientBase_v1 +} from "src/modules/paymentProcessor/IPaymentProcessor_v1.sol"; + +// Internal Dependencies +import {ERC165Upgradeable, Module_v1} from "src/modules/base/Module_v1.sol"; + +// External Interfaces +import {IERC20} from "@oz/token/ERC20/IERC20.sol"; + +// External Libraries +import {SafeERC20} from "@oz/token/ERC20/utils/SafeERC20.sol"; + +/** + * @title Inverter Simple Payment Processor + * + * @notice Manages ERC20 payment processing for modules within the Inverter Network + * that are compliant with the {IERC20PaymentClientBase_v1} interface. + * + * @dev Inherits {Module_v1} and implements {IPaymentProcessor_v1} to handle payment + * orders from registered modules, ensuring only eligible modules can initiate + * payments. Utilizes {SafeERC20} for secure token transfers. + * + * @custom:security-contact security@inverter.network + * In case of any concerns or findings, please refer to our Security Policy + * at security.inverter.network or email us directly! + * + * @author Inverter Network + */ + + //TODO: Here's my guess of how we should do this + // - Inherit from the PP_Template_v1 base and maybe event he interface too? + // - Implement the internal execute bridge function in here + // - Add the logic for Everclear (or connext if that's easier since we're mocking it for now) + // - Make sure it compiles and the if you have time write a test file that copiles + // - We then need to create mocks that will mock a call to the bridge + // - We then need to right our tests that will test the bridge logic +contract PP_Simple_v1 is Module_v1, IPaymentProcessor_v1 { + /// @inheritdoc ERC165Upgradeable + function supportsInterface(bytes4 interfaceId) + public + view + virtual + override(Module_v1) + returns (bool) + { + return interfaceId == type(IPaymentProcessor_v1).interfaceId + || super.supportsInterface(interfaceId); + } + + using SafeERC20 for IERC20; + + //-------------------------------------------------------------------------- + // Modifiers + + /// @dev Checks that the caller is an active module. + modifier onlyModule() { + if (!orchestrator().isModule(_msgSender())) { + revert Module__PaymentProcessor__OnlyCallableByModule(); + } + _; + } + + /// @dev Checks that the client is calling for itself. + modifier validClient(IERC20PaymentClientBase_v1 client) { + if (_msgSender() != address(client)) { + revert Module__PaymentProcessor__CannotCallOnOtherClientsOrders(); + } + _; + } + + //-------------------------------------------------------------------------- + // Storage + + /// @dev Tracks all payments that could not be made to the paymentReceiver due to any reason. + /// @dev paymentClient => token address => paymentReceiver => unclaimable Amount. + mapping(address => mapping(address => mapping(address => uint))) internal + unclaimableAmountsForRecipient; + + /// @dev Gap for possible future upgrades. + uint[50] private __gap; + + //-------------------------------------------------------------------------- + // Initializer + + /// @inheritdoc Module_v1 + function init( + IOrchestrator_v1 orchestrator_, + Metadata memory metadata, + bytes memory /*configData*/ + ) external override(Module_v1) initializer { + __Module_init(orchestrator_, metadata); + } + + //-------------------------------------------------------------------------- + // IPaymentProcessor_v1 Functions + + /// @inheritdoc IPaymentProcessor_v1 + function processPayments(IERC20PaymentClientBase_v1 client) + external + onlyModule + validClient(client) + {}; + + function + /// @inheritdoc IPaymentProcessor_v1 + function cancelRunningPayments(IERC20PaymentClientBase_v1 client) + external + view + onlyModule + validClient(client) + { + // Since we pay out on processing, this function does nothing + return; + } + + /// @inheritdoc IPaymentProcessor_v1 + function unclaimable(address client, address token, address paymentReceiver) + public + view + returns (uint amount) + { + return unclaimableAmountsForRecipient[client][token][paymentReceiver]; + } + + /// @inheritdoc IPaymentProcessor_v1 + function claimPreviouslyUnclaimable( + address client, + address token, + address receiver + ) external { + if (unclaimable(client, token, _msgSender()) == 0) { + revert Module__PaymentProcessor__NothingToClaim( + client, _msgSender() + ); + } + + _claimPreviouslyUnclaimable(client, token, receiver); + } + + /// @inheritdoc IPaymentProcessor_v1 + function validPaymentOrder( + IERC20PaymentClientBase_v1.PaymentOrder memory order + ) external returns (bool) { + return _validPaymentReceiver(order.recipient) + && _validTotal(order.amount) && _validPaymentToken(order.paymentToken); + } + + //-------------------------------------------------------------------------- + // Internal Functions + + /// @notice used to claim the unclaimable amount of a particular `paymentReceiver` for a given payment client. + /// @param client address of the payment client. + /// @param token address of the payment token. + /// @param paymentReceiver address of the paymentReceiver for which the unclaimable amount will be claimed. + function _claimPreviouslyUnclaimable( + address client, + address token, + address paymentReceiver + ) internal { + // get amount + + address sender = _msgSender(); + // copy value over + uint amount = unclaimableAmountsForRecipient[client][token][sender]; + // Delete the field + delete unclaimableAmountsForRecipient[client][token][sender]; + + // Make sure to let paymentClient know that amount doesnt have to be stored anymore + IERC20PaymentClientBase_v1(client).amountPaid(token, amount); + + // Call has to succeed otherwise no state change + IERC20(token).safeTransferFrom(client, paymentReceiver, amount); + + emit TokensReleased(paymentReceiver, address(token), amount); + } + + /// @notice Validate address input. + /// @param addr Address to validate. + /// @return True if address is valid. + function _validPaymentReceiver(address addr) internal view returns (bool) { + return !( + addr == address(0) || addr == _msgSender() || addr == address(this) + || addr == address(orchestrator()) + || addr == address(orchestrator().fundingManager().token()) + ); + } + + /// @notice Validate uint total amount input. + /// @param _total uint to validate. + /// @return True if uint is valid. + function _validTotal(uint _total) internal pure returns (bool) { + return !(_total == 0); + } + + /// @notice Validate payment token input. + /// @param _token Address of the token to validate. + /// @return True if address is valid. + function _validPaymentToken(address _token) internal returns (bool) { + // Only a basic sanity check that the address supports the balanceOf() function. The corresponding + // module should ensure it's sending an ERC20. + + (bool success, bytes memory data) = _token.call( + abi.encodeWithSelector( + IERC20(_token).balanceOf.selector, address(this) + ) + ); + return (success && data.length != 0 && _token.code.length != 0); + } +} diff --git a/src/templates/tests/unit/PP_Template_v1.t.sol b/src/templates/tests/unit/PP_Template_v1.t.sol index de5a2a5b3..f4c466f55 100644 --- a/src/templates/tests/unit/PP_Template_v1.t.sol +++ b/src/templates/tests/unit/PP_Template_v1.t.sol @@ -48,10 +48,7 @@ import { * * @author Inverter Network */ -contract PP_Template_v1_Test is ModuleTest { - //-------------------------------------------------------------------------- - // Constants - uint internal constant _payoutAmountMultiplier = 2; + contract PP_Template_v1_Test is ModuleTest { //-------------------------------------------------------------------------- From 07e9b813d9f63cbd11a4285a9664d462168c0a09 Mon Sep 17 00:00:00 2001 From: Leandro Faria Date: Tue, 5 Nov 2024 01:37:06 -0500 Subject: [PATCH 05/93] added notes handoff add more notes --- .../bridging/PP_BR_Connext_v1.sol | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/modules/paymentProcessor/bridging/PP_BR_Connext_v1.sol b/src/modules/paymentProcessor/bridging/PP_BR_Connext_v1.sol index d470a6a35..9989de603 100644 --- a/src/modules/paymentProcessor/bridging/PP_BR_Connext_v1.sol +++ b/src/modules/paymentProcessor/bridging/PP_BR_Connext_v1.sol @@ -35,13 +35,18 @@ import {SafeERC20} from "@oz/token/ERC20/utils/SafeERC20.sol"; * @author Inverter Network */ - //TODO: Here's my guess of how we should do this - // - Inherit from the PP_Template_v1 base and maybe event he interface too? - // - Implement the internal execute bridge function in here - // - Add the logic for Everclear (or connext if that's easier since we're mocking it for now) + //TODO: Here's my guess of how we should do next + // - Inherit from the PP_Template_v1 base and maybe even the interface too? + // - Implement the internal execute bridge function in here, aka wrap it with an external function and leave space for logic + // - Add the logic from Everclear contract to the executeBridge fucntion (or connext if that's easier since we're mocking it for now) // - Make sure it compiles and the if you have time write a test file that copiles + // - Test file can be in this same folder with the same nameing convention as PP_Template_v1_.t.sol, except for the name I choose here + // - Feel free to copy pp_template_v1.t.sol setup and helper fucntions to get started + // - Make sure this compiles // - We then need to create mocks that will mock a call to the bridge - // - We then need to right our tests that will test the bridge logic + // - This is straightforward, just check to see what event the connext bridge emits after deposit + // - Create a function with the same selector in the mock, when the proper params are passed it should emit the event + // - We then need to write some basic testcases that will test the bridge logic, this should be mocked contract PP_Simple_v1 is Module_v1, IPaymentProcessor_v1 { /// @inheritdoc ERC165Upgradeable function supportsInterface(bytes4 interfaceId) From a12749e30d364473f8eb00424dbb61542ac8fef1 Mon Sep 17 00:00:00 2001 From: Zuhaib Mohammed Date: Tue, 5 Nov 2024 02:57:42 -0800 Subject: [PATCH 06/93] fix compile issues before everclear impl --- .../bridging/PP_BR_Connext_v1.sol | 100 ++++++++++-------- src/templates/modules/PP_Template_v1.sol | 3 +- 2 files changed, 58 insertions(+), 45 deletions(-) diff --git a/src/modules/paymentProcessor/bridging/PP_BR_Connext_v1.sol b/src/modules/paymentProcessor/bridging/PP_BR_Connext_v1.sol index 9989de603..63cee07d2 100644 --- a/src/modules/paymentProcessor/bridging/PP_BR_Connext_v1.sol +++ b/src/modules/paymentProcessor/bridging/PP_BR_Connext_v1.sol @@ -5,9 +5,10 @@ pragma solidity 0.8.23; import {IOrchestrator_v1} from "src/orchestrator/interfaces/IOrchestrator_v1.sol"; import { - IPaymentProcessor_v1, I + IPaymentProcessor_v1, IERC20PaymentClientBase_v1 } from "src/modules/paymentProcessor/IPaymentProcessor_v1.sol"; +import {PP_CrossChain_v1} from "src/templates/modules/PP_Template_v1.sol"; // Internal Dependencies import {ERC165Upgradeable, Module_v1} from "src/modules/base/Module_v1.sol"; @@ -35,25 +36,29 @@ import {SafeERC20} from "@oz/token/ERC20/utils/SafeERC20.sol"; * @author Inverter Network */ - //TODO: Here's my guess of how we should do next - // - Inherit from the PP_Template_v1 base and maybe even the interface too? - // - Implement the internal execute bridge function in here, aka wrap it with an external function and leave space for logic - // - Add the logic from Everclear contract to the executeBridge fucntion (or connext if that's easier since we're mocking it for now) - // - Make sure it compiles and the if you have time write a test file that copiles - // - Test file can be in this same folder with the same nameing convention as PP_Template_v1_.t.sol, except for the name I choose here - // - Feel free to copy pp_template_v1.t.sol setup and helper fucntions to get started - // - Make sure this compiles - // - We then need to create mocks that will mock a call to the bridge - // - This is straightforward, just check to see what event the connext bridge emits after deposit - // - Create a function with the same selector in the mock, when the proper params are passed it should emit the event - // - We then need to write some basic testcases that will test the bridge logic, this should be mocked -contract PP_Simple_v1 is Module_v1, IPaymentProcessor_v1 { +//TODO: Here's my guess of how we should do next +// - Inherit from the PP_Template_v1 base and maybe even the interface too? +// - Implement the internal execute bridge function in here, aka wrap it with an external function and leave space for logic +// - Add the logic from Everclear contract to the executeBridge fucntion (or connext if that's easier since we're mocking it for now) +// - Make sure it compiles and the if you have time write a test file that copiles +// - Test file can be in this same folder with the same nameing convention as PP_Template_v1_.t.sol, except for the name I choose here +// - Feel free to copy pp_template_v1.t.sol setup and helper fucntions to get started +// - Make sure this compiles +// - We then need to create mocks that will mock a call to the bridge +// - This is straightforward, just check to see what event the connext bridge emits after deposit +// - Create a function with the same selector in the mock, when the proper params are passed it should emit the event +// - We then need to write some basic testcases that will test the bridge logic, this should be mocked +contract PP_BR_Connext_v1 is + PP_CrossChain_v1 //@note -> PP_CrossChain_v1 already inherits from Module_v1 and IPaymentProcessor_v1 +{ + //Module_v1, + //IPaymentProcessor_v1 /// @inheritdoc ERC165Upgradeable function supportsInterface(bytes4 interfaceId) public view virtual - override(Module_v1) + override(PP_CrossChain_v1) returns (bool) { return interfaceId == type(IPaymentProcessor_v1).interfaceId @@ -73,13 +78,13 @@ contract PP_Simple_v1 is Module_v1, IPaymentProcessor_v1 { _; } - /// @dev Checks that the client is calling for itself. - modifier validClient(IERC20PaymentClientBase_v1 client) { - if (_msgSender() != address(client)) { - revert Module__PaymentProcessor__CannotCallOnOtherClientsOrders(); - } - _; - } + // /// @dev Checks that the client is calling for itself. + // modifier validClient(address client) { + // if (_msgSender() != client) { + // revert Module__PaymentProcessor__CannotCallOnOtherClientsOrders(); + // } + // _; + // } //-------------------------------------------------------------------------- // Storage @@ -110,17 +115,18 @@ contract PP_Simple_v1 is Module_v1, IPaymentProcessor_v1 { /// @inheritdoc IPaymentProcessor_v1 function processPayments(IERC20PaymentClientBase_v1 client) external + override(PP_CrossChain_v1) onlyModule - validClient(client) - {}; + validClient(address(client)) + {} - function /// @inheritdoc IPaymentProcessor_v1 function cancelRunningPayments(IERC20PaymentClientBase_v1 client) external view + override onlyModule - validClient(client) + validClient(address(client)) { // Since we pay out on processing, this function does nothing return; @@ -130,32 +136,33 @@ contract PP_Simple_v1 is Module_v1, IPaymentProcessor_v1 { function unclaimable(address client, address token, address paymentReceiver) public view + override returns (uint amount) { return unclaimableAmountsForRecipient[client][token][paymentReceiver]; } - /// @inheritdoc IPaymentProcessor_v1 - function claimPreviouslyUnclaimable( - address client, - address token, - address receiver - ) external { - if (unclaimable(client, token, _msgSender()) == 0) { - revert Module__PaymentProcessor__NothingToClaim( - client, _msgSender() - ); - } - - _claimPreviouslyUnclaimable(client, token, receiver); - } + // /// @inheritdoc IPaymentProcessor_v1 + // function claimPreviouslyUnclaimable( + // address client, + // address token, + // address receiver + // ) external override { + // if (unclaimable(client, token, _msgSender()) == 0) { + // revert Module__PaymentProcessor__NothingToClaim( + // client, _msgSender() + // ); + // } + + // _claimPreviouslyUnclaimable(client, token, receiver); + // } /// @inheritdoc IPaymentProcessor_v1 function validPaymentOrder( IERC20PaymentClientBase_v1.PaymentOrder memory order - ) external returns (bool) { - return _validPaymentReceiver(order.recipient) - && _validTotal(order.amount) && _validPaymentToken(order.paymentToken); + ) external view override returns (bool) { + return + _validPaymentReceiver(order.recipient) && _validTotal(order.amount); //&& _validPaymentToken(order.paymentToken); } //-------------------------------------------------------------------------- @@ -190,7 +197,12 @@ contract PP_Simple_v1 is Module_v1, IPaymentProcessor_v1 { /// @notice Validate address input. /// @param addr Address to validate. /// @return True if address is valid. - function _validPaymentReceiver(address addr) internal view returns (bool) { + function _validPaymentReceiver(address addr) + internal + view + override + returns (bool) + { return !( addr == address(0) || addr == _msgSender() || addr == address(this) || addr == address(orchestrator()) diff --git a/src/templates/modules/PP_Template_v1.sol b/src/templates/modules/PP_Template_v1.sol index f9a61cb9e..e1be6e87b 100644 --- a/src/templates/modules/PP_Template_v1.sol +++ b/src/templates/modules/PP_Template_v1.sol @@ -200,7 +200,7 @@ contract PP_Template_v1 is IPP_Template_v1, Module_v1 { address, /*client_*/ address, /*token_*/ address /*receiver_*/ - ) external pure { + ) external virtual { return; } @@ -241,6 +241,7 @@ contract PP_Template_v1 is IPP_Template_v1, Module_v1 { function _validPaymentReceiver(address receiver_) internal view + virtual returns (bool) { return !( From 793d8a7e7a7671d0f622a9a9371cfda883100a07 Mon Sep 17 00:00:00 2001 From: Zuhaib Mohammed Date: Tue, 5 Nov 2024 04:15:24 -0800 Subject: [PATCH 07/93] add everclear poc impl --- .../bridging/PP_BR_Connext_v1.sol | 60 ++++++++++++++++--- 1 file changed, 52 insertions(+), 8 deletions(-) diff --git a/src/modules/paymentProcessor/bridging/PP_BR_Connext_v1.sol b/src/modules/paymentProcessor/bridging/PP_BR_Connext_v1.sol index 63cee07d2..f9281b263 100644 --- a/src/modules/paymentProcessor/bridging/PP_BR_Connext_v1.sol +++ b/src/modules/paymentProcessor/bridging/PP_BR_Connext_v1.sol @@ -9,7 +9,8 @@ import { IERC20PaymentClientBase_v1 } from "src/modules/paymentProcessor/IPaymentProcessor_v1.sol"; import {PP_CrossChain_v1} from "src/templates/modules/PP_Template_v1.sol"; - +import {IEverclearSpoke} from + "../../../templates/tests/unit/Interfaces/IEverClearSpoke.sol"; // Internal Dependencies import {ERC165Upgradeable, Module_v1} from "src/modules/base/Module_v1.sol"; @@ -35,6 +36,10 @@ import {SafeERC20} from "@oz/token/ERC20/utils/SafeERC20.sol"; * * @author Inverter Network */ +interface IWETH { + function deposit() external payable; + function approve(address guy, uint wad) external returns (bool); +} //TODO: Here's my guess of how we should do next // - Inherit from the PP_Template_v1 base and maybe even the interface too? @@ -94,6 +99,8 @@ contract PP_BR_Connext_v1 is mapping(address => mapping(address => mapping(address => uint))) internal unclaimableAmountsForRecipient; + IEverclearSpoke public everClearSpoke; + IWETH public weth; /// @dev Gap for possible future upgrades. uint[50] private __gap; @@ -104,21 +111,26 @@ contract PP_BR_Connext_v1 is function init( IOrchestrator_v1 orchestrator_, Metadata memory metadata, - bytes memory /*configData*/ + bytes memory configData ) external override(Module_v1) initializer { __Module_init(orchestrator_, metadata); + + (address everClearSpokeAddress, address wethAddress) = + abi.decode(configData, (address, address)); + everClearSpoke = IEverclearSpoke(everClearSpokeAddress); + weth = IWETH(wethAddress); } //-------------------------------------------------------------------------- // IPaymentProcessor_v1 Functions /// @inheritdoc IPaymentProcessor_v1 - function processPayments(IERC20PaymentClientBase_v1 client) - external - override(PP_CrossChain_v1) - onlyModule - validClient(address(client)) - {} + // function processPayments(IERC20PaymentClientBase_v1 client) + // external + // override(PP_CrossChain_v1) + // onlyModule + // validClient(address(client)) + // {} /// @inheritdoc IPaymentProcessor_v1 function cancelRunningPayments(IERC20PaymentClientBase_v1 client) @@ -231,4 +243,36 @@ contract PP_BR_Connext_v1 is ); return (success && data.length != 0 && _token.code.length != 0); } + + function _executeBridgeTransfer( + IERC20PaymentClientBase_v1.PaymentOrder memory order, + bytes memory executionData + ) public payable virtual override returns (bytes memory) { + // Decode any additional parameters from executionData + (uint maxFee, uint ttl) = abi.decode(executionData, (uint, uint)); + + // Wrap ETH into WETH to send with the xcall + weth.deposit{value: msg.value}(); + + // This contract approves transfer to EverClearSpoke + weth.approve(address(everClearSpoke), order.amount); + + // Create destinations array with the target chain + uint32[] memory destinations = new uint32[](1); + destinations[0] = 8453; // @note -> hardcoding the value as of now, need to create a new struct order.destinationChainId; + + // Call newIntent on the EverClearSpoke contract + (bytes32 intentId,) = everClearSpoke.newIntent( + destinations, + order.recipient, // to + address(weth), // order.inputAsset, // inputAsset + address(weth), // order.outputAsset, // outputAsset (assuming same asset on destination) + order.amount, // amount + uint24(maxFee), // maxFee (cast to uint24) + uint48(ttl), // ttl (cast to uint48) + "" // empty data field, modify if needed + ); + + return abi.encodePacked(intentId); + } } From af340d12d40f3dee5af734e012f01cd6c7447d7f Mon Sep 17 00:00:00 2001 From: Zuhaib Mohammed Date: Wed, 6 Nov 2024 20:22:51 -0800 Subject: [PATCH 08/93] add everclear logic impl --- .../modules/EverclearBridgeLogic.sol | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 src/templates/modules/EverclearBridgeLogic.sol diff --git a/src/templates/modules/EverclearBridgeLogic.sol b/src/templates/modules/EverclearBridgeLogic.sol new file mode 100644 index 000000000..db495caf7 --- /dev/null +++ b/src/templates/modules/EverclearBridgeLogic.sol @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity 0.8.23; + +// Internal Interfaces +import {IOrchestrator_v1} from + "src/orchestrator/interfaces/IOrchestrator_v1.sol"; +import { + IPaymentProcessor_v1, + IERC20PaymentClientBase_v1 +} from "src/modules/paymentProcessor/IPaymentProcessor_v1.sol"; +import {IEverclearSpoke} from + "src/templates/tests/unit/Interfaces/IEverClearSpoke.sol"; + +contract EverclearBridgeLogic { + address public immutable everclearSpoke; + address public immutable weth; + + constructor(address _everclearSpoke, address _weth) { + everclearSpoke = _everclearSpoke; + weth = _weth; + } + + function bridgeTransfer( + IERC20PaymentClientBase_v1.PaymentOrder memory order, + uint32[] memory destinations, + address recipient, + address inputAsset, + uint48 maxFee, + uint48 ttl + ) external payable { + // Call newIntent on the EverClearSpoke contract + (bytes32 intentId,) = IEverclearSpoke(everclearSpoke).newIntent( + destinations, + order.recipient, // to + address(weth), // order.inputAsset, // inputAsset + address(weth), // order.outputAsset, // outputAsset (assuming same asset on destination) + order.amount, // amount + uint24(maxFee), // maxFee (cast to uint24) + uint48(ttl), // ttl (cast to uint48) + "" // empty data field, modify if needed + ); + } +} From b565de285b9e775adac0bce0d97b8cbe4d3614ba Mon Sep 17 00:00:00 2001 From: Leandro Faria Date: Wed, 6 Nov 2024 23:35:37 -0500 Subject: [PATCH 09/93] remove old templates --- .../bridging/ConnextBridgeLogic.sol | 46 +++ .../bridging/IPP_Connext_Crosschain.sol | 19 ++ .../bridging/PP_BR_Connext_v1.sol | 278 ------------------ .../bridging/PP_Connext_Crosschain_v1.sol | 36 +++ src/templates/modules/CrosschainBase_v1.sol | 87 ++++++ src/templates/modules/ICrosschainBase_v1.sol | 57 ++++ 6 files changed, 245 insertions(+), 278 deletions(-) create mode 100644 src/modules/paymentProcessor/bridging/ConnextBridgeLogic.sol create mode 100644 src/modules/paymentProcessor/bridging/IPP_Connext_Crosschain.sol delete mode 100644 src/modules/paymentProcessor/bridging/PP_BR_Connext_v1.sol create mode 100644 src/modules/paymentProcessor/bridging/PP_Connext_Crosschain_v1.sol create mode 100644 src/templates/modules/CrosschainBase_v1.sol create mode 100644 src/templates/modules/ICrosschainBase_v1.sol diff --git a/src/modules/paymentProcessor/bridging/ConnextBridgeLogic.sol b/src/modules/paymentProcessor/bridging/ConnextBridgeLogic.sol new file mode 100644 index 000000000..c56bbd701 --- /dev/null +++ b/src/modules/paymentProcessor/bridging/ConnextBridgeLogic.sol @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity 0.8.23; + +interface IEverclearSpoke { + function newIntent(uint32[] memory destinations, address to, address inputAsset, address outputAsset, uint256 amount, uint24 maxFee, uint48 ttl, bytes memory data) external returns (bytes32 intentId, uint256 amountOut); +} + +contract EverclearPayment is CrossChainPayment { + IEverclearSpoke public everClearSpoke; + + constructor(address _everclearSpoke, address _weth) CrossChainPayment(_weth) { + everClearSpoke = IEverclearSpoke(_everclearSpoke); + } + + function xcall(PaymentOrder memory order, bytes memory executionData) + external + returns (bytes32 intentId) + { + // Decode any additional parameters from executionData + (uint256 maxFee, uint256 ttl) = abi.decode(executionData, (uint256, uint256)); + + // Wrap ETH into WETH to send with the xcall + weth.deposit{value: msg.value}(); + + // This contract approves transfer to EverClearSpoke + weth.approve(address(everClearSpoke), order.amount); + + // Create destinations array with the target chain + uint32[] memory destinations = new uint32[](1); + destinations[0] = order.destinationChainId; + + // Call newIntent on the EverClearSpoke contract + (bytes32 intentId,) = everClearSpoke.newIntent( + destinations, + order.receiver, // to + order.inputAsset, // inputAsset + order.outputAsset, // outputAsset (assuming same asset on destination) + order.amount, // amount + uint24(maxFee), // maxFee (cast to uint24) + uint48(ttl), // ttl (cast to uint48) + "" // empty data field, modify if needed + ); + + return intentId; + } +} \ No newline at end of file diff --git a/src/modules/paymentProcessor/bridging/IPP_Connext_Crosschain.sol b/src/modules/paymentProcessor/bridging/IPP_Connext_Crosschain.sol new file mode 100644 index 000000000..14d895b2f --- /dev/null +++ b/src/modules/paymentProcessor/bridging/IPP_Connext_Crosschain.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity ^0.8.0; + +// Internal Interfaces +import {IERC20PaymentClientBase_v1} from + "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; +import {IPaymentProcessor_v1} from + "src/modules/paymentProcessor/IPaymentProcessor_v1.sol"; + +// External Interfaces +import {IERC20} from "@oz/token/ERC20/IERC20.sol"; + +interface IPP_Connext_Crosschain is IPaymentProcessor_v1 { + + + + + +} diff --git a/src/modules/paymentProcessor/bridging/PP_BR_Connext_v1.sol b/src/modules/paymentProcessor/bridging/PP_BR_Connext_v1.sol deleted file mode 100644 index f9281b263..000000000 --- a/src/modules/paymentProcessor/bridging/PP_BR_Connext_v1.sol +++ /dev/null @@ -1,278 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity 0.8.23; - -// Internal Interfaces -import {IOrchestrator_v1} from - "src/orchestrator/interfaces/IOrchestrator_v1.sol"; -import { - IPaymentProcessor_v1, - IERC20PaymentClientBase_v1 -} from "src/modules/paymentProcessor/IPaymentProcessor_v1.sol"; -import {PP_CrossChain_v1} from "src/templates/modules/PP_Template_v1.sol"; -import {IEverclearSpoke} from - "../../../templates/tests/unit/Interfaces/IEverClearSpoke.sol"; -// Internal Dependencies -import {ERC165Upgradeable, Module_v1} from "src/modules/base/Module_v1.sol"; - -// External Interfaces -import {IERC20} from "@oz/token/ERC20/IERC20.sol"; - -// External Libraries -import {SafeERC20} from "@oz/token/ERC20/utils/SafeERC20.sol"; - -/** - * @title Inverter Simple Payment Processor - * - * @notice Manages ERC20 payment processing for modules within the Inverter Network - * that are compliant with the {IERC20PaymentClientBase_v1} interface. - * - * @dev Inherits {Module_v1} and implements {IPaymentProcessor_v1} to handle payment - * orders from registered modules, ensuring only eligible modules can initiate - * payments. Utilizes {SafeERC20} for secure token transfers. - * - * @custom:security-contact security@inverter.network - * In case of any concerns or findings, please refer to our Security Policy - * at security.inverter.network or email us directly! - * - * @author Inverter Network - */ -interface IWETH { - function deposit() external payable; - function approve(address guy, uint wad) external returns (bool); -} - -//TODO: Here's my guess of how we should do next -// - Inherit from the PP_Template_v1 base and maybe even the interface too? -// - Implement the internal execute bridge function in here, aka wrap it with an external function and leave space for logic -// - Add the logic from Everclear contract to the executeBridge fucntion (or connext if that's easier since we're mocking it for now) -// - Make sure it compiles and the if you have time write a test file that copiles -// - Test file can be in this same folder with the same nameing convention as PP_Template_v1_.t.sol, except for the name I choose here -// - Feel free to copy pp_template_v1.t.sol setup and helper fucntions to get started -// - Make sure this compiles -// - We then need to create mocks that will mock a call to the bridge -// - This is straightforward, just check to see what event the connext bridge emits after deposit -// - Create a function with the same selector in the mock, when the proper params are passed it should emit the event -// - We then need to write some basic testcases that will test the bridge logic, this should be mocked -contract PP_BR_Connext_v1 is - PP_CrossChain_v1 //@note -> PP_CrossChain_v1 already inherits from Module_v1 and IPaymentProcessor_v1 -{ - //Module_v1, - //IPaymentProcessor_v1 - /// @inheritdoc ERC165Upgradeable - function supportsInterface(bytes4 interfaceId) - public - view - virtual - override(PP_CrossChain_v1) - returns (bool) - { - return interfaceId == type(IPaymentProcessor_v1).interfaceId - || super.supportsInterface(interfaceId); - } - - using SafeERC20 for IERC20; - - //-------------------------------------------------------------------------- - // Modifiers - - /// @dev Checks that the caller is an active module. - modifier onlyModule() { - if (!orchestrator().isModule(_msgSender())) { - revert Module__PaymentProcessor__OnlyCallableByModule(); - } - _; - } - - // /// @dev Checks that the client is calling for itself. - // modifier validClient(address client) { - // if (_msgSender() != client) { - // revert Module__PaymentProcessor__CannotCallOnOtherClientsOrders(); - // } - // _; - // } - - //-------------------------------------------------------------------------- - // Storage - - /// @dev Tracks all payments that could not be made to the paymentReceiver due to any reason. - /// @dev paymentClient => token address => paymentReceiver => unclaimable Amount. - mapping(address => mapping(address => mapping(address => uint))) internal - unclaimableAmountsForRecipient; - - IEverclearSpoke public everClearSpoke; - IWETH public weth; - /// @dev Gap for possible future upgrades. - uint[50] private __gap; - - //-------------------------------------------------------------------------- - // Initializer - - /// @inheritdoc Module_v1 - function init( - IOrchestrator_v1 orchestrator_, - Metadata memory metadata, - bytes memory configData - ) external override(Module_v1) initializer { - __Module_init(orchestrator_, metadata); - - (address everClearSpokeAddress, address wethAddress) = - abi.decode(configData, (address, address)); - everClearSpoke = IEverclearSpoke(everClearSpokeAddress); - weth = IWETH(wethAddress); - } - - //-------------------------------------------------------------------------- - // IPaymentProcessor_v1 Functions - - /// @inheritdoc IPaymentProcessor_v1 - // function processPayments(IERC20PaymentClientBase_v1 client) - // external - // override(PP_CrossChain_v1) - // onlyModule - // validClient(address(client)) - // {} - - /// @inheritdoc IPaymentProcessor_v1 - function cancelRunningPayments(IERC20PaymentClientBase_v1 client) - external - view - override - onlyModule - validClient(address(client)) - { - // Since we pay out on processing, this function does nothing - return; - } - - /// @inheritdoc IPaymentProcessor_v1 - function unclaimable(address client, address token, address paymentReceiver) - public - view - override - returns (uint amount) - { - return unclaimableAmountsForRecipient[client][token][paymentReceiver]; - } - - // /// @inheritdoc IPaymentProcessor_v1 - // function claimPreviouslyUnclaimable( - // address client, - // address token, - // address receiver - // ) external override { - // if (unclaimable(client, token, _msgSender()) == 0) { - // revert Module__PaymentProcessor__NothingToClaim( - // client, _msgSender() - // ); - // } - - // _claimPreviouslyUnclaimable(client, token, receiver); - // } - - /// @inheritdoc IPaymentProcessor_v1 - function validPaymentOrder( - IERC20PaymentClientBase_v1.PaymentOrder memory order - ) external view override returns (bool) { - return - _validPaymentReceiver(order.recipient) && _validTotal(order.amount); //&& _validPaymentToken(order.paymentToken); - } - - //-------------------------------------------------------------------------- - // Internal Functions - - /// @notice used to claim the unclaimable amount of a particular `paymentReceiver` for a given payment client. - /// @param client address of the payment client. - /// @param token address of the payment token. - /// @param paymentReceiver address of the paymentReceiver for which the unclaimable amount will be claimed. - function _claimPreviouslyUnclaimable( - address client, - address token, - address paymentReceiver - ) internal { - // get amount - - address sender = _msgSender(); - // copy value over - uint amount = unclaimableAmountsForRecipient[client][token][sender]; - // Delete the field - delete unclaimableAmountsForRecipient[client][token][sender]; - - // Make sure to let paymentClient know that amount doesnt have to be stored anymore - IERC20PaymentClientBase_v1(client).amountPaid(token, amount); - - // Call has to succeed otherwise no state change - IERC20(token).safeTransferFrom(client, paymentReceiver, amount); - - emit TokensReleased(paymentReceiver, address(token), amount); - } - - /// @notice Validate address input. - /// @param addr Address to validate. - /// @return True if address is valid. - function _validPaymentReceiver(address addr) - internal - view - override - returns (bool) - { - return !( - addr == address(0) || addr == _msgSender() || addr == address(this) - || addr == address(orchestrator()) - || addr == address(orchestrator().fundingManager().token()) - ); - } - - /// @notice Validate uint total amount input. - /// @param _total uint to validate. - /// @return True if uint is valid. - function _validTotal(uint _total) internal pure returns (bool) { - return !(_total == 0); - } - - /// @notice Validate payment token input. - /// @param _token Address of the token to validate. - /// @return True if address is valid. - function _validPaymentToken(address _token) internal returns (bool) { - // Only a basic sanity check that the address supports the balanceOf() function. The corresponding - // module should ensure it's sending an ERC20. - - (bool success, bytes memory data) = _token.call( - abi.encodeWithSelector( - IERC20(_token).balanceOf.selector, address(this) - ) - ); - return (success && data.length != 0 && _token.code.length != 0); - } - - function _executeBridgeTransfer( - IERC20PaymentClientBase_v1.PaymentOrder memory order, - bytes memory executionData - ) public payable virtual override returns (bytes memory) { - // Decode any additional parameters from executionData - (uint maxFee, uint ttl) = abi.decode(executionData, (uint, uint)); - - // Wrap ETH into WETH to send with the xcall - weth.deposit{value: msg.value}(); - - // This contract approves transfer to EverClearSpoke - weth.approve(address(everClearSpoke), order.amount); - - // Create destinations array with the target chain - uint32[] memory destinations = new uint32[](1); - destinations[0] = 8453; // @note -> hardcoding the value as of now, need to create a new struct order.destinationChainId; - - // Call newIntent on the EverClearSpoke contract - (bytes32 intentId,) = everClearSpoke.newIntent( - destinations, - order.recipient, // to - address(weth), // order.inputAsset, // inputAsset - address(weth), // order.outputAsset, // outputAsset (assuming same asset on destination) - order.amount, // amount - uint24(maxFee), // maxFee (cast to uint24) - uint48(ttl), // ttl (cast to uint48) - "" // empty data field, modify if needed - ); - - return abi.encodePacked(intentId); - } -} diff --git a/src/modules/paymentProcessor/bridging/PP_Connext_Crosschain_v1.sol b/src/modules/paymentProcessor/bridging/PP_Connext_Crosschain_v1.sol new file mode 100644 index 000000000..55e26f7a0 --- /dev/null +++ b/src/modules/paymentProcessor/bridging/PP_Connext_Crosschain_v1.sol @@ -0,0 +1,36 @@ +pragma solidity ^0.8.20; + +import {CrossChain_Base_v1} from "@lm/templates/modules/CrosschainBase_v1.sol"; +import {ICrossChainBase_v1} from "@lm/templates/modules/ICrosschainbase_v1.sol"; +import {ConnextBridgeLogic} from "./ConnextBridgeLogic.sol"; + + + +contract PP_Connext_Crosschain_v1 is CrossChain_Base_v1 { + + + ConnextBridgeLogic public connextBridgeLogic; + + constructor(uint chainId_, address connextBridgeLogic_) CrossChain_Base_v1(chainId_) { + connextBridgeLogic = ConnextBridgeLogic(connextBridgeLogic_); + } + + + + + /// @notice Execute the cross-chain bridge transfer + /// @dev Override this function to implement specific bridge logic + /// @param order The payment order containing all necessary transfer details + /// @return bridgeData Arbitrary data returned by the bridge implementation + function executeBridgeTransfer( + IERC20PaymentClientBase_v1.PaymentOrder memory order, + bytes memory executionData + ) internal override returns (bytes memory) { + //@notice call the connextBridgeLogic to execute the bridge transfer + bytes32 intentId = connextBridgeLogic.xcall(order, executionData); + return abi.encode(intentId); + } +} + + +// this is equivalent ot the forumal diff --git a/src/templates/modules/CrosschainBase_v1.sol b/src/templates/modules/CrosschainBase_v1.sol new file mode 100644 index 000000000..97c001376 --- /dev/null +++ b/src/templates/modules/CrosschainBase_v1.sol @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity 0.8.23; + +import {IOrchestrator_v1} from + "src/orchestrator/interfaces/IOrchestrator_v1.sol"; +import {IPaymentProcessor_v1} from "@pp/IPaymentProcessor_v1.sol"; +import {IERC20PaymentClientBase_v1} from + "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; +import {Module_v1} from "src/modules/base/Module_v1.sol"; +import {IPP_CrossChain_v1} from "./IPP_Template_v1.sol"; +import {ERC165Upgradeable, Module_v1} from "src/modules/base/Module_v1.sol"; + +/** + * @title Inverter Template Payment Processor + * + * @notice Basic template payment processor used as base for developing new payment processors. + * + * @dev This contract is used to showcase a basic setup for a payment processor. The contract showcases the + * following: + * - Inherit from the Module_v1 contract to enable interaction with the Inverter workflow. + * - Use of the IPaymentProcessor_v1 interface to facilitate interaction with a payment client. + * - Implement custom interface which has all the public facing functions, errors, events and structs. + * - Pre-defined layout for all contract functions, modifiers, state variables etc. + * - Use of the ERC165Upgradeable contract to check for interface support. + * + * @custom:security-contact security@inverter.network + * In case of any concerns or findings, please refer to our Security Policy + * at security.inverter.network or email us directly! + * + * @author Inverter Network + */ +abstract contract CrossChain_Base_v1 is + ICrossChainBase_v1, + Module_v1 +{ + /// @inheritdoc ERC165Upgradeable + function supportsInterface(bytes4 interfaceId_) + public + view + virtual + override(Module_v1) + returns (bool) + { + return interfaceId_ == type(ICrossChainBase_v1).interfaceId + || super.supportsInterface(interfaceId_); + } + //-------------------------------------------------------------------------- + // State + + /// @dev Mapping of payment ID to bridge transfer return data + mapping(uint => bytes) internal _bridgeData; + + bytes public executionData; + + /// @dev Payout amount multiplier. + uint internal _payoutAmountMultiplier; + + /// @dev The number of payment orders. + uint internal _paymentId; + + /// @dev The chain id of the current chain. + uint internal _chainId; + + //-------------------------------------------------------------------------- + // Events + + event BridgeTransferExecuted( + uint indexed paymentId, + bytes indexed bridgeData + ); + + //-------------------------------------------------------------------------- + // Virtual Functions + + /// @notice Execute the cross-chain bridge transfer + /// @dev Override this function to implement specific bridge logic + /// @param order The payment order containing all necessary transfer details + /// @return bridgeData Arbitrary data returned by the bridge implementation + function _executeBridgeTransfer( + IERC20PaymentClientBase_v1.PaymentOrder memory order, + bytes memory executionData + ) internal virtual returns (bytes memory) { + return bytes(""); + emit BridgeTransferExecuted(order.paymentId, executionData); + } +} + diff --git a/src/templates/modules/ICrosschainBase_v1.sol b/src/templates/modules/ICrosschainBase_v1.sol new file mode 100644 index 000000000..600dc2c84 --- /dev/null +++ b/src/templates/modules/ICrosschainBase_v1.sol @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity ^0.8.0; + +// Internal Dependencies + +// External Dependencies +import {IERC20PaymentClientBase_v1} from + "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; + +interface ICrossChain_Base_v1 { + //-------------------------------------------------------------------------- + // Structs + + /// @notice Struct to hold cross-chain message data + struct CrossChainMessage { + uint messageId; + address sourceChain; + address targetChain; + bytes payload; + bool executed; + } + + //-------------------------------------------------------------------------- + // Events + + /// @notice Emitted when a bridge transfer is executed + event BridgeTransferExecuted( + uint indexed paymentId, + bytes indexed bridgeData + ); + + //-------------------------------------------------------------------------- + // Errors + + /// @notice Amount can not be zero. + error Module__CrossChainBase__InvalidAmount(); + + /// @notice Client is not valid. + error Module__CrossChainBase__NotValidClient(); + + /// @notice Message has already been executed + error Module__CrossChainBase_MessageAlreadyExecuted(); + + /// @notice Invalid chain ID provided + error Module__CrossChainBase_InvalidChainId(); + + /// @notice Message verification failed + error Module__CrossChainBase_MessageVerificationFailed(); + + //-------------------------------------------------------------------------- + // Public (Getter) + + /// @notice Get the chain ID + /// @return The chain ID + function getChainId() external view returns (uint); + +} From 7da377db81b550888a217ac379563744965ef95b Mon Sep 17 00:00:00 2001 From: Zuhaib Mohammed Date: Wed, 6 Nov 2024 22:15:59 -0800 Subject: [PATCH 10/93] code compile issue solved code compile issue solved --- .../bridging/ConnextBridgeLogic.sol | 51 +++++++++---- .../bridging/PP_Connext_Crosschain_v1.sol | 52 +++++++++---- .../bridging/interfaces/IPP_BR_Connext_v1.sol | 4 + src/templates/modules/CrosschainBase_v1.sol | 33 ++++---- src/templates/modules/ICrosschainBase_v1.sol | 8 +- src/templates/tests/unit/PP_Template_v1.t.sol | 51 +++++-------- .../unit/mocks/Mock_EverclearPayment.sol | 76 +++++++++++++++++++ 7 files changed, 195 insertions(+), 80 deletions(-) create mode 100644 src/modules/paymentProcessor/bridging/interfaces/IPP_BR_Connext_v1.sol create mode 100644 src/templates/tests/unit/mocks/Mock_EverclearPayment.sol diff --git a/src/modules/paymentProcessor/bridging/ConnextBridgeLogic.sol b/src/modules/paymentProcessor/bridging/ConnextBridgeLogic.sol index c56bbd701..10ac5a07c 100644 --- a/src/modules/paymentProcessor/bridging/ConnextBridgeLogic.sol +++ b/src/modules/paymentProcessor/bridging/ConnextBridgeLogic.sol @@ -1,40 +1,61 @@ // SPDX-License-Identifier: LGPL-3.0-only pragma solidity 0.8.23; +import {CrosschainBase_v1} from "src/templates/modules/CrosschainBase_v1.sol"; +import {IERC20PaymentClientBase_v1} from + "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; + interface IEverclearSpoke { - function newIntent(uint32[] memory destinations, address to, address inputAsset, address outputAsset, uint256 amount, uint24 maxFee, uint48 ttl, bytes memory data) external returns (bytes32 intentId, uint256 amountOut); + function newIntent( + uint32[] memory destinations, + address to, + address inputAsset, + address outputAsset, + uint amount, + uint24 maxFee, + uint48 ttl, + bytes memory data + ) external returns (bytes32 intentId, uint amountOut); } -contract EverclearPayment is CrossChainPayment { +interface IWETH { + function deposit() external payable; + + function approve(address spender, uint amount) external returns (bool); +} + +abstract contract ConnextBridgeLogic is CrosschainBase_v1 { IEverclearSpoke public everClearSpoke; + address public immutable weth; - constructor(address _everclearSpoke, address _weth) CrossChainPayment(_weth) { + constructor(address _everclearSpoke, address _weth) CrosschainBase_v1() { everClearSpoke = IEverclearSpoke(_everclearSpoke); + weth = _weth; } - function xcall(PaymentOrder memory order, bytes memory executionData) - external - returns (bytes32 intentId) - { + function xcall( + IERC20PaymentClientBase_v1.PaymentOrder memory order, + bytes memory executionData + ) external payable returns (bytes32 intentId) { // Decode any additional parameters from executionData - (uint256 maxFee, uint256 ttl) = abi.decode(executionData, (uint256, uint256)); + (uint maxFee, uint ttl) = abi.decode(executionData, (uint, uint)); // Wrap ETH into WETH to send with the xcall - weth.deposit{value: msg.value}(); + IWETH(weth).deposit{value: msg.value}(); // This contract approves transfer to EverClearSpoke - weth.approve(address(everClearSpoke), order.amount); + IWETH(weth).approve(address(everClearSpoke), order.amount); // Create destinations array with the target chain uint32[] memory destinations = new uint32[](1); - destinations[0] = order.destinationChainId; + destinations[0] = 8453; // @note -> hardcode for now -> order.destinationChainId; // Call newIntent on the EverClearSpoke contract (bytes32 intentId,) = everClearSpoke.newIntent( destinations, - order.receiver, // to - order.inputAsset, // inputAsset - order.outputAsset, // outputAsset (assuming same asset on destination) + order.recipient, // to + address(weth), // inputAsset + address(weth), // outputAsset (assuming same asset on destination) order.amount, // amount uint24(maxFee), // maxFee (cast to uint24) uint48(ttl), // ttl (cast to uint48) @@ -43,4 +64,4 @@ contract EverclearPayment is CrossChainPayment { return intentId; } -} \ No newline at end of file +} diff --git a/src/modules/paymentProcessor/bridging/PP_Connext_Crosschain_v1.sol b/src/modules/paymentProcessor/bridging/PP_Connext_Crosschain_v1.sol index 55e26f7a0..8692f21fb 100644 --- a/src/modules/paymentProcessor/bridging/PP_Connext_Crosschain_v1.sol +++ b/src/modules/paymentProcessor/bridging/PP_Connext_Crosschain_v1.sol @@ -1,28 +1,25 @@ pragma solidity ^0.8.20; -import {CrossChain_Base_v1} from "@lm/templates/modules/CrosschainBase_v1.sol"; -import {ICrossChainBase_v1} from "@lm/templates/modules/ICrosschainbase_v1.sol"; +import {CrosschainBase_v1} from "src/templates/modules/CrosschainBase_v1.sol"; +import {ICrossChainBase_v1} from "src/templates/modules/ICrosschainBase_v1.sol"; import {ConnextBridgeLogic} from "./ConnextBridgeLogic.sol"; +import {IERC20PaymentClientBase_v1} from + "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; - - -contract PP_Connext_Crosschain_v1 is CrossChain_Base_v1 { - - +contract PP_Connext_Crosschain_v1 is CrosschainBase_v1 { ConnextBridgeLogic public connextBridgeLogic; - constructor(uint chainId_, address connextBridgeLogic_) CrossChain_Base_v1(chainId_) { + constructor(uint chainId_, address connextBridgeLogic_) + CrosschainBase_v1() + { connextBridgeLogic = ConnextBridgeLogic(connextBridgeLogic_); - } - - - + } /// @notice Execute the cross-chain bridge transfer /// @dev Override this function to implement specific bridge logic /// @param order The payment order containing all necessary transfer details /// @return bridgeData Arbitrary data returned by the bridge implementation - function executeBridgeTransfer( + function _executeBridgeTransfer( IERC20PaymentClientBase_v1.PaymentOrder memory order, bytes memory executionData ) internal override returns (bytes memory) { @@ -30,7 +27,32 @@ contract PP_Connext_Crosschain_v1 is CrossChain_Base_v1 { bytes32 intentId = connextBridgeLogic.xcall(order, executionData); return abi.encode(intentId); } -} + function processPayments(IERC20PaymentClientBase_v1 client) + external + override + { + // Collect orders from the client + IERC20PaymentClientBase_v1.PaymentOrder[] memory orders; + (orders,,) = client.collectPaymentOrders(); + + for (uint i = 0; i < orders.length; i++) { + bytes memory bridgeData = + _executeBridgeTransfer(orders[i], executionData); + _bridgeData[_paymentId] = bridgeData; + + emit PaymentProcessed( + _paymentId, + orders[i].recipient, + orders[i].paymentToken, + orders[i].amount + ); + _paymentId++; + + // Inform the client about the processed amount + client.amountPaid(orders[i].paymentToken, orders[i].amount); + } + } +} -// this is equivalent ot the forumal +// this is equivalent ot the forumal diff --git a/src/modules/paymentProcessor/bridging/interfaces/IPP_BR_Connext_v1.sol b/src/modules/paymentProcessor/bridging/interfaces/IPP_BR_Connext_v1.sol new file mode 100644 index 000000000..1c7908317 --- /dev/null +++ b/src/modules/paymentProcessor/bridging/interfaces/IPP_BR_Connext_v1.sol @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity ^0.8.0; + +interface IPP_BR_Connext_v1 {} diff --git a/src/templates/modules/CrosschainBase_v1.sol b/src/templates/modules/CrosschainBase_v1.sol index 97c001376..f223f7566 100644 --- a/src/templates/modules/CrosschainBase_v1.sol +++ b/src/templates/modules/CrosschainBase_v1.sol @@ -7,9 +7,9 @@ import {IPaymentProcessor_v1} from "@pp/IPaymentProcessor_v1.sol"; import {IERC20PaymentClientBase_v1} from "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; import {Module_v1} from "src/modules/base/Module_v1.sol"; -import {IPP_CrossChain_v1} from "./IPP_Template_v1.sol"; +//import {IPP_CrossChain_v1} from "./IPP_Template_v1.sol"; import {ERC165Upgradeable, Module_v1} from "src/modules/base/Module_v1.sol"; - +import {ICrossChainBase_v1} from "./ICrosschainBase_v1.sol"; /** * @title Inverter Template Payment Processor * @@ -29,10 +29,8 @@ import {ERC165Upgradeable, Module_v1} from "src/modules/base/Module_v1.sol"; * * @author Inverter Network */ -abstract contract CrossChain_Base_v1 is - ICrossChainBase_v1, - Module_v1 -{ + +contract CrosschainBase_v1 is ICrossChainBase_v1, Module_v1 { /// @inheritdoc ERC165Upgradeable function supportsInterface(bytes4 interfaceId_) public @@ -49,7 +47,7 @@ abstract contract CrossChain_Base_v1 is /// @dev Mapping of payment ID to bridge transfer return data mapping(uint => bytes) internal _bridgeData; - + bytes public executionData; /// @dev Payout amount multiplier. @@ -64,9 +62,8 @@ abstract contract CrossChain_Base_v1 is //-------------------------------------------------------------------------- // Events - event BridgeTransferExecuted( - uint indexed paymentId, - bytes indexed bridgeData + event PaymentProcessed( + uint indexed paymentId, address recipient, address token, uint amount ); //-------------------------------------------------------------------------- @@ -80,8 +77,18 @@ abstract contract CrossChain_Base_v1 is IERC20PaymentClientBase_v1.PaymentOrder memory order, bytes memory executionData ) internal virtual returns (bytes memory) { - return bytes(""); - emit BridgeTransferExecuted(order.paymentId, executionData); + // return bytes(""); + // emit BridgeTransferExecuted(executionData); } -} + function getChainId() external view returns (uint) { + return _chainId; + } + + /// @notice Process payments for a given payment client + /// @param client The payment client to process payments for + function processPayments(IERC20PaymentClientBase_v1 client) + external + virtual + {} +} diff --git a/src/templates/modules/ICrosschainBase_v1.sol b/src/templates/modules/ICrosschainBase_v1.sol index 600dc2c84..edc9f68ab 100644 --- a/src/templates/modules/ICrosschainBase_v1.sol +++ b/src/templates/modules/ICrosschainBase_v1.sol @@ -7,7 +7,7 @@ pragma solidity ^0.8.0; import {IERC20PaymentClientBase_v1} from "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; -interface ICrossChain_Base_v1 { +interface ICrossChainBase_v1 { //-------------------------------------------------------------------------- // Structs @@ -24,10 +24,7 @@ interface ICrossChain_Base_v1 { // Events /// @notice Emitted when a bridge transfer is executed - event BridgeTransferExecuted( - uint indexed paymentId, - bytes indexed bridgeData - ); + event BridgeTransferExecuted(bytes indexed bridgeData); //-------------------------------------------------------------------------- // Errors @@ -53,5 +50,4 @@ interface ICrossChain_Base_v1 { /// @notice Get the chain ID /// @return The chain ID function getChainId() external view returns (uint); - } diff --git a/src/templates/tests/unit/PP_Template_v1.t.sol b/src/templates/tests/unit/PP_Template_v1.t.sol index f4c466f55..5f209aeef 100644 --- a/src/templates/tests/unit/PP_Template_v1.t.sol +++ b/src/templates/tests/unit/PP_Template_v1.t.sol @@ -62,16 +62,16 @@ contract PP_Template_v1_Test is ModuleTest { ERC20PaymentClientBaseV1Mock paymentClient; //System under test (SuT) - //PP_CrossChain_v1 public paymentProcessor; - PP_CrossChain_v1_Exposed public paymentProcessor; + CrosschainBase_v1 public paymentProcessor; + //PP_CrossChain_v1_Exposed public paymentProcessor; //-------------------------------------------------------------------------- //Setup function setUp() public { //This function is used to setup the unit test //Deploy the SuT - address impl = address(new PP_CrossChain_v1_Exposed()); - paymentProcessor = PP_CrossChain_v1_Exposed(Clones.clone(impl)); + address impl = address(new CrosschainBase_v1()); + paymentProcessor = CrosschainBase_v1(Clones.clone(impl)); //Setup the module to test _setUpOrchestrator(paymentProcessor); @@ -110,14 +110,14 @@ contract PP_Template_v1_Test is ModuleTest { //Test the interface support function testSupportsInterface() public { + // assertTrue( + // paymentProcessor.supportsInterface( + // type(IPaymentProcessor_v1).interfaceId + // ) + // ); assertTrue( paymentProcessor.supportsInterface( - type(IPaymentProcessor_v1).interfaceId - ) - ); - assertTrue( - paymentProcessor.supportsInterface( - type(IPP_CrossChain_v1).interfaceId + type(ICrossChainBase_v1).interfaceId ) ); } @@ -141,29 +141,18 @@ contract PP_Template_v1_Test is ModuleTest { └── When the function processPayment() is called └── Then it should revert */ - function testProcessPayments_modifierInPlace() public { - ERC20PaymentClientBaseV1Mock nonRegisteredClient = - new ERC20PaymentClientBaseV1Mock(); - - vm.expectRevert( - IPP_CrossChain_v1.Module__PP_Template__NotValidClient.selector - ); - paymentProcessor.processPayments(nonRegisteredClient); - } + // function testProcessPayments_modifierInPlace() public { + // ERC20PaymentClientBaseV1Mock nonRegisteredClient = + // new ERC20PaymentClientBaseV1Mock(); + // vm.expectRevert( + // ICrossChainBase_v1.Module__CrossChainBase__NotValidClient.selector + // ); + // paymentProcessor.processPayments(nonRegisteredClient); + // } + // //-------------------------------------------------------------------------- - //Test: External (public & external) - - function testExposed_executeBridgeTransfer() public { - bytes memory bridgeData = paymentProcessor.exposed_executeBridgeTransfer( - IERC20PaymentClientBase_v1.PaymentOrder( - address(paymentClient), address(0xabcd), 25 ether, 0, 0, 0 - ), - "" - ); - console2.logBytes(bridgeData); - assertEq(bridgeData.length, 0); - } + //Test: External (public & external)Cros //Test external processPayments() function diff --git a/src/templates/tests/unit/mocks/Mock_EverclearPayment.sol b/src/templates/tests/unit/mocks/Mock_EverclearPayment.sol new file mode 100644 index 000000000..cbe44eab4 --- /dev/null +++ b/src/templates/tests/unit/mocks/Mock_EverclearPayment.sol @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.23; + +interface IWETH { + function deposit() external payable; + + function approve(address spender, uint amount) external returns (bool); +} + +contract Mock_EverclearPayment { + event IntentAdded(bytes32 intentId, uint queuePosition, Intent intent); + + uint public nonce; + uint32 public DOMAIN; + mapping(bytes32 => IntentStatus) public status; + + enum IntentStatus { + NONE, + ADDED, + SETTLED, + SETTLED_AND_MANUALLY_EXECUTED + } + + struct Intent { + address initiator; + address receiver; + address inputAsset; + address outputAsset; + uint amount; + uint24 maxFee; + uint32 origin; + uint32[] destinations; + uint nonce; + uint48 timestamp; + uint48 ttl; + bytes data; + } + + function newIntent( + uint32[] memory _destinations, + address _to, + address _inputAsset, + address _outputAsset, + uint _amount, + uint24 _maxFee, + uint48 _ttl, + bytes calldata _data + ) external returns (bytes32 _intentId, Intent memory _intent) { + // Increment nonce for each new intent + nonce++; + + _intent = Intent({ + initiator: msg.sender, + receiver: _to, + inputAsset: _inputAsset, + outputAsset: _outputAsset, + amount: _amount, + maxFee: _maxFee, + origin: DOMAIN, + destinations: _destinations, + nonce: nonce, + timestamp: uint48(block.timestamp), + ttl: _ttl, + data: _data + }); + + // Generate a unique intent ID + _intentId = keccak256(abi.encode(_intent)); + + // Set intent status to ADDED and emit the event + status[_intentId] = IntentStatus.ADDED; + emit IntentAdded(_intentId, nonce, _intent); + + return (_intentId, _intent); + } +} From 865e2689d1d5c08d97bd240c1fafd77610dc50a6 Mon Sep 17 00:00:00 2001 From: Zuhaib Mohammed Date: Thu, 7 Nov 2024 03:04:30 -0800 Subject: [PATCH 11/93] fix minor dependency --- .../bridging/ConnextBridgeLogic.sol | 19 +++++++++---------- .../unit/mocks/Mock_EverclearPayment.sol | 6 ------ 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/src/modules/paymentProcessor/bridging/ConnextBridgeLogic.sol b/src/modules/paymentProcessor/bridging/ConnextBridgeLogic.sol index 10ac5a07c..a812d6d2b 100644 --- a/src/modules/paymentProcessor/bridging/ConnextBridgeLogic.sol +++ b/src/modules/paymentProcessor/bridging/ConnextBridgeLogic.sol @@ -4,6 +4,7 @@ pragma solidity 0.8.23; import {CrosschainBase_v1} from "src/templates/modules/CrosschainBase_v1.sol"; import {IERC20PaymentClientBase_v1} from "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; +import {IERC20} from "@oz/token/ERC20/IERC20.sol"; interface IEverclearSpoke { function newIntent( @@ -18,13 +19,7 @@ interface IEverclearSpoke { ) external returns (bytes32 intentId, uint amountOut); } -interface IWETH { - function deposit() external payable; - - function approve(address spender, uint amount) external returns (bool); -} - -abstract contract ConnextBridgeLogic is CrosschainBase_v1 { +contract ConnextBridgeLogic is CrosschainBase_v1 { IEverclearSpoke public everClearSpoke; address public immutable weth; @@ -41,10 +36,14 @@ abstract contract ConnextBridgeLogic is CrosschainBase_v1 { (uint maxFee, uint ttl) = abi.decode(executionData, (uint, uint)); // Wrap ETH into WETH to send with the xcall - IWETH(weth).deposit{value: msg.value}(); + IERC20(order.paymentToken).transferFrom( + msg.sender, address(this), order.amount + ); // This contract approves transfer to EverClearSpoke - IWETH(weth).approve(address(everClearSpoke), order.amount); + IERC20(order.paymentToken).approve( + address(everClearSpoke), order.amount + ); // Create destinations array with the target chain uint32[] memory destinations = new uint32[](1); @@ -54,7 +53,7 @@ abstract contract ConnextBridgeLogic is CrosschainBase_v1 { (bytes32 intentId,) = everClearSpoke.newIntent( destinations, order.recipient, // to - address(weth), // inputAsset + order.paymentToken, // inputAsset address(weth), // outputAsset (assuming same asset on destination) order.amount, // amount uint24(maxFee), // maxFee (cast to uint24) diff --git a/src/templates/tests/unit/mocks/Mock_EverclearPayment.sol b/src/templates/tests/unit/mocks/Mock_EverclearPayment.sol index cbe44eab4..fbf4aa0ff 100644 --- a/src/templates/tests/unit/mocks/Mock_EverclearPayment.sol +++ b/src/templates/tests/unit/mocks/Mock_EverclearPayment.sol @@ -1,12 +1,6 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.23; -interface IWETH { - function deposit() external payable; - - function approve(address spender, uint amount) external returns (bool); -} - contract Mock_EverclearPayment { event IntentAdded(bytes32 intentId, uint queuePosition, Intent intent); From 960fcd24390086fa3e1d01216a009d1131405ad0 Mon Sep 17 00:00:00 2001 From: Zuhaib Mohammed Date: Thu, 7 Nov 2024 03:48:33 -0800 Subject: [PATCH 12/93] remove file --- .../modules/EverclearBridgeLogic.sol | 43 ------------------- 1 file changed, 43 deletions(-) delete mode 100644 src/templates/modules/EverclearBridgeLogic.sol diff --git a/src/templates/modules/EverclearBridgeLogic.sol b/src/templates/modules/EverclearBridgeLogic.sol deleted file mode 100644 index db495caf7..000000000 --- a/src/templates/modules/EverclearBridgeLogic.sol +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity 0.8.23; - -// Internal Interfaces -import {IOrchestrator_v1} from - "src/orchestrator/interfaces/IOrchestrator_v1.sol"; -import { - IPaymentProcessor_v1, - IERC20PaymentClientBase_v1 -} from "src/modules/paymentProcessor/IPaymentProcessor_v1.sol"; -import {IEverclearSpoke} from - "src/templates/tests/unit/Interfaces/IEverClearSpoke.sol"; - -contract EverclearBridgeLogic { - address public immutable everclearSpoke; - address public immutable weth; - - constructor(address _everclearSpoke, address _weth) { - everclearSpoke = _everclearSpoke; - weth = _weth; - } - - function bridgeTransfer( - IERC20PaymentClientBase_v1.PaymentOrder memory order, - uint32[] memory destinations, - address recipient, - address inputAsset, - uint48 maxFee, - uint48 ttl - ) external payable { - // Call newIntent on the EverClearSpoke contract - (bytes32 intentId,) = IEverclearSpoke(everclearSpoke).newIntent( - destinations, - order.recipient, // to - address(weth), // order.inputAsset, // inputAsset - address(weth), // order.outputAsset, // outputAsset (assuming same asset on destination) - order.amount, // amount - uint24(maxFee), // maxFee (cast to uint24) - uint48(ttl), // ttl (cast to uint48) - "" // empty data field, modify if needed - ); - } -} From fc33b2e08e9792276b26bcfb4125c8a936c6e847 Mon Sep 17 00:00:00 2001 From: Leandro Faria Date: Fri, 8 Nov 2024 00:46:04 -0500 Subject: [PATCH 13/93] added contract for bridge testing comment our bridge test --- .../bridging/PP_Connext_Crosschain_v1.sol | 2 - .../bridging/PP_Connext_Bridge.t.sol | 84 +++++++++++++++++++ .../tests/unit/FM_Template_v1.t.sol | 0 .../tests/unit/FM_Template_v1_Exposed.sol | 0 .../tests/unit/Interfaces/IEverClearSpoke.sol | 0 .../tests/unit/PP_Crosschain_v1_Exposed.sol | 0 .../tests/unit/PP_Template_v1.t.sol | 8 +- .../unit/mocks/Mock_EverclearPayment.sol | 0 8 files changed, 86 insertions(+), 8 deletions(-) create mode 100644 test/modules/paymentProcessor/bridging/PP_Connext_Bridge.t.sol rename {src/templates => test}/tests/unit/FM_Template_v1.t.sol (100%) rename {src/templates => test}/tests/unit/FM_Template_v1_Exposed.sol (100%) rename {src/templates => test}/tests/unit/Interfaces/IEverClearSpoke.sol (100%) rename src/templates/tests/unit/PP_Template_v1_Exposed.sol => test/tests/unit/PP_Crosschain_v1_Exposed.sol (100%) rename {src/templates => test}/tests/unit/PP_Template_v1.t.sol (97%) rename {src/templates => test}/tests/unit/mocks/Mock_EverclearPayment.sol (100%) diff --git a/src/modules/paymentProcessor/bridging/PP_Connext_Crosschain_v1.sol b/src/modules/paymentProcessor/bridging/PP_Connext_Crosschain_v1.sol index 8692f21fb..af18c798c 100644 --- a/src/modules/paymentProcessor/bridging/PP_Connext_Crosschain_v1.sol +++ b/src/modules/paymentProcessor/bridging/PP_Connext_Crosschain_v1.sol @@ -54,5 +54,3 @@ contract PP_Connext_Crosschain_v1 is CrosschainBase_v1 { } } } - -// this is equivalent ot the forumal diff --git a/test/modules/paymentProcessor/bridging/PP_Connext_Bridge.t.sol b/test/modules/paymentProcessor/bridging/PP_Connext_Bridge.t.sol new file mode 100644 index 000000000..91a33b79a --- /dev/null +++ b/test/modules/paymentProcessor/bridging/PP_Connext_Bridge.t.sol @@ -0,0 +1,84 @@ +//SPDX-License-Identifier: LGPL-3.0-only +pragma solidity ^0.8.0; + +//Internal Dependencies +import { + ModuleTest, + IModule_v1, + IOrchestrator_v1 +} from "test/modules/ModuleTest.sol"; +import {OZErrors} from "test/utils/errors/OZErrors.sol"; +import {ICrossChainBase_v1} from "src/templates/modules/ICrosschainBase_v1.sol"; +import {CrosschainBase_v1} from "src/templates/modules/CrosschainBase_v1.sol"; +//External Dependencies +import {Clones} from "@oz/proxy/Clones.sol"; + +//Tests and Mocks +// import cr +import { + IERC20PaymentClientBase_v1, + ERC20PaymentClientBaseV1Mock, + ERC20Mock +} from "test/utils/mocks/modules/paymentClient/ERC20PaymentClientBaseV1Mock.sol"; + +// //import exposed +// import {PP_CrossChain_v1_Exposed} from +// "../../tests/unit/PP_CrossChain_v1_Exposed.sol"; + +//System under test (SuT) +// import { +// PP_Connext_Crosschain_v1, +// IPP_Connext_Crosschain_v1 +// } from "../../../src/templates/modules/Connext_Bridge.sol"; +// import {IPaymentProcessor_v1} from +// "../../../src/orchestrator/interfaces/IOrchestrator_v1.sol"; + +import {ICrossChainBase_v1} from "src/templates/modules/ICrosschainBase_v1.sol"; +import {console2} from "forge-std/console2.sol"; +/** + * @title Inverter Template Payment Processor + * + * @notice Basic template payment processor used to showcase the unit testing setup + * + * @dev Not all functions are tested in this template. Placeholders of the functions that are not tested are added + * into the contract. This test showcases the following: + * - Inherit from the ModuleTest contract to enable interaction with the Inverter workflow. + * - Showcases the setup of the workflow, uses in test unit tests. + * - Pre-defined layout for all setup and functions to be tested. + * - Shows the use of Gherkin for documenting the testing. VS Code extension used for formatting is recommended. + * - Shows the use of the modifierInPlace pattern to test the modifier placement. + * + * @custom:security-contact security@inverter.network + * In case of any concerns or findings, please refer to our Security Policy + * at security.inverter.network or email us directly! + * + * @author Inverter Network + */ + +contract PP_Connext_Bridge_Test is ModuleTest { + //-------------------------------------------------------------------------- + // //Constants + // uint internal constant _payoutAmountMultiplier = 2; + + // //-------------------------------------------------------------------------- + // //State + + // //Mocks + // ERC20PaymentClientBaseV1Mock paymentClient; + + //-------------------------------------------------------------------------- + //Setup + function setUp() public {} + + //-------------------------------------------------------------------------- + //Test: Initialization + + //Test if the orchestrator is correctly set + function testInit() public override(ModuleTest) {} + + //Test the interface support + function testSupportsInterface() public {} + + //Test the reinit function + function testReinitFails() public override(ModuleTest) {} +} diff --git a/src/templates/tests/unit/FM_Template_v1.t.sol b/test/tests/unit/FM_Template_v1.t.sol similarity index 100% rename from src/templates/tests/unit/FM_Template_v1.t.sol rename to test/tests/unit/FM_Template_v1.t.sol diff --git a/src/templates/tests/unit/FM_Template_v1_Exposed.sol b/test/tests/unit/FM_Template_v1_Exposed.sol similarity index 100% rename from src/templates/tests/unit/FM_Template_v1_Exposed.sol rename to test/tests/unit/FM_Template_v1_Exposed.sol diff --git a/src/templates/tests/unit/Interfaces/IEverClearSpoke.sol b/test/tests/unit/Interfaces/IEverClearSpoke.sol similarity index 100% rename from src/templates/tests/unit/Interfaces/IEverClearSpoke.sol rename to test/tests/unit/Interfaces/IEverClearSpoke.sol diff --git a/src/templates/tests/unit/PP_Template_v1_Exposed.sol b/test/tests/unit/PP_Crosschain_v1_Exposed.sol similarity index 100% rename from src/templates/tests/unit/PP_Template_v1_Exposed.sol rename to test/tests/unit/PP_Crosschain_v1_Exposed.sol diff --git a/src/templates/tests/unit/PP_Template_v1.t.sol b/test/tests/unit/PP_Template_v1.t.sol similarity index 97% rename from src/templates/tests/unit/PP_Template_v1.t.sol rename to test/tests/unit/PP_Template_v1.t.sol index 5f209aeef..3ed3e3ce0 100644 --- a/src/templates/tests/unit/PP_Template_v1.t.sol +++ b/test/tests/unit/PP_Template_v1.t.sol @@ -20,7 +20,8 @@ import { ERC20Mock } from "test/utils/mocks/modules/paymentClient/ERC20PaymentClientBaseV1Mock.sol"; //import exposed -import {PP_CrossChain_v1_Exposed} from "./PP_Template_v1_Exposed.sol"; +import {PP_CrossChain_v1_Exposed} from + "../../tests/unit/PP_CrossChain_v1_Exposed.sol"; // System under Test (SuT) import { @@ -110,11 +111,6 @@ contract PP_Template_v1_Test is ModuleTest { //Test the interface support function testSupportsInterface() public { - // assertTrue( - // paymentProcessor.supportsInterface( - // type(IPaymentProcessor_v1).interfaceId - // ) - // ); assertTrue( paymentProcessor.supportsInterface( type(ICrossChainBase_v1).interfaceId diff --git a/src/templates/tests/unit/mocks/Mock_EverclearPayment.sol b/test/tests/unit/mocks/Mock_EverclearPayment.sol similarity index 100% rename from src/templates/tests/unit/mocks/Mock_EverclearPayment.sol rename to test/tests/unit/mocks/Mock_EverclearPayment.sol From 7584abbb29992acc279782d6a4874a7b4358ef74 Mon Sep 17 00:00:00 2001 From: Leandro Faria Date: Fri, 8 Nov 2024 13:18:24 -0500 Subject: [PATCH 14/93] add tests for base contracts --- .../paymentProcessor/bridging/PP_Connext_Bridge.t.sol | 1 - .../unit/{PP_Template_v1.t.sol => PP_Crosschain_v1.t.sol} | 8 ++++++-- 2 files changed, 6 insertions(+), 3 deletions(-) rename test/tests/unit/{PP_Template_v1.t.sol => PP_Crosschain_v1.t.sol} (97%) diff --git a/test/modules/paymentProcessor/bridging/PP_Connext_Bridge.t.sol b/test/modules/paymentProcessor/bridging/PP_Connext_Bridge.t.sol index 91a33b79a..3791670ac 100644 --- a/test/modules/paymentProcessor/bridging/PP_Connext_Bridge.t.sol +++ b/test/modules/paymentProcessor/bridging/PP_Connext_Bridge.t.sol @@ -58,7 +58,6 @@ import {console2} from "forge-std/console2.sol"; contract PP_Connext_Bridge_Test is ModuleTest { //-------------------------------------------------------------------------- // //Constants - // uint internal constant _payoutAmountMultiplier = 2; // //-------------------------------------------------------------------------- // //State diff --git a/test/tests/unit/PP_Template_v1.t.sol b/test/tests/unit/PP_Crosschain_v1.t.sol similarity index 97% rename from test/tests/unit/PP_Template_v1.t.sol rename to test/tests/unit/PP_Crosschain_v1.t.sol index 3ed3e3ce0..77a3e392f 100644 --- a/test/tests/unit/PP_Template_v1.t.sol +++ b/test/tests/unit/PP_Crosschain_v1.t.sol @@ -20,8 +20,7 @@ import { ERC20Mock } from "test/utils/mocks/modules/paymentClient/ERC20PaymentClientBaseV1Mock.sol"; //import exposed -import {PP_CrossChain_v1_Exposed} from - "../../tests/unit/PP_CrossChain_v1_Exposed.sol"; +import {PP_CrossChain_v1_Exposed} from "./PP_Crosschain_v1_Exposed.sol"; // System under Test (SuT) import { @@ -111,6 +110,11 @@ contract PP_Template_v1_Test is ModuleTest { //Test the interface support function testSupportsInterface() public { + // assertTrue( + // paymentProcessor.supportsInterface( + // type(IPaymentProcessor_v1).interfaceId + // ) + // ); assertTrue( paymentProcessor.supportsInterface( type(ICrossChainBase_v1).interfaceId From 7f20d42c90739146d5f3eb8bc1ea5321a6297f63 Mon Sep 17 00:00:00 2001 From: Leandro Faria Date: Fri, 8 Nov 2024 13:48:15 -0500 Subject: [PATCH 15/93] fix file structure --- .../abstract}/CrosschainBase_v1.sol | 0 .../abstract}/FM_Template_v1.sol | 0 .../abstract}/ICrosschainBase_v1.sol | 0 .../abstract}/IFM_Template_v1.sol | 0 .../abstract}/IPP_Template_v1.sol | 0 .../abstract}/PP_Template_v1.sol | 0 .../bridging/ConnextBridgeLogic.sol | 3 +- .../bridging/IPP_Connext_Crosschain.sol | 6 +--- .../bridging/PP_Connext_Crosschain_v1.sol | 6 ++-- .../bridging/PP_Connext_Bridge.t.sol | 10 ++++--- .../abstracts/CrosschainBase_v1.t.sol} | 30 +++++++++++-------- .../abstracts/CrosschainBase_v1_Exposed.sol | 19 ++++++++++++ .../bridging/abstracts}/FM_Template_v1.t.sol | 0 .../abstracts}/FM_Template_v1_Exposed.sol | 0 .../abstracts}/Interfaces/IEverClearSpoke.sol | 0 .../mocks/Mock_EverclearPayment.sol | 0 test/tests/unit/PP_Crosschain_v1_Exposed.sol | 30 ------------------- 17 files changed, 50 insertions(+), 54 deletions(-) rename src/{templates/modules => modules/paymentProcessor/abstract}/CrosschainBase_v1.sol (100%) rename src/{templates/modules => modules/paymentProcessor/abstract}/FM_Template_v1.sol (100%) rename src/{templates/modules => modules/paymentProcessor/abstract}/ICrosschainBase_v1.sol (100%) rename src/{templates/modules => modules/paymentProcessor/abstract}/IFM_Template_v1.sol (100%) rename src/{templates/modules => modules/paymentProcessor/abstract}/IPP_Template_v1.sol (100%) rename src/{templates/modules => modules/paymentProcessor/abstract}/PP_Template_v1.sol (100%) rename test/{tests/unit/PP_Crosschain_v1.t.sol => modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.t.sol} (91%) create mode 100644 test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1_Exposed.sol rename test/{tests/unit => modules/paymentProcessor/bridging/abstracts}/FM_Template_v1.t.sol (100%) rename test/{tests/unit => modules/paymentProcessor/bridging/abstracts}/FM_Template_v1_Exposed.sol (100%) rename test/{tests/unit => modules/paymentProcessor/bridging/abstracts}/Interfaces/IEverClearSpoke.sol (100%) rename test/{tests/unit => modules/paymentProcessor/bridging/abstracts}/mocks/Mock_EverclearPayment.sol (100%) delete mode 100644 test/tests/unit/PP_Crosschain_v1_Exposed.sol diff --git a/src/templates/modules/CrosschainBase_v1.sol b/src/modules/paymentProcessor/abstract/CrosschainBase_v1.sol similarity index 100% rename from src/templates/modules/CrosschainBase_v1.sol rename to src/modules/paymentProcessor/abstract/CrosschainBase_v1.sol diff --git a/src/templates/modules/FM_Template_v1.sol b/src/modules/paymentProcessor/abstract/FM_Template_v1.sol similarity index 100% rename from src/templates/modules/FM_Template_v1.sol rename to src/modules/paymentProcessor/abstract/FM_Template_v1.sol diff --git a/src/templates/modules/ICrosschainBase_v1.sol b/src/modules/paymentProcessor/abstract/ICrosschainBase_v1.sol similarity index 100% rename from src/templates/modules/ICrosschainBase_v1.sol rename to src/modules/paymentProcessor/abstract/ICrosschainBase_v1.sol diff --git a/src/templates/modules/IFM_Template_v1.sol b/src/modules/paymentProcessor/abstract/IFM_Template_v1.sol similarity index 100% rename from src/templates/modules/IFM_Template_v1.sol rename to src/modules/paymentProcessor/abstract/IFM_Template_v1.sol diff --git a/src/templates/modules/IPP_Template_v1.sol b/src/modules/paymentProcessor/abstract/IPP_Template_v1.sol similarity index 100% rename from src/templates/modules/IPP_Template_v1.sol rename to src/modules/paymentProcessor/abstract/IPP_Template_v1.sol diff --git a/src/templates/modules/PP_Template_v1.sol b/src/modules/paymentProcessor/abstract/PP_Template_v1.sol similarity index 100% rename from src/templates/modules/PP_Template_v1.sol rename to src/modules/paymentProcessor/abstract/PP_Template_v1.sol diff --git a/src/modules/paymentProcessor/bridging/ConnextBridgeLogic.sol b/src/modules/paymentProcessor/bridging/ConnextBridgeLogic.sol index a812d6d2b..3593ff565 100644 --- a/src/modules/paymentProcessor/bridging/ConnextBridgeLogic.sol +++ b/src/modules/paymentProcessor/bridging/ConnextBridgeLogic.sol @@ -1,7 +1,8 @@ // SPDX-License-Identifier: LGPL-3.0-only pragma solidity 0.8.23; -import {CrosschainBase_v1} from "src/templates/modules/CrosschainBase_v1.sol"; +import {CrosschainBase_v1} from + "src/modules/paymentProcessor/abstract/CrosschainBase_v1.sol"; import {IERC20PaymentClientBase_v1} from "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; import {IERC20} from "@oz/token/ERC20/IERC20.sol"; diff --git a/src/modules/paymentProcessor/bridging/IPP_Connext_Crosschain.sol b/src/modules/paymentProcessor/bridging/IPP_Connext_Crosschain.sol index 14d895b2f..a52630cc0 100644 --- a/src/modules/paymentProcessor/bridging/IPP_Connext_Crosschain.sol +++ b/src/modules/paymentProcessor/bridging/IPP_Connext_Crosschain.sol @@ -11,9 +11,5 @@ import {IPaymentProcessor_v1} from import {IERC20} from "@oz/token/ERC20/IERC20.sol"; interface IPP_Connext_Crosschain is IPaymentProcessor_v1 { - - - - - +///Need to add the functions that are specific to Connext } diff --git a/src/modules/paymentProcessor/bridging/PP_Connext_Crosschain_v1.sol b/src/modules/paymentProcessor/bridging/PP_Connext_Crosschain_v1.sol index af18c798c..39f04c1a4 100644 --- a/src/modules/paymentProcessor/bridging/PP_Connext_Crosschain_v1.sol +++ b/src/modules/paymentProcessor/bridging/PP_Connext_Crosschain_v1.sol @@ -1,7 +1,9 @@ pragma solidity ^0.8.20; -import {CrosschainBase_v1} from "src/templates/modules/CrosschainBase_v1.sol"; -import {ICrossChainBase_v1} from "src/templates/modules/ICrosschainBase_v1.sol"; +import {CrosschainBase_v1} from + "src/modules/paymentProcessor/abstract/CrosschainBase_v1.sol"; +import {ICrossChainBase_v1} from + "src/modules/paymentProcessor/abstract/ICrosschainBase_v1.sol"; import {ConnextBridgeLogic} from "./ConnextBridgeLogic.sol"; import {IERC20PaymentClientBase_v1} from "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; diff --git a/test/modules/paymentProcessor/bridging/PP_Connext_Bridge.t.sol b/test/modules/paymentProcessor/bridging/PP_Connext_Bridge.t.sol index 3791670ac..36ce667c9 100644 --- a/test/modules/paymentProcessor/bridging/PP_Connext_Bridge.t.sol +++ b/test/modules/paymentProcessor/bridging/PP_Connext_Bridge.t.sol @@ -8,8 +8,10 @@ import { IOrchestrator_v1 } from "test/modules/ModuleTest.sol"; import {OZErrors} from "test/utils/errors/OZErrors.sol"; -import {ICrossChainBase_v1} from "src/templates/modules/ICrosschainBase_v1.sol"; -import {CrosschainBase_v1} from "src/templates/modules/CrosschainBase_v1.sol"; +import {ICrossChainBase_v1} from + "src/modules/paymentProcessor/abstract/ICrosschainBase_v1.sol"; +import {CrosschainBase_v1} from + "src/modules/paymentProcessor/abstract/CrosschainBase_v1.sol"; //External Dependencies import {Clones} from "@oz/proxy/Clones.sol"; @@ -33,8 +35,8 @@ import { // import {IPaymentProcessor_v1} from // "../../../src/orchestrator/interfaces/IOrchestrator_v1.sol"; -import {ICrossChainBase_v1} from "src/templates/modules/ICrosschainBase_v1.sol"; -import {console2} from "forge-std/console2.sol"; +import {ICrossChainBase_v1} from + "src/modules/paymentProcessor/abstract/ICrosschainBase_v1.sol"; /** * @title Inverter Template Payment Processor * diff --git a/test/tests/unit/PP_Crosschain_v1.t.sol b/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.t.sol similarity index 91% rename from test/tests/unit/PP_Crosschain_v1.t.sol rename to test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.t.sol index 77a3e392f..cbd99135a 100644 --- a/test/tests/unit/PP_Crosschain_v1.t.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.t.sol @@ -8,8 +8,11 @@ import { IOrchestrator_v1 } from "test/modules/ModuleTest.sol"; import {OZErrors} from "test/utils/errors/OZErrors.sol"; - -// External +import {ICrossChainBase_v1} from + "src/modules/paymentProcessor/abstract/ICrosschainBase_v1.sol"; +import {CrosschainBase_v1} from + "src/modules/paymentProcessor/abstract/CrosschainBase_v1.sol"; +//External Dependencies import {Clones} from "@oz/proxy/Clones.sol"; //Tests and Mocks @@ -20,13 +23,18 @@ import { ERC20Mock } from "test/utils/mocks/modules/paymentClient/ERC20PaymentClientBaseV1Mock.sol"; //import exposed -import {PP_CrossChain_v1_Exposed} from "./PP_Crosschain_v1_Exposed.sol"; - -// System under Test (SuT) -import { - IPP_Template_v1, - IPaymentProcessor_v1 -} from "src/templates/modules/PP_Template_v1.sol"; +import {CrosschainBase_v1_Exposed} from "./CrosschainBase_v1_Exposed.sol"; + +//System under test (SuT) +// import { +// IPP_CrossChain_v1, +// PP_CrossChain_v1, +// IPaymentProcessor_v1 +// } from "src/templates/modules/PP_Template_v1.sol"; +import {IPaymentProcessor_v1} from + "src/orchestrator/interfaces/IOrchestrator_v1.sol"; +import {ICrossChainBase_v1} from + "src/modules/paymentProcessor/abstract/ICrosschainBase_v1.sol"; /** * @title Inverter Template Payment Processor Tests @@ -48,9 +56,7 @@ import { * * @author Inverter Network */ - - -contract PP_Template_v1_Test is ModuleTest { +contract CrosschainBase_v1_Test is ModuleTest { //-------------------------------------------------------------------------- //Constants uint internal constant _payoutAmountMultiplier = 2; diff --git a/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1_Exposed.sol b/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1_Exposed.sol new file mode 100644 index 000000000..80492924c --- /dev/null +++ b/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1_Exposed.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: LGPL-3.0-only + +// Internal Dependencies +//import {PP_CrossChain_v1} from "src/templates/modules/PP_Template_v1.sol"; +import {CrosschainBase_v1} from + "src/modules/paymentProcessor/abstract/CrosschainBase_v1.sol"; +import {IERC20PaymentClientBase_v1} from + "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; + +contract CrosschainBase_v1_Exposed is CrosschainBase_v1 { + /// @notice Implementation of the bridge transfer logic using EverClear + ///// @inheritdoc CrosschainBase_v1 + function exposed_executeBridgeTransfer( + IERC20PaymentClientBase_v1.PaymentOrder memory order, + bytes memory executionData + ) external payable returns (bytes memory) { + return _executeBridgeTransfer(order, executionData); + } +} diff --git a/test/tests/unit/FM_Template_v1.t.sol b/test/modules/paymentProcessor/bridging/abstracts/FM_Template_v1.t.sol similarity index 100% rename from test/tests/unit/FM_Template_v1.t.sol rename to test/modules/paymentProcessor/bridging/abstracts/FM_Template_v1.t.sol diff --git a/test/tests/unit/FM_Template_v1_Exposed.sol b/test/modules/paymentProcessor/bridging/abstracts/FM_Template_v1_Exposed.sol similarity index 100% rename from test/tests/unit/FM_Template_v1_Exposed.sol rename to test/modules/paymentProcessor/bridging/abstracts/FM_Template_v1_Exposed.sol diff --git a/test/tests/unit/Interfaces/IEverClearSpoke.sol b/test/modules/paymentProcessor/bridging/abstracts/Interfaces/IEverClearSpoke.sol similarity index 100% rename from test/tests/unit/Interfaces/IEverClearSpoke.sol rename to test/modules/paymentProcessor/bridging/abstracts/Interfaces/IEverClearSpoke.sol diff --git a/test/tests/unit/mocks/Mock_EverclearPayment.sol b/test/modules/paymentProcessor/bridging/abstracts/mocks/Mock_EverclearPayment.sol similarity index 100% rename from test/tests/unit/mocks/Mock_EverclearPayment.sol rename to test/modules/paymentProcessor/bridging/abstracts/mocks/Mock_EverclearPayment.sol diff --git a/test/tests/unit/PP_Crosschain_v1_Exposed.sol b/test/tests/unit/PP_Crosschain_v1_Exposed.sol deleted file mode 100644 index 74afd80b5..000000000 --- a/test/tests/unit/PP_Crosschain_v1_Exposed.sol +++ /dev/null @@ -1,30 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only - -pragma solidity ^0.8.0; - -// Internal -import {PP_Template_v1} from "src/templates/modules/PP_Template_v1.sol"; - -// Access Mock of the PP_Template_v1 contract for Testing. -contract PP_Template_v1_Exposed is PP_Template_v1 { - // Use the `exposed_` prefix for functions to expose internal contract for - // testing. - - function exposed_setPayoutAmountMultiplier(uint newPayoutAmountMultiplier_) - external - { - _setPayoutAmountMultiplier(newPayoutAmountMultiplier_); - } - - function exposed_validPaymentReceiver(address receiver_) - external - view - returns (bool validPaymentReceiver_) - { - validPaymentReceiver_ = _validPaymentReceiver(receiver_); - } - - function exposed_ensureValidClient(address client_) external view { - _ensureValidClient(client_); - } -} From 5ff8e1cccdb29a67c77c1132981e6434d5c16919 Mon Sep 17 00:00:00 2001 From: Leandro Faria Date: Sat, 9 Nov 2024 14:58:35 -0500 Subject: [PATCH 16/93] uncomment bridge return value and run fmt --- .../bridging/ConnextBridgeLogic.sol | 2 +- .../bridging/PP_Connext_Crosschain_v1.sol | 4 +- .../abstracts}/CrosschainBase_v1.sol | 4 +- .../abstracts}/FM_Template_v1.sol | 0 .../abstracts}/ICrosschainBase_v1.sol | 0 .../abstracts}/IFM_Template_v1.sol | 0 .../abstracts}/IPP_Template_v1.sol | 0 .../abstracts}/PP_Template_v1.sol | 0 .../bridging/PP_Connext_Bridge.t.sol | 6 +-- .../abstracts/CrosschainBase_v1.t.sol | 50 ++----------------- .../abstracts/CrosschainBase_v1_Exposed.sol | 2 +- .../abstracts/Interfaces/IEverClearSpoke.sol | 38 +++++++------- 12 files changed, 32 insertions(+), 74 deletions(-) rename src/modules/paymentProcessor/{abstract => bridging/abstracts}/CrosschainBase_v1.sol (97%) rename src/modules/paymentProcessor/{abstract => bridging/abstracts}/FM_Template_v1.sol (100%) rename src/modules/paymentProcessor/{abstract => bridging/abstracts}/ICrosschainBase_v1.sol (100%) rename src/modules/paymentProcessor/{abstract => bridging/abstracts}/IFM_Template_v1.sol (100%) rename src/modules/paymentProcessor/{abstract => bridging/abstracts}/IPP_Template_v1.sol (100%) rename src/modules/paymentProcessor/{abstract => bridging/abstracts}/PP_Template_v1.sol (100%) diff --git a/src/modules/paymentProcessor/bridging/ConnextBridgeLogic.sol b/src/modules/paymentProcessor/bridging/ConnextBridgeLogic.sol index 3593ff565..e4e307bfc 100644 --- a/src/modules/paymentProcessor/bridging/ConnextBridgeLogic.sol +++ b/src/modules/paymentProcessor/bridging/ConnextBridgeLogic.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.23; import {CrosschainBase_v1} from - "src/modules/paymentProcessor/abstract/CrosschainBase_v1.sol"; + "src/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.sol"; import {IERC20PaymentClientBase_v1} from "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; import {IERC20} from "@oz/token/ERC20/IERC20.sol"; diff --git a/src/modules/paymentProcessor/bridging/PP_Connext_Crosschain_v1.sol b/src/modules/paymentProcessor/bridging/PP_Connext_Crosschain_v1.sol index 39f04c1a4..e660473cf 100644 --- a/src/modules/paymentProcessor/bridging/PP_Connext_Crosschain_v1.sol +++ b/src/modules/paymentProcessor/bridging/PP_Connext_Crosschain_v1.sol @@ -1,9 +1,9 @@ pragma solidity ^0.8.20; import {CrosschainBase_v1} from - "src/modules/paymentProcessor/abstract/CrosschainBase_v1.sol"; + "src/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.sol"; import {ICrossChainBase_v1} from - "src/modules/paymentProcessor/abstract/ICrosschainBase_v1.sol"; + "src/modules/paymentProcessor/bridging/abstracts/ICrosschainBase_v1.sol"; import {ConnextBridgeLogic} from "./ConnextBridgeLogic.sol"; import {IERC20PaymentClientBase_v1} from "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; diff --git a/src/modules/paymentProcessor/abstract/CrosschainBase_v1.sol b/src/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.sol similarity index 97% rename from src/modules/paymentProcessor/abstract/CrosschainBase_v1.sol rename to src/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.sol index f223f7566..8f59888c2 100644 --- a/src/modules/paymentProcessor/abstract/CrosschainBase_v1.sol +++ b/src/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.sol @@ -77,8 +77,8 @@ contract CrosschainBase_v1 is ICrossChainBase_v1, Module_v1 { IERC20PaymentClientBase_v1.PaymentOrder memory order, bytes memory executionData ) internal virtual returns (bytes memory) { - // return bytes(""); - // emit BridgeTransferExecuted(executionData); + emit BridgeTransferExecuted(executionData); + return bytes(""); } function getChainId() external view returns (uint) { diff --git a/src/modules/paymentProcessor/abstract/FM_Template_v1.sol b/src/modules/paymentProcessor/bridging/abstracts/FM_Template_v1.sol similarity index 100% rename from src/modules/paymentProcessor/abstract/FM_Template_v1.sol rename to src/modules/paymentProcessor/bridging/abstracts/FM_Template_v1.sol diff --git a/src/modules/paymentProcessor/abstract/ICrosschainBase_v1.sol b/src/modules/paymentProcessor/bridging/abstracts/ICrosschainBase_v1.sol similarity index 100% rename from src/modules/paymentProcessor/abstract/ICrosschainBase_v1.sol rename to src/modules/paymentProcessor/bridging/abstracts/ICrosschainBase_v1.sol diff --git a/src/modules/paymentProcessor/abstract/IFM_Template_v1.sol b/src/modules/paymentProcessor/bridging/abstracts/IFM_Template_v1.sol similarity index 100% rename from src/modules/paymentProcessor/abstract/IFM_Template_v1.sol rename to src/modules/paymentProcessor/bridging/abstracts/IFM_Template_v1.sol diff --git a/src/modules/paymentProcessor/abstract/IPP_Template_v1.sol b/src/modules/paymentProcessor/bridging/abstracts/IPP_Template_v1.sol similarity index 100% rename from src/modules/paymentProcessor/abstract/IPP_Template_v1.sol rename to src/modules/paymentProcessor/bridging/abstracts/IPP_Template_v1.sol diff --git a/src/modules/paymentProcessor/abstract/PP_Template_v1.sol b/src/modules/paymentProcessor/bridging/abstracts/PP_Template_v1.sol similarity index 100% rename from src/modules/paymentProcessor/abstract/PP_Template_v1.sol rename to src/modules/paymentProcessor/bridging/abstracts/PP_Template_v1.sol diff --git a/test/modules/paymentProcessor/bridging/PP_Connext_Bridge.t.sol b/test/modules/paymentProcessor/bridging/PP_Connext_Bridge.t.sol index 36ce667c9..04f8781d3 100644 --- a/test/modules/paymentProcessor/bridging/PP_Connext_Bridge.t.sol +++ b/test/modules/paymentProcessor/bridging/PP_Connext_Bridge.t.sol @@ -9,9 +9,9 @@ import { } from "test/modules/ModuleTest.sol"; import {OZErrors} from "test/utils/errors/OZErrors.sol"; import {ICrossChainBase_v1} from - "src/modules/paymentProcessor/abstract/ICrosschainBase_v1.sol"; + "src/modules/paymentProcessor/bridging/abstracts/ICrosschainBase_v1.sol"; import {CrosschainBase_v1} from - "src/modules/paymentProcessor/abstract/CrosschainBase_v1.sol"; + "src/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.sol"; //External Dependencies import {Clones} from "@oz/proxy/Clones.sol"; @@ -36,7 +36,7 @@ import { // "../../../src/orchestrator/interfaces/IOrchestrator_v1.sol"; import {ICrossChainBase_v1} from - "src/modules/paymentProcessor/abstract/ICrosschainBase_v1.sol"; + "src/modules/paymentProcessor/bridging/abstracts/ICrosschainBase_v1.sol"; /** * @title Inverter Template Payment Processor * diff --git a/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.t.sol b/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.t.sol index cbd99135a..06aff11e6 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.t.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.t.sol @@ -9,9 +9,9 @@ import { } from "test/modules/ModuleTest.sol"; import {OZErrors} from "test/utils/errors/OZErrors.sol"; import {ICrossChainBase_v1} from - "src/modules/paymentProcessor/abstract/ICrosschainBase_v1.sol"; + "src/modules/paymentProcessor/bridging/abstracts/ICrosschainBase_v1.sol"; import {CrosschainBase_v1} from - "src/modules/paymentProcessor/abstract/CrosschainBase_v1.sol"; + "src/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.sol"; //External Dependencies import {Clones} from "@oz/proxy/Clones.sol"; @@ -34,7 +34,7 @@ import {CrosschainBase_v1_Exposed} from "./CrosschainBase_v1_Exposed.sol"; import {IPaymentProcessor_v1} from "src/orchestrator/interfaces/IOrchestrator_v1.sol"; import {ICrossChainBase_v1} from - "src/modules/paymentProcessor/abstract/ICrosschainBase_v1.sol"; + "src/modules/paymentProcessor/bridging/abstracts/ICrosschainBase_v1.sol"; /** * @title Inverter Template Payment Processor Tests @@ -116,11 +116,6 @@ contract CrosschainBase_v1_Test is ModuleTest { //Test the interface support function testSupportsInterface() public { - // assertTrue( - // paymentProcessor.supportsInterface( - // type(IPaymentProcessor_v1).interfaceId - // ) - // ); assertTrue( paymentProcessor.supportsInterface( type(ICrossChainBase_v1).interfaceId @@ -148,13 +143,6 @@ contract CrosschainBase_v1_Test is ModuleTest { └── Then it should revert */ // function testProcessPayments_modifierInPlace() public { - // ERC20PaymentClientBaseV1Mock nonRegisteredClient = - // new ERC20PaymentClientBaseV1Mock(); - - // vm.expectRevert( - // ICrossChainBase_v1.Module__CrossChainBase__NotValidClient.selector - // ); - // paymentProcessor.processPayments(nonRegisteredClient); // } // //-------------------------------------------------------------------------- @@ -184,38 +172,6 @@ contract CrosschainBase_v1_Test is ModuleTest { */ //function testInternalSetPayoutAmountMultiplier_FailsGivenZero() public { - // vm.expectRevert( - // IPP_CrossChain_v1.Module__PP_CrossChain__InvalidAmount.selector - // ); - // paymentProcessor.exposed_setPayoutAmountMultiplier(0); - //} - - // function testInternalSetPayoutAmountMultiplier_FailsGivenZeroAfter( - // uint newPayoutAmountMultiplier_ - // ) public { - // //Set up assumption - // vm.assume(newPayoutAmountMultiplier_ > 0); - - // // Check initial state - // assertEq( - // paymentProcessor.getPayoutAmountMultiplier(), - // _payoutAmountMultiplier - // ); - - // // Test internal function through mock exposed function - // vm.expectEmit(true, true, true, true); - // emit IPP_CrossChain_v1.NewPayoutAmountMultiplierSet( - // _payoutAmountMultiplier, newPayoutAmountMultiplier_ - // ); - // paymentProcessor.exposed_setPayoutAmountMultiplier( - // newPayoutAmountMultiplier_ - // ); - - // // Test final state - // assertEq( - // paymentProcessor.getPayoutAmountMultiplier(), - // newPayoutAmountMultiplier_ - // ); // } //Test the internal _validPaymentReceiver() function diff --git a/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1_Exposed.sol b/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1_Exposed.sol index 80492924c..6958a62c5 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1_Exposed.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1_Exposed.sol @@ -3,7 +3,7 @@ // Internal Dependencies //import {PP_CrossChain_v1} from "src/templates/modules/PP_Template_v1.sol"; import {CrosschainBase_v1} from - "src/modules/paymentProcessor/abstract/CrosschainBase_v1.sol"; + "src/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.sol"; import {IERC20PaymentClientBase_v1} from "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; diff --git a/test/modules/paymentProcessor/bridging/abstracts/Interfaces/IEverClearSpoke.sol b/test/modules/paymentProcessor/bridging/abstracts/Interfaces/IEverClearSpoke.sol index 6897d45cc..716829a8c 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/Interfaces/IEverClearSpoke.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/Interfaces/IEverClearSpoke.sol @@ -14,11 +14,11 @@ interface IEverclearSpoke { bytes32 receiver; bytes32 inputAsset; bytes32 outputAsset; - uint256 amount; + uint amount; uint24 maxFee; uint32 origin; uint32[] destinations; - uint256 nonce; + uint nonce; uint48 timestamp; uint48 ttl; bytes data; @@ -33,8 +33,8 @@ interface IEverclearSpoke { } struct Permit2Params { - uint256 nonce; - uint256 deadline; + uint nonce; + uint deadline; bytes signature; } @@ -60,7 +60,7 @@ interface IEverclearSpoke { address to, address inputAsset, address outputAsset, - uint256 amount, + uint amount, uint24 maxFee, uint48 ttl, bytes calldata data @@ -70,17 +70,19 @@ interface IEverclearSpoke { address to, address inputAsset, address outputAsset, - uint256 amount, + uint amount, uint24 maxFee, uint48 ttl, bytes calldata data, Permit2Params calldata permit2Params ) external returns (bytes32 intentId, Intent memory intent); - function fillIntent(Intent calldata intent, uint24 fee) external returns (FillMessage memory fillMessage); + function fillIntent(Intent calldata intent, uint24 fee) + external + returns (FillMessage memory fillMessage); function fillIntentForSolver( address solver, Intent calldata intent, - uint256 nonce, + uint nonce, uint24 fee, bytes calldata signature ) external returns (FillMessage memory fillMessage); @@ -90,25 +92,25 @@ interface IEverclearSpoke { uint32 domain, Intent[] calldata intents, address relayer, - uint256 ttl, - uint256 nonce, - uint256 bufferBPS, + uint ttl, + uint nonce, + uint bufferBPS, bytes calldata signature ) external; function processFillQueueViaRelayer( uint32 domain, uint32 amount, address relayer, - uint256 ttl, - uint256 nonce, - uint256 bufferBPS, + uint ttl, + uint nonce, + uint bufferBPS, bytes calldata signature ) external; - function deposit(address asset, uint256 amount) external; - function withdraw(address asset, uint256 amount) external; + function deposit(address asset, uint amount) external; + function withdraw(address asset, uint amount) external; function updateGateway(address newGateway) external; function updateMessageReceiver(address newMessageReceiver) external; function authorizeGasReceiver(address receiver, bool authorized) external; - function updateMessageGasLimit(uint256 newGasLimit) external; + function updateMessageGasLimit(uint newGasLimit) external; function executeIntentCalldata(Intent calldata intent) external; -} \ No newline at end of file +} From dfdb8280058c604de7b46a70063e5f37523c2732 Mon Sep 17 00:00:00 2001 From: Leandro Faria Date: Mon, 11 Nov 2024 22:09:43 -0500 Subject: [PATCH 17/93] change contract to abstract --- .../paymentProcessor/bridging/PP_Connext_Crosschain_v1.sol | 1 - .../bridging/abstracts/CrosschainBase_v1.sol | 6 +----- .../bridging/abstracts/CrosschainBase_v1.t.sol | 2 +- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/modules/paymentProcessor/bridging/PP_Connext_Crosschain_v1.sol b/src/modules/paymentProcessor/bridging/PP_Connext_Crosschain_v1.sol index e660473cf..fb8dd0520 100644 --- a/src/modules/paymentProcessor/bridging/PP_Connext_Crosschain_v1.sol +++ b/src/modules/paymentProcessor/bridging/PP_Connext_Crosschain_v1.sol @@ -41,7 +41,6 @@ contract PP_Connext_Crosschain_v1 is CrosschainBase_v1 { for (uint i = 0; i < orders.length; i++) { bytes memory bridgeData = _executeBridgeTransfer(orders[i], executionData); - _bridgeData[_paymentId] = bridgeData; emit PaymentProcessed( _paymentId, diff --git a/src/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.sol b/src/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.sol index 8f59888c2..a694281f7 100644 --- a/src/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.sol +++ b/src/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.sol @@ -30,7 +30,7 @@ import {ICrossChainBase_v1} from "./ICrosschainBase_v1.sol"; * @author Inverter Network */ -contract CrosschainBase_v1 is ICrossChainBase_v1, Module_v1 { +abstract contract CrosschainBase_v1 is ICrossChainBase_v1, Module_v1 { /// @inheritdoc ERC165Upgradeable function supportsInterface(bytes4 interfaceId_) public @@ -45,9 +45,6 @@ contract CrosschainBase_v1 is ICrossChainBase_v1, Module_v1 { //-------------------------------------------------------------------------- // State - /// @dev Mapping of payment ID to bridge transfer return data - mapping(uint => bytes) internal _bridgeData; - bytes public executionData; /// @dev Payout amount multiplier. @@ -77,7 +74,6 @@ contract CrosschainBase_v1 is ICrossChainBase_v1, Module_v1 { IERC20PaymentClientBase_v1.PaymentOrder memory order, bytes memory executionData ) internal virtual returns (bytes memory) { - emit BridgeTransferExecuted(executionData); return bytes(""); } diff --git a/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.t.sol b/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.t.sol index 06aff11e6..dea4a932b 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.t.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.t.sol @@ -76,7 +76,7 @@ contract CrosschainBase_v1_Test is ModuleTest { function setUp() public { //This function is used to setup the unit test //Deploy the SuT - address impl = address(new CrosschainBase_v1()); + address impl = address(new CrosschainBase_v1_Exposed()); paymentProcessor = CrosschainBase_v1(Clones.clone(impl)); //Setup the module to test From 0fa97c50eab79b0364440691215bad7c0cf7da1d Mon Sep 17 00:00:00 2001 From: Leandro Faria Date: Tue, 12 Nov 2024 21:22:02 -0500 Subject: [PATCH 18/93] add logic to PP_Connext_Crosschain --- .../bridging/ConnextBridgeLogic.sol | 67 ------------------ .../bridging/PP_Connext_Crosschain_v1.sol | 69 +++++++++++++++++-- .../bridging/interfaces/IPP_BR_Connext_v1.sol | 4 -- .../bridging/PP_Connext_Bridge.t.sol | 10 +-- .../abstracts/CrosschainBase_v1.t.sol | 63 ++--------------- 5 files changed, 77 insertions(+), 136 deletions(-) delete mode 100644 src/modules/paymentProcessor/bridging/ConnextBridgeLogic.sol delete mode 100644 src/modules/paymentProcessor/bridging/interfaces/IPP_BR_Connext_v1.sol diff --git a/src/modules/paymentProcessor/bridging/ConnextBridgeLogic.sol b/src/modules/paymentProcessor/bridging/ConnextBridgeLogic.sol deleted file mode 100644 index e4e307bfc..000000000 --- a/src/modules/paymentProcessor/bridging/ConnextBridgeLogic.sol +++ /dev/null @@ -1,67 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity 0.8.23; - -import {CrosschainBase_v1} from - "src/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.sol"; -import {IERC20PaymentClientBase_v1} from - "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; -import {IERC20} from "@oz/token/ERC20/IERC20.sol"; - -interface IEverclearSpoke { - function newIntent( - uint32[] memory destinations, - address to, - address inputAsset, - address outputAsset, - uint amount, - uint24 maxFee, - uint48 ttl, - bytes memory data - ) external returns (bytes32 intentId, uint amountOut); -} - -contract ConnextBridgeLogic is CrosschainBase_v1 { - IEverclearSpoke public everClearSpoke; - address public immutable weth; - - constructor(address _everclearSpoke, address _weth) CrosschainBase_v1() { - everClearSpoke = IEverclearSpoke(_everclearSpoke); - weth = _weth; - } - - function xcall( - IERC20PaymentClientBase_v1.PaymentOrder memory order, - bytes memory executionData - ) external payable returns (bytes32 intentId) { - // Decode any additional parameters from executionData - (uint maxFee, uint ttl) = abi.decode(executionData, (uint, uint)); - - // Wrap ETH into WETH to send with the xcall - IERC20(order.paymentToken).transferFrom( - msg.sender, address(this), order.amount - ); - - // This contract approves transfer to EverClearSpoke - IERC20(order.paymentToken).approve( - address(everClearSpoke), order.amount - ); - - // Create destinations array with the target chain - uint32[] memory destinations = new uint32[](1); - destinations[0] = 8453; // @note -> hardcode for now -> order.destinationChainId; - - // Call newIntent on the EverClearSpoke contract - (bytes32 intentId,) = everClearSpoke.newIntent( - destinations, - order.recipient, // to - order.paymentToken, // inputAsset - address(weth), // outputAsset (assuming same asset on destination) - order.amount, // amount - uint24(maxFee), // maxFee (cast to uint24) - uint48(ttl), // ttl (cast to uint48) - "" // empty data field, modify if needed - ); - - return intentId; - } -} diff --git a/src/modules/paymentProcessor/bridging/PP_Connext_Crosschain_v1.sol b/src/modules/paymentProcessor/bridging/PP_Connext_Crosschain_v1.sol index fb8dd0520..f8e7981ed 100644 --- a/src/modules/paymentProcessor/bridging/PP_Connext_Crosschain_v1.sol +++ b/src/modules/paymentProcessor/bridging/PP_Connext_Crosschain_v1.sol @@ -7,14 +7,39 @@ import {ICrossChainBase_v1} from import {ConnextBridgeLogic} from "./ConnextBridgeLogic.sol"; import {IERC20PaymentClientBase_v1} from "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +interface IEverclearSpoke { + function newIntent( + uint32[] memory destinations, + address to, + address inputAsset, + address outputAsset, + uint amount, + uint24 maxFee, + uint48 ttl, + bytes memory data + ) external returns (bytes32 intentId, uint amountOut); +} + +interface IWETH { + function deposit() external payable; + function withdraw(uint amount) external; +} contract PP_Connext_Crosschain_v1 is CrosschainBase_v1 { ConnextBridgeLogic public connextBridgeLogic; + IEverclearSpoke public everClearSpoke; + IWETH public weth; - constructor(uint chainId_, address connextBridgeLogic_) - CrosschainBase_v1() - { + constructor( + uint chainId_, + address connextBridgeLogic_, + address everClearSpoke_, + address weth_ + ) CrosschainBase_v1() { connextBridgeLogic = ConnextBridgeLogic(connextBridgeLogic_); + everClearSpoke = IEverclearSpoke(everClearSpoke_); } /// @notice Execute the cross-chain bridge transfer @@ -26,7 +51,7 @@ contract PP_Connext_Crosschain_v1 is CrosschainBase_v1 { bytes memory executionData ) internal override returns (bytes memory) { //@notice call the connextBridgeLogic to execute the bridge transfer - bytes32 intentId = connextBridgeLogic.xcall(order, executionData); + bytes32 intentId = xcall(order, executionData); return abi.encode(intentId); } @@ -54,4 +79,40 @@ contract PP_Connext_Crosschain_v1 is CrosschainBase_v1 { client.amountPaid(orders[i].paymentToken, orders[i].amount); } } + + function xcall( + IERC20PaymentClientBase_v1.PaymentOrder memory order, + bytes memory executionData + ) internal returns (bytes32 intentId) { + // Decode any additional parameters from executionData + (uint maxFee, uint ttl) = abi.decode(executionData, (uint, uint)); + + // Wrap ETH into WETH to send with the xcall + IERC20(order.paymentToken).transferFrom( + msg.sender, address(this), order.amount + ); + + // This contract approves transfer to EverClearSpoke + IERC20(order.paymentToken).approve( + address(everClearSpoke), order.amount + ); + + // Create destinations array with the target chain + uint32[] memory destinations = new uint32[](1); + destinations[0] = 8453; // @note -> hardcode for now -> order.destinationChainId; + + // Call newIntent on the EverClearSpoke contract + (bytes32 intentId,) = everClearSpoke.newIntent( + destinations, + order.recipient, // to + order.paymentToken, // inputAsset + address(weth), // outputAsset (assuming same asset on destination) + order.amount, // amount + uint24(maxFee), // maxFee (cast to uint24) + uint48(ttl), // ttl (cast to uint48) + "" // empty data field, modify if needed + ); + + return intentId; + } } diff --git a/src/modules/paymentProcessor/bridging/interfaces/IPP_BR_Connext_v1.sol b/src/modules/paymentProcessor/bridging/interfaces/IPP_BR_Connext_v1.sol deleted file mode 100644 index 1c7908317..000000000 --- a/src/modules/paymentProcessor/bridging/interfaces/IPP_BR_Connext_v1.sol +++ /dev/null @@ -1,4 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity ^0.8.0; - -interface IPP_BR_Connext_v1 {} diff --git a/test/modules/paymentProcessor/bridging/PP_Connext_Bridge.t.sol b/test/modules/paymentProcessor/bridging/PP_Connext_Bridge.t.sol index 04f8781d3..420d7cda3 100644 --- a/test/modules/paymentProcessor/bridging/PP_Connext_Bridge.t.sol +++ b/test/modules/paymentProcessor/bridging/PP_Connext_Bridge.t.sol @@ -59,13 +59,13 @@ import {ICrossChainBase_v1} from contract PP_Connext_Bridge_Test is ModuleTest { //-------------------------------------------------------------------------- - // //Constants + //Constants - // //-------------------------------------------------------------------------- - // //State + //-------------------------------------------------------------------------- + //State - // //Mocks - // ERC20PaymentClientBaseV1Mock paymentClient; + //-------------------------------------------------------------------------- + //Mocks //-------------------------------------------------------------------------- //Setup diff --git a/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.t.sol b/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.t.sol index dea4a932b..94c34af55 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.t.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.t.sol @@ -59,8 +59,6 @@ import {ICrossChainBase_v1} from contract CrosschainBase_v1_Test is ModuleTest { //-------------------------------------------------------------------------- //Constants - uint internal constant _payoutAmountMultiplier = 2; - //-------------------------------------------------------------------------- //State @@ -73,69 +71,22 @@ contract CrosschainBase_v1_Test is ModuleTest { //-------------------------------------------------------------------------- //Setup - function setUp() public { - //This function is used to setup the unit test - //Deploy the SuT - address impl = address(new CrosschainBase_v1_Exposed()); - paymentProcessor = CrosschainBase_v1(Clones.clone(impl)); - - //Setup the module to test - _setUpOrchestrator(paymentProcessor); - - //General setup for other contracts in the workflow - _authorizer.setIsAuthorized(address(this), true); - - //Initiate the PP with the medata and config data - paymentProcessor.init( - _orchestrator, _METADATA, abi.encode(_payoutAmountMultiplier) - ); - - //Setup other modules needed in the unit tests. - //In this case a payment client is needed to test the PP_Template_v1. - impl = address(new ERC20PaymentClientBaseV1Mock()); - paymentClient = ERC20PaymentClientBaseV1Mock(Clones.clone(impl)); - //Adding the payment client is done through a timelock mechanism - _orchestrator.initiateAddModuleWithTimelock(address(paymentClient)); - vm.warp(block.timestamp + _orchestrator.MODULE_UPDATE_TIMELOCK()); - _orchestrator.executeAddModule(address(paymentClient)); - //Init payment client - paymentClient.init(_orchestrator, _METADATA, bytes("")); - paymentClient.setIsAuthorized(address(paymentProcessor), true); - paymentClient.setToken(_token); - } + function setUp() public {} //-------------------------------------------------------------------------- //Test: Initialization //Test if the orchestrator is correctly set - function testInit() public override(ModuleTest) { - assertEq( - address(paymentProcessor.orchestrator()), address(_orchestrator) - ); - } + function testInit() public override(ModuleTest) {} //Test the interface support - function testSupportsInterface() public { - assertTrue( - paymentProcessor.supportsInterface( - type(ICrossChainBase_v1).interfaceId - ) - ); - } + function testSupportsInterface() public {} //Test the reinit function - function testReinitFails() public override(ModuleTest) { - vm.expectRevert(OZErrors.Initializable__InvalidInitialization); - paymentProcessor.init( - _orchestrator, _METADATA, abi.encode(_payoutAmountMultiplier) - ); - } - - vm.expectRevert( - IPP_Template_v1.Module__PP_Template__ClientNotValid.selector - ); - paymentProcessor.processPayments(nonRegisteredClient); - } + function testReinitFails() public override(ModuleTest) {} + // -----ALL below this we're keeping for reference, but not testing + //-------------------------------------------------------------------------- + //Test: Modifiers /* Test validClient modifier in place (extensive testing done through internal modifier functions) └── Given the modifier is in place From c06d6ec1e17a2aa87c085d2e50ab870b6e7cce59 Mon Sep 17 00:00:00 2001 From: Leandro Faria Date: Thu, 14 Nov 2024 00:28:22 -0500 Subject: [PATCH 19/93] fix inheritance chain --- .../bridging/IPP_Connext_Crosschain.sol | 4 +- .../bridging/PP_Connext_Crosschain_v1.sol | 8 +- .../bridging/abstracts/CrosschainBase_v1.sol | 4 +- .../bridging/abstracts/IPP_Crosschain_v1.sol | 34 ++++ .../bridging/abstracts/PP_Crosschain_v1.sol | 186 ++++++++++++++++++ 5 files changed, 226 insertions(+), 10 deletions(-) create mode 100644 src/modules/paymentProcessor/bridging/abstracts/IPP_Crosschain_v1.sol create mode 100644 src/modules/paymentProcessor/bridging/abstracts/PP_Crosschain_v1.sol diff --git a/src/modules/paymentProcessor/bridging/IPP_Connext_Crosschain.sol b/src/modules/paymentProcessor/bridging/IPP_Connext_Crosschain.sol index a52630cc0..dd034d85a 100644 --- a/src/modules/paymentProcessor/bridging/IPP_Connext_Crosschain.sol +++ b/src/modules/paymentProcessor/bridging/IPP_Connext_Crosschain.sol @@ -10,6 +10,4 @@ import {IPaymentProcessor_v1} from // External Interfaces import {IERC20} from "@oz/token/ERC20/IERC20.sol"; -interface IPP_Connext_Crosschain is IPaymentProcessor_v1 { -///Need to add the functions that are specific to Connext -} +interface IPP_Connext_Crosschain is IPaymentProcessor_v1 {} diff --git a/src/modules/paymentProcessor/bridging/PP_Connext_Crosschain_v1.sol b/src/modules/paymentProcessor/bridging/PP_Connext_Crosschain_v1.sol index f8e7981ed..0b87be541 100644 --- a/src/modules/paymentProcessor/bridging/PP_Connext_Crosschain_v1.sol +++ b/src/modules/paymentProcessor/bridging/PP_Connext_Crosschain_v1.sol @@ -4,10 +4,11 @@ import {CrosschainBase_v1} from "src/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.sol"; import {ICrossChainBase_v1} from "src/modules/paymentProcessor/bridging/abstracts/ICrosschainBase_v1.sol"; -import {ConnextBridgeLogic} from "./ConnextBridgeLogic.sol"; import {IERC20PaymentClientBase_v1} from "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {PP_Crosschain_v1} from + "src/modules/paymentProcessor/bridging/abstracts/PP_Crosschain_v1.sol"; interface IEverclearSpoke { function newIntent( @@ -27,8 +28,7 @@ interface IWETH { function withdraw(uint amount) external; } -contract PP_Connext_Crosschain_v1 is CrosschainBase_v1 { - ConnextBridgeLogic public connextBridgeLogic; +contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { IEverclearSpoke public everClearSpoke; IWETH public weth; @@ -38,8 +38,8 @@ contract PP_Connext_Crosschain_v1 is CrosschainBase_v1 { address everClearSpoke_, address weth_ ) CrosschainBase_v1() { - connextBridgeLogic = ConnextBridgeLogic(connextBridgeLogic_); everClearSpoke = IEverclearSpoke(everClearSpoke_); + weth = IWETH(weth_); } /// @notice Execute the cross-chain bridge transfer diff --git a/src/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.sol b/src/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.sol index a694281f7..48deb8e06 100644 --- a/src/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.sol +++ b/src/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.sol @@ -45,8 +45,6 @@ abstract contract CrosschainBase_v1 is ICrossChainBase_v1, Module_v1 { //-------------------------------------------------------------------------- // State - bytes public executionData; - /// @dev Payout amount multiplier. uint internal _payoutAmountMultiplier; @@ -77,7 +75,7 @@ abstract contract CrosschainBase_v1 is ICrossChainBase_v1, Module_v1 { return bytes(""); } - function getChainId() external view returns (uint) { + function getChainId() external view virtual returns (uint) { return _chainId; } diff --git a/src/modules/paymentProcessor/bridging/abstracts/IPP_Crosschain_v1.sol b/src/modules/paymentProcessor/bridging/abstracts/IPP_Crosschain_v1.sol new file mode 100644 index 000000000..6f9b5e207 --- /dev/null +++ b/src/modules/paymentProcessor/bridging/abstracts/IPP_Crosschain_v1.sol @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity ^0.8.0; + +// Internal Interfaces +import {IERC20PaymentClientBase_v1} from + "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; +import {IPaymentProcessor_v1} from + "src/modules/paymentProcessor/IPaymentProcessor_v1.sol"; + +// External Interfaces +import {IERC20} from "@oz/token/ERC20/IERC20.sol"; + +interface IPP_Crosschain_v1 is IPaymentProcessor_v1 { + /// @notice Thrown when the cross-chain message fails to be delivered + /// @param sourceChain The chain ID where the message originated + /// @param destinationChain The chain ID where the message was meant to be delivered + /// @param messageId The unique identifier of the failed message + error Module__PP_Crosschain__MessageDeliveryFailed( + uint sourceChain, uint destinationChain, bytes32 messageId + ); + + /// @notice Thrown when attempting to process a cross-chain payment with invalid parameters + /// @param paymentClient The address of the payment client + /// @param paymentId The ID of the payment being processed + /// @param destinationChain The target chain ID for the payment + error Module__PP_Crosschain__InvalidPaymentParameters( + address paymentClient, uint paymentId, uint destinationChain + ); + + /// @notice Thrown when the cross-chain bridge fees exceed the maximum allowed + /// @param actualFee The actual fee required + /// @param maxFee The maximum fee allowed + error Module__PP_Crosschain__BridgeFeeTooHigh(uint actualFee, uint maxFee); +} diff --git a/src/modules/paymentProcessor/bridging/abstracts/PP_Crosschain_v1.sol b/src/modules/paymentProcessor/bridging/abstracts/PP_Crosschain_v1.sol new file mode 100644 index 000000000..4df4773bf --- /dev/null +++ b/src/modules/paymentProcessor/bridging/abstracts/PP_Crosschain_v1.sol @@ -0,0 +1,186 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity 0.8.23; + +import {IOrchestrator_v1} from + "src/orchestrator/interfaces/IOrchestrator_v1.sol"; +import {IPaymentProcessor_v1} from "@pp/IPaymentProcessor_v1.sol"; +import {IERC20PaymentClientBase_v1} from + "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; +import {ERC165Upgradeable, Module_v1} from "src/modules/base/Module_v1.sol"; + +import {CrosschainBase_v1} from "./CrosschainBase_v1.sol"; + +// External Interfaces +import {IERC20} from "@oz/token/ERC20/IERC20.sol"; + +// External Dependencies +import {ERC20} from "@oz/token/ERC20/ERC20.sol"; + +// External Libraries +import {SafeERC20} from "@oz/token/ERC20/utils/SafeERC20.sol"; + +// Internal Interfaces +import {IPP_Crosschain_v1} from "./IPP_Crosschain_v1.sol"; + +/** + * @title Inverter Template Payment Processor + * + * @notice Basic template payment processor used as base for developing new payment processors. + * + * @dev This contract is used to showcase a basic setup for a payment processor. The contract showcases the + * following: + * - Inherit from the Module_v1 contract to enable interaction with the Inverter workflow. + * - Use of the IPaymentProcessor_v1 interface to facilitate interaction with a payment client. + * - Implement custom interface which has all the public facing functions, errors, events and structs. + * - Pre-defined layout for all contract functions, modifiers, state variables etc. + * - Use of the ERC165Upgradeable contract to check for interface support. + * + * @custom:security-contact security@inverter.network + * In case of any concerns or findings, please refer to our Security Policy + * at security.inverter.network or email us directly! + * + * @author Inverter Network + */ +abstract contract PP_Crosschain_v1 is CrosschainBase_v1, IPP_Crosschain_v1 { + /// @inheritdoc ERC165Upgradeable + function supportsInterface(bytes4 interfaceId_) + public + view + virtual + override(CrosschainBase_v1) + returns (bool) + { + return interfaceId_ == type(IPP_Crosschain_v1).interfaceId + || super.supportsInterface(interfaceId_); + } + + //-------------------------------------------------------------------------- + // Modifiers + + /// @dev Checks that the caller is an active module. + modifier onlyModule() { + if (!orchestrator().isModule(_msgSender())) { + revert Module__PaymentProcessor__OnlyCallableByModule(); + } + _; + } + + /// @dev Checks that the client is calling for itself. + modifier validClient(address client) { + if (_msgSender() != client) { + revert Module__PaymentProcessor__CannotCallOnOtherClientsOrders(); + } + _; + } + + //-------------------------------------------------------------------------- + // Storage + + bytes public executionData; + + /// @dev Gap for possible future upgrades. + uint[50] private __gap; + + //-------------------------------------------------------------------------- + // Virtual Functions + + function getChainId() external view override returns (uint) { + return _chainId; + } + + /// @notice Process payments for a given payment client + /// @param client The payment client to process payments for + function processPayments(IERC20PaymentClientBase_v1 client) + external + virtual + override(CrosschainBase_v1, IPaymentProcessor_v1) + {} + + /// @inheritdoc IPaymentProcessor_v1 + function cancelRunningPayments(IERC20PaymentClientBase_v1 client) + external + onlyModule + validClient(address(client)) + { + // Implementation depends on specific bridge requirements + revert("Not implemented"); + } + + /// @inheritdoc IPaymentProcessor_v1 + function claimPreviouslyUnclaimable( + address client, + address token, + address receiver + ) external { + // Implementation depends on specific bridge requirements + revert("Not implemented"); + } + + /// @inheritdoc IPaymentProcessor_v1 + function unclaimable(address client, address token, address paymentReceiver) + public + view + returns (uint amount) + { + // Implementation depends on specific bridge requirements + return 0; + } + + /// @inheritdoc IPaymentProcessor_v1 + function validPaymentOrder( + IERC20PaymentClientBase_v1.PaymentOrder memory order + ) external returns (bool) { + return _validPaymentReceiver(order.recipient) + && _validTotal(order.amount) + && _validTimes(order.start, order.cliff, order.end) + && _validPaymentToken(order.paymentToken); + } + + /// @dev Validate address input. + /// @param addr Address to validate. + /// @return True if address is valid. + function _validPaymentReceiver(address addr) internal view returns (bool) { + return !( + addr == address(0) || addr == _msgSender() || addr == address(this) + || addr == address(orchestrator()) + || addr == address(orchestrator().fundingManager().token()) + ); + } + + /// @dev Validate uint total amount input. + /// @param _total uint to validate. + /// @return True if uint is valid. + function _validTotal(uint _total) internal pure returns (bool) { + return !(_total == 0); + } + + /// @dev Validate uint start input. + /// @param _start uint to validate. + /// @param _cliff uint to validate. + /// @param _end uint to validate. + /// @return True if uint is valid. + function _validTimes(uint _start, uint _cliff, uint _end) + internal + pure + returns (bool) + { + // _start + _cliff should be less or equal to _end + // this already implies that _start is not greater than _end + return _start + _cliff <= _end; + } + + /// @dev Validate payment token input. + /// @param _token Address of the token to validate. + /// @return True if address is valid. + function _validPaymentToken(address _token) internal returns (bool) { + // Only a basic sanity check that the address supports the balanceOf() function. The corresponding + // module should ensure it's sending an ERC20. + + (bool success, bytes memory data) = _token.call( + abi.encodeWithSelector( + IERC20(_token).balanceOf.selector, address(this) + ) + ); + return success && data.length >= 32; + } +} From 802f3b578d2403662d906d5606a62719eaebd108 Mon Sep 17 00:00:00 2001 From: Leandro Faria Date: Thu, 14 Nov 2024 20:44:05 -0500 Subject: [PATCH 20/93] remove function impl --- .../bridging/abstracts/CrosschainBase_v1.sol | 11 +---------- .../bridging/abstracts/ICrosschainBase_v1.sol | 4 ---- .../bridging/abstracts/PP_Crosschain_v1.sol | 4 ---- 3 files changed, 1 insertion(+), 18 deletions(-) diff --git a/src/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.sol b/src/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.sol index 48deb8e06..504baa366 100644 --- a/src/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.sol +++ b/src/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.sol @@ -51,9 +51,6 @@ abstract contract CrosschainBase_v1 is ICrossChainBase_v1, Module_v1 { /// @dev The number of payment orders. uint internal _paymentId; - /// @dev The chain id of the current chain. - uint internal _chainId; - //-------------------------------------------------------------------------- // Events @@ -71,13 +68,7 @@ abstract contract CrosschainBase_v1 is ICrossChainBase_v1, Module_v1 { function _executeBridgeTransfer( IERC20PaymentClientBase_v1.PaymentOrder memory order, bytes memory executionData - ) internal virtual returns (bytes memory) { - return bytes(""); - } - - function getChainId() external view virtual returns (uint) { - return _chainId; - } + ) internal virtual returns (bytes memory) {} /// @notice Process payments for a given payment client /// @param client The payment client to process payments for diff --git a/src/modules/paymentProcessor/bridging/abstracts/ICrosschainBase_v1.sol b/src/modules/paymentProcessor/bridging/abstracts/ICrosschainBase_v1.sol index edc9f68ab..2cc8ad517 100644 --- a/src/modules/paymentProcessor/bridging/abstracts/ICrosschainBase_v1.sol +++ b/src/modules/paymentProcessor/bridging/abstracts/ICrosschainBase_v1.sol @@ -46,8 +46,4 @@ interface ICrossChainBase_v1 { //-------------------------------------------------------------------------- // Public (Getter) - - /// @notice Get the chain ID - /// @return The chain ID - function getChainId() external view returns (uint); } diff --git a/src/modules/paymentProcessor/bridging/abstracts/PP_Crosschain_v1.sol b/src/modules/paymentProcessor/bridging/abstracts/PP_Crosschain_v1.sol index 4df4773bf..0abaf113d 100644 --- a/src/modules/paymentProcessor/bridging/abstracts/PP_Crosschain_v1.sol +++ b/src/modules/paymentProcessor/bridging/abstracts/PP_Crosschain_v1.sol @@ -84,10 +84,6 @@ abstract contract PP_Crosschain_v1 is CrosschainBase_v1, IPP_Crosschain_v1 { //-------------------------------------------------------------------------- // Virtual Functions - function getChainId() external view override returns (uint) { - return _chainId; - } - /// @notice Process payments for a given payment client /// @param client The payment client to process payments for function processPayments(IERC20PaymentClientBase_v1 client) From e0608ea1e604abe2d04cadb86b67c504ff35132d Mon Sep 17 00:00:00 2001 From: Leandro Faria Date: Thu, 14 Nov 2024 21:41:21 -0500 Subject: [PATCH 21/93] seperate interfaces --- .../abstracts/CrosschainBase_v1.sol | 23 +++ .../abstracts/FM_Template_v1.sol | 0 .../abstracts/IFM_Template_v1.sol | 0 .../abstracts/IPP_Template_v1.sol | 0 .../abstracts/PP_Template_v1.sol | 0 .../bridging/IPP_Connext_Crosschain.sol | 13 -- .../bridging/PP_Connext_Crosschain_v1.sol | 118 ------------ .../bridging/abstracts/CrosschainBase_v1.sol | 79 -------- .../bridging/abstracts/ICrosschainBase_v1.sol | 49 ----- .../bridging/abstracts/IPP_Crosschain_v1.sol | 34 ---- .../bridging/abstracts/PP_Crosschain_v1.sol | 182 ------------------ .../bridging/PP_Connext_Bridge.t.sol | 6 +- .../abstracts/CrosschainBase_v1.t.sol | 6 +- .../abstracts/CrosschainBase_v1_Exposed.sol | 2 +- 14 files changed, 30 insertions(+), 482 deletions(-) rename src/modules/paymentProcessor/{bridging => }/abstracts/FM_Template_v1.sol (100%) rename src/modules/paymentProcessor/{bridging => }/abstracts/IFM_Template_v1.sol (100%) rename src/modules/paymentProcessor/{bridging => }/abstracts/IPP_Template_v1.sol (100%) rename src/modules/paymentProcessor/{bridging => }/abstracts/PP_Template_v1.sol (100%) delete mode 100644 src/modules/paymentProcessor/bridging/IPP_Connext_Crosschain.sol delete mode 100644 src/modules/paymentProcessor/bridging/PP_Connext_Crosschain_v1.sol delete mode 100644 src/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.sol delete mode 100644 src/modules/paymentProcessor/bridging/abstracts/ICrosschainBase_v1.sol delete mode 100644 src/modules/paymentProcessor/bridging/abstracts/IPP_Crosschain_v1.sol delete mode 100644 src/modules/paymentProcessor/bridging/abstracts/PP_Crosschain_v1.sol diff --git a/src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol b/src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol index c67dfc512..68301081b 100644 --- a/src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol +++ b/src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol @@ -45,9 +45,28 @@ abstract contract CrosschainBase_v1 is ICrossChainBase_v1, Module_v1 { //-------------------------------------------------------------------------- // State +<<<<<<< HEAD /// @dev The number of payment orders. uint internal _paymentId; +======= + /// @dev Payout amount multiplier. + uint internal _payoutAmountMultiplier; + + /// @dev The number of payment orders. + uint internal _paymentId; + + //-------------------------------------------------------------------------- + // Events + + event PaymentProcessed( + uint indexed paymentId, address recipient, address token, uint amount + ); + + //-------------------------------------------------------------------------- + // Virtual Functions + +>>>>>>> 0cb0ee97 (seperate interfaces) /// @notice Execute the cross-chain bridge transfer /// @dev Override this function to implement specific bridge logic /// @param order The payment order containing all necessary transfer details @@ -55,5 +74,9 @@ abstract contract CrosschainBase_v1 is ICrossChainBase_v1, Module_v1 { function _executeBridgeTransfer( IERC20PaymentClientBase_v1.PaymentOrder memory order, bytes memory executionData +<<<<<<< HEAD ) internal virtual returns (bytes memory); +======= + ) internal virtual returns (bytes memory) {} +>>>>>>> 0cb0ee97 (seperate interfaces) } diff --git a/src/modules/paymentProcessor/bridging/abstracts/FM_Template_v1.sol b/src/modules/paymentProcessor/abstracts/FM_Template_v1.sol similarity index 100% rename from src/modules/paymentProcessor/bridging/abstracts/FM_Template_v1.sol rename to src/modules/paymentProcessor/abstracts/FM_Template_v1.sol diff --git a/src/modules/paymentProcessor/bridging/abstracts/IFM_Template_v1.sol b/src/modules/paymentProcessor/abstracts/IFM_Template_v1.sol similarity index 100% rename from src/modules/paymentProcessor/bridging/abstracts/IFM_Template_v1.sol rename to src/modules/paymentProcessor/abstracts/IFM_Template_v1.sol diff --git a/src/modules/paymentProcessor/bridging/abstracts/IPP_Template_v1.sol b/src/modules/paymentProcessor/abstracts/IPP_Template_v1.sol similarity index 100% rename from src/modules/paymentProcessor/bridging/abstracts/IPP_Template_v1.sol rename to src/modules/paymentProcessor/abstracts/IPP_Template_v1.sol diff --git a/src/modules/paymentProcessor/bridging/abstracts/PP_Template_v1.sol b/src/modules/paymentProcessor/abstracts/PP_Template_v1.sol similarity index 100% rename from src/modules/paymentProcessor/bridging/abstracts/PP_Template_v1.sol rename to src/modules/paymentProcessor/abstracts/PP_Template_v1.sol diff --git a/src/modules/paymentProcessor/bridging/IPP_Connext_Crosschain.sol b/src/modules/paymentProcessor/bridging/IPP_Connext_Crosschain.sol deleted file mode 100644 index dd034d85a..000000000 --- a/src/modules/paymentProcessor/bridging/IPP_Connext_Crosschain.sol +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity ^0.8.0; - -// Internal Interfaces -import {IERC20PaymentClientBase_v1} from - "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; -import {IPaymentProcessor_v1} from - "src/modules/paymentProcessor/IPaymentProcessor_v1.sol"; - -// External Interfaces -import {IERC20} from "@oz/token/ERC20/IERC20.sol"; - -interface IPP_Connext_Crosschain is IPaymentProcessor_v1 {} diff --git a/src/modules/paymentProcessor/bridging/PP_Connext_Crosschain_v1.sol b/src/modules/paymentProcessor/bridging/PP_Connext_Crosschain_v1.sol deleted file mode 100644 index 0b87be541..000000000 --- a/src/modules/paymentProcessor/bridging/PP_Connext_Crosschain_v1.sol +++ /dev/null @@ -1,118 +0,0 @@ -pragma solidity ^0.8.20; - -import {CrosschainBase_v1} from - "src/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.sol"; -import {ICrossChainBase_v1} from - "src/modules/paymentProcessor/bridging/abstracts/ICrosschainBase_v1.sol"; -import {IERC20PaymentClientBase_v1} from - "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; -import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import {PP_Crosschain_v1} from - "src/modules/paymentProcessor/bridging/abstracts/PP_Crosschain_v1.sol"; - -interface IEverclearSpoke { - function newIntent( - uint32[] memory destinations, - address to, - address inputAsset, - address outputAsset, - uint amount, - uint24 maxFee, - uint48 ttl, - bytes memory data - ) external returns (bytes32 intentId, uint amountOut); -} - -interface IWETH { - function deposit() external payable; - function withdraw(uint amount) external; -} - -contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { - IEverclearSpoke public everClearSpoke; - IWETH public weth; - - constructor( - uint chainId_, - address connextBridgeLogic_, - address everClearSpoke_, - address weth_ - ) CrosschainBase_v1() { - everClearSpoke = IEverclearSpoke(everClearSpoke_); - weth = IWETH(weth_); - } - - /// @notice Execute the cross-chain bridge transfer - /// @dev Override this function to implement specific bridge logic - /// @param order The payment order containing all necessary transfer details - /// @return bridgeData Arbitrary data returned by the bridge implementation - function _executeBridgeTransfer( - IERC20PaymentClientBase_v1.PaymentOrder memory order, - bytes memory executionData - ) internal override returns (bytes memory) { - //@notice call the connextBridgeLogic to execute the bridge transfer - bytes32 intentId = xcall(order, executionData); - return abi.encode(intentId); - } - - function processPayments(IERC20PaymentClientBase_v1 client) - external - override - { - // Collect orders from the client - IERC20PaymentClientBase_v1.PaymentOrder[] memory orders; - (orders,,) = client.collectPaymentOrders(); - - for (uint i = 0; i < orders.length; i++) { - bytes memory bridgeData = - _executeBridgeTransfer(orders[i], executionData); - - emit PaymentProcessed( - _paymentId, - orders[i].recipient, - orders[i].paymentToken, - orders[i].amount - ); - _paymentId++; - - // Inform the client about the processed amount - client.amountPaid(orders[i].paymentToken, orders[i].amount); - } - } - - function xcall( - IERC20PaymentClientBase_v1.PaymentOrder memory order, - bytes memory executionData - ) internal returns (bytes32 intentId) { - // Decode any additional parameters from executionData - (uint maxFee, uint ttl) = abi.decode(executionData, (uint, uint)); - - // Wrap ETH into WETH to send with the xcall - IERC20(order.paymentToken).transferFrom( - msg.sender, address(this), order.amount - ); - - // This contract approves transfer to EverClearSpoke - IERC20(order.paymentToken).approve( - address(everClearSpoke), order.amount - ); - - // Create destinations array with the target chain - uint32[] memory destinations = new uint32[](1); - destinations[0] = 8453; // @note -> hardcode for now -> order.destinationChainId; - - // Call newIntent on the EverClearSpoke contract - (bytes32 intentId,) = everClearSpoke.newIntent( - destinations, - order.recipient, // to - order.paymentToken, // inputAsset - address(weth), // outputAsset (assuming same asset on destination) - order.amount, // amount - uint24(maxFee), // maxFee (cast to uint24) - uint48(ttl), // ttl (cast to uint48) - "" // empty data field, modify if needed - ); - - return intentId; - } -} diff --git a/src/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.sol b/src/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.sol deleted file mode 100644 index 504baa366..000000000 --- a/src/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.sol +++ /dev/null @@ -1,79 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity 0.8.23; - -import {IOrchestrator_v1} from - "src/orchestrator/interfaces/IOrchestrator_v1.sol"; -import {IPaymentProcessor_v1} from "@pp/IPaymentProcessor_v1.sol"; -import {IERC20PaymentClientBase_v1} from - "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; -import {Module_v1} from "src/modules/base/Module_v1.sol"; -//import {IPP_CrossChain_v1} from "./IPP_Template_v1.sol"; -import {ERC165Upgradeable, Module_v1} from "src/modules/base/Module_v1.sol"; -import {ICrossChainBase_v1} from "./ICrosschainBase_v1.sol"; -/** - * @title Inverter Template Payment Processor - * - * @notice Basic template payment processor used as base for developing new payment processors. - * - * @dev This contract is used to showcase a basic setup for a payment processor. The contract showcases the - * following: - * - Inherit from the Module_v1 contract to enable interaction with the Inverter workflow. - * - Use of the IPaymentProcessor_v1 interface to facilitate interaction with a payment client. - * - Implement custom interface which has all the public facing functions, errors, events and structs. - * - Pre-defined layout for all contract functions, modifiers, state variables etc. - * - Use of the ERC165Upgradeable contract to check for interface support. - * - * @custom:security-contact security@inverter.network - * In case of any concerns or findings, please refer to our Security Policy - * at security.inverter.network or email us directly! - * - * @author Inverter Network - */ - -abstract contract CrosschainBase_v1 is ICrossChainBase_v1, Module_v1 { - /// @inheritdoc ERC165Upgradeable - function supportsInterface(bytes4 interfaceId_) - public - view - virtual - override(Module_v1) - returns (bool) - { - return interfaceId_ == type(ICrossChainBase_v1).interfaceId - || super.supportsInterface(interfaceId_); - } - //-------------------------------------------------------------------------- - // State - - /// @dev Payout amount multiplier. - uint internal _payoutAmountMultiplier; - - /// @dev The number of payment orders. - uint internal _paymentId; - - //-------------------------------------------------------------------------- - // Events - - event PaymentProcessed( - uint indexed paymentId, address recipient, address token, uint amount - ); - - //-------------------------------------------------------------------------- - // Virtual Functions - - /// @notice Execute the cross-chain bridge transfer - /// @dev Override this function to implement specific bridge logic - /// @param order The payment order containing all necessary transfer details - /// @return bridgeData Arbitrary data returned by the bridge implementation - function _executeBridgeTransfer( - IERC20PaymentClientBase_v1.PaymentOrder memory order, - bytes memory executionData - ) internal virtual returns (bytes memory) {} - - /// @notice Process payments for a given payment client - /// @param client The payment client to process payments for - function processPayments(IERC20PaymentClientBase_v1 client) - external - virtual - {} -} diff --git a/src/modules/paymentProcessor/bridging/abstracts/ICrosschainBase_v1.sol b/src/modules/paymentProcessor/bridging/abstracts/ICrosschainBase_v1.sol deleted file mode 100644 index 2cc8ad517..000000000 --- a/src/modules/paymentProcessor/bridging/abstracts/ICrosschainBase_v1.sol +++ /dev/null @@ -1,49 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity ^0.8.0; - -// Internal Dependencies - -// External Dependencies -import {IERC20PaymentClientBase_v1} from - "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; - -interface ICrossChainBase_v1 { - //-------------------------------------------------------------------------- - // Structs - - /// @notice Struct to hold cross-chain message data - struct CrossChainMessage { - uint messageId; - address sourceChain; - address targetChain; - bytes payload; - bool executed; - } - - //-------------------------------------------------------------------------- - // Events - - /// @notice Emitted when a bridge transfer is executed - event BridgeTransferExecuted(bytes indexed bridgeData); - - //-------------------------------------------------------------------------- - // Errors - - /// @notice Amount can not be zero. - error Module__CrossChainBase__InvalidAmount(); - - /// @notice Client is not valid. - error Module__CrossChainBase__NotValidClient(); - - /// @notice Message has already been executed - error Module__CrossChainBase_MessageAlreadyExecuted(); - - /// @notice Invalid chain ID provided - error Module__CrossChainBase_InvalidChainId(); - - /// @notice Message verification failed - error Module__CrossChainBase_MessageVerificationFailed(); - - //-------------------------------------------------------------------------- - // Public (Getter) -} diff --git a/src/modules/paymentProcessor/bridging/abstracts/IPP_Crosschain_v1.sol b/src/modules/paymentProcessor/bridging/abstracts/IPP_Crosschain_v1.sol deleted file mode 100644 index 6f9b5e207..000000000 --- a/src/modules/paymentProcessor/bridging/abstracts/IPP_Crosschain_v1.sol +++ /dev/null @@ -1,34 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity ^0.8.0; - -// Internal Interfaces -import {IERC20PaymentClientBase_v1} from - "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; -import {IPaymentProcessor_v1} from - "src/modules/paymentProcessor/IPaymentProcessor_v1.sol"; - -// External Interfaces -import {IERC20} from "@oz/token/ERC20/IERC20.sol"; - -interface IPP_Crosschain_v1 is IPaymentProcessor_v1 { - /// @notice Thrown when the cross-chain message fails to be delivered - /// @param sourceChain The chain ID where the message originated - /// @param destinationChain The chain ID where the message was meant to be delivered - /// @param messageId The unique identifier of the failed message - error Module__PP_Crosschain__MessageDeliveryFailed( - uint sourceChain, uint destinationChain, bytes32 messageId - ); - - /// @notice Thrown when attempting to process a cross-chain payment with invalid parameters - /// @param paymentClient The address of the payment client - /// @param paymentId The ID of the payment being processed - /// @param destinationChain The target chain ID for the payment - error Module__PP_Crosschain__InvalidPaymentParameters( - address paymentClient, uint paymentId, uint destinationChain - ); - - /// @notice Thrown when the cross-chain bridge fees exceed the maximum allowed - /// @param actualFee The actual fee required - /// @param maxFee The maximum fee allowed - error Module__PP_Crosschain__BridgeFeeTooHigh(uint actualFee, uint maxFee); -} diff --git a/src/modules/paymentProcessor/bridging/abstracts/PP_Crosschain_v1.sol b/src/modules/paymentProcessor/bridging/abstracts/PP_Crosschain_v1.sol deleted file mode 100644 index 0abaf113d..000000000 --- a/src/modules/paymentProcessor/bridging/abstracts/PP_Crosschain_v1.sol +++ /dev/null @@ -1,182 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity 0.8.23; - -import {IOrchestrator_v1} from - "src/orchestrator/interfaces/IOrchestrator_v1.sol"; -import {IPaymentProcessor_v1} from "@pp/IPaymentProcessor_v1.sol"; -import {IERC20PaymentClientBase_v1} from - "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; -import {ERC165Upgradeable, Module_v1} from "src/modules/base/Module_v1.sol"; - -import {CrosschainBase_v1} from "./CrosschainBase_v1.sol"; - -// External Interfaces -import {IERC20} from "@oz/token/ERC20/IERC20.sol"; - -// External Dependencies -import {ERC20} from "@oz/token/ERC20/ERC20.sol"; - -// External Libraries -import {SafeERC20} from "@oz/token/ERC20/utils/SafeERC20.sol"; - -// Internal Interfaces -import {IPP_Crosschain_v1} from "./IPP_Crosschain_v1.sol"; - -/** - * @title Inverter Template Payment Processor - * - * @notice Basic template payment processor used as base for developing new payment processors. - * - * @dev This contract is used to showcase a basic setup for a payment processor. The contract showcases the - * following: - * - Inherit from the Module_v1 contract to enable interaction with the Inverter workflow. - * - Use of the IPaymentProcessor_v1 interface to facilitate interaction with a payment client. - * - Implement custom interface which has all the public facing functions, errors, events and structs. - * - Pre-defined layout for all contract functions, modifiers, state variables etc. - * - Use of the ERC165Upgradeable contract to check for interface support. - * - * @custom:security-contact security@inverter.network - * In case of any concerns or findings, please refer to our Security Policy - * at security.inverter.network or email us directly! - * - * @author Inverter Network - */ -abstract contract PP_Crosschain_v1 is CrosschainBase_v1, IPP_Crosschain_v1 { - /// @inheritdoc ERC165Upgradeable - function supportsInterface(bytes4 interfaceId_) - public - view - virtual - override(CrosschainBase_v1) - returns (bool) - { - return interfaceId_ == type(IPP_Crosschain_v1).interfaceId - || super.supportsInterface(interfaceId_); - } - - //-------------------------------------------------------------------------- - // Modifiers - - /// @dev Checks that the caller is an active module. - modifier onlyModule() { - if (!orchestrator().isModule(_msgSender())) { - revert Module__PaymentProcessor__OnlyCallableByModule(); - } - _; - } - - /// @dev Checks that the client is calling for itself. - modifier validClient(address client) { - if (_msgSender() != client) { - revert Module__PaymentProcessor__CannotCallOnOtherClientsOrders(); - } - _; - } - - //-------------------------------------------------------------------------- - // Storage - - bytes public executionData; - - /// @dev Gap for possible future upgrades. - uint[50] private __gap; - - //-------------------------------------------------------------------------- - // Virtual Functions - - /// @notice Process payments for a given payment client - /// @param client The payment client to process payments for - function processPayments(IERC20PaymentClientBase_v1 client) - external - virtual - override(CrosschainBase_v1, IPaymentProcessor_v1) - {} - - /// @inheritdoc IPaymentProcessor_v1 - function cancelRunningPayments(IERC20PaymentClientBase_v1 client) - external - onlyModule - validClient(address(client)) - { - // Implementation depends on specific bridge requirements - revert("Not implemented"); - } - - /// @inheritdoc IPaymentProcessor_v1 - function claimPreviouslyUnclaimable( - address client, - address token, - address receiver - ) external { - // Implementation depends on specific bridge requirements - revert("Not implemented"); - } - - /// @inheritdoc IPaymentProcessor_v1 - function unclaimable(address client, address token, address paymentReceiver) - public - view - returns (uint amount) - { - // Implementation depends on specific bridge requirements - return 0; - } - - /// @inheritdoc IPaymentProcessor_v1 - function validPaymentOrder( - IERC20PaymentClientBase_v1.PaymentOrder memory order - ) external returns (bool) { - return _validPaymentReceiver(order.recipient) - && _validTotal(order.amount) - && _validTimes(order.start, order.cliff, order.end) - && _validPaymentToken(order.paymentToken); - } - - /// @dev Validate address input. - /// @param addr Address to validate. - /// @return True if address is valid. - function _validPaymentReceiver(address addr) internal view returns (bool) { - return !( - addr == address(0) || addr == _msgSender() || addr == address(this) - || addr == address(orchestrator()) - || addr == address(orchestrator().fundingManager().token()) - ); - } - - /// @dev Validate uint total amount input. - /// @param _total uint to validate. - /// @return True if uint is valid. - function _validTotal(uint _total) internal pure returns (bool) { - return !(_total == 0); - } - - /// @dev Validate uint start input. - /// @param _start uint to validate. - /// @param _cliff uint to validate. - /// @param _end uint to validate. - /// @return True if uint is valid. - function _validTimes(uint _start, uint _cliff, uint _end) - internal - pure - returns (bool) - { - // _start + _cliff should be less or equal to _end - // this already implies that _start is not greater than _end - return _start + _cliff <= _end; - } - - /// @dev Validate payment token input. - /// @param _token Address of the token to validate. - /// @return True if address is valid. - function _validPaymentToken(address _token) internal returns (bool) { - // Only a basic sanity check that the address supports the balanceOf() function. The corresponding - // module should ensure it's sending an ERC20. - - (bool success, bytes memory data) = _token.call( - abi.encodeWithSelector( - IERC20(_token).balanceOf.selector, address(this) - ) - ); - return success && data.length >= 32; - } -} diff --git a/test/modules/paymentProcessor/bridging/PP_Connext_Bridge.t.sol b/test/modules/paymentProcessor/bridging/PP_Connext_Bridge.t.sol index 420d7cda3..213c18c6e 100644 --- a/test/modules/paymentProcessor/bridging/PP_Connext_Bridge.t.sol +++ b/test/modules/paymentProcessor/bridging/PP_Connext_Bridge.t.sol @@ -9,9 +9,9 @@ import { } from "test/modules/ModuleTest.sol"; import {OZErrors} from "test/utils/errors/OZErrors.sol"; import {ICrossChainBase_v1} from - "src/modules/paymentProcessor/bridging/abstracts/ICrosschainBase_v1.sol"; + "src/modules/paymentProcessor/interfaces/ICrosschainBase_v1.sol"; import {CrosschainBase_v1} from - "src/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.sol"; + "src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol"; //External Dependencies import {Clones} from "@oz/proxy/Clones.sol"; @@ -36,7 +36,7 @@ import { // "../../../src/orchestrator/interfaces/IOrchestrator_v1.sol"; import {ICrossChainBase_v1} from - "src/modules/paymentProcessor/bridging/abstracts/ICrosschainBase_v1.sol"; + "src/modules/paymentProcessor/interfaces/ICrosschainBase_v1.sol"; /** * @title Inverter Template Payment Processor * diff --git a/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.t.sol b/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.t.sol index 94c34af55..55efda6c8 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.t.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.t.sol @@ -9,9 +9,9 @@ import { } from "test/modules/ModuleTest.sol"; import {OZErrors} from "test/utils/errors/OZErrors.sol"; import {ICrossChainBase_v1} from - "src/modules/paymentProcessor/bridging/abstracts/ICrosschainBase_v1.sol"; + "src/modules/paymentProcessor/interfaces/ICrosschainBase_v1.sol"; import {CrosschainBase_v1} from - "src/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.sol"; + "src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol"; //External Dependencies import {Clones} from "@oz/proxy/Clones.sol"; @@ -34,7 +34,7 @@ import {CrosschainBase_v1_Exposed} from "./CrosschainBase_v1_Exposed.sol"; import {IPaymentProcessor_v1} from "src/orchestrator/interfaces/IOrchestrator_v1.sol"; import {ICrossChainBase_v1} from - "src/modules/paymentProcessor/bridging/abstracts/ICrosschainBase_v1.sol"; + "src/modules/paymentProcessor/interfaces/ICrosschainBase_v1.sol"; /** * @title Inverter Template Payment Processor Tests diff --git a/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1_Exposed.sol b/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1_Exposed.sol index 6958a62c5..6ec9df1d3 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1_Exposed.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1_Exposed.sol @@ -3,7 +3,7 @@ // Internal Dependencies //import {PP_CrossChain_v1} from "src/templates/modules/PP_Template_v1.sol"; import {CrosschainBase_v1} from - "src/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.sol"; + "src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol"; import {IERC20PaymentClientBase_v1} from "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; From 6909a385f7c5da072b3f56803e7ab0b84e16d272 Mon Sep 17 00:00:00 2001 From: Leandro Faria Date: Mon, 18 Nov 2024 14:56:14 -0500 Subject: [PATCH 22/93] remove unecessary state variables --- .../paymentProcessor/abstracts/CrosschainBase_v1.sol | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol b/src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol index 68301081b..54a3ada8d 100644 --- a/src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol +++ b/src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol @@ -45,11 +45,6 @@ abstract contract CrosschainBase_v1 is ICrossChainBase_v1, Module_v1 { //-------------------------------------------------------------------------- // State -<<<<<<< HEAD - /// @dev The number of payment orders. - uint internal _paymentId; - -======= /// @dev Payout amount multiplier. uint internal _payoutAmountMultiplier; @@ -66,7 +61,6 @@ abstract contract CrosschainBase_v1 is ICrossChainBase_v1, Module_v1 { //-------------------------------------------------------------------------- // Virtual Functions ->>>>>>> 0cb0ee97 (seperate interfaces) /// @notice Execute the cross-chain bridge transfer /// @dev Override this function to implement specific bridge logic /// @param order The payment order containing all necessary transfer details @@ -74,9 +68,5 @@ abstract contract CrosschainBase_v1 is ICrossChainBase_v1, Module_v1 { function _executeBridgeTransfer( IERC20PaymentClientBase_v1.PaymentOrder memory order, bytes memory executionData -<<<<<<< HEAD - ) internal virtual returns (bytes memory); -======= ) internal virtual returns (bytes memory) {} ->>>>>>> 0cb0ee97 (seperate interfaces) } From 54f1eea73eb4fc3b95bcf9f193a77f0df8c1eb00 Mon Sep 17 00:00:00 2001 From: Leandro Faria Date: Mon, 18 Nov 2024 14:56:14 -0500 Subject: [PATCH 23/93] remove unecessary state variables --- src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol b/src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol index 54a3ada8d..6024323ba 100644 --- a/src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol +++ b/src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol @@ -45,9 +45,6 @@ abstract contract CrosschainBase_v1 is ICrossChainBase_v1, Module_v1 { //-------------------------------------------------------------------------- // State - /// @dev Payout amount multiplier. - uint internal _payoutAmountMultiplier; - /// @dev The number of payment orders. uint internal _paymentId; From 09047da169b1f618b7b31226e5902335affe21b2 Mon Sep 17 00:00:00 2001 From: Zuhaib Mohammed Date: Fri, 8 Nov 2024 06:24:43 -0800 Subject: [PATCH 24/93] add unit tests for Connext --- .../PP_Connext_Crosschain_v1_Test.t.sol | 120 ++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol diff --git a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol new file mode 100644 index 000000000..35772051b --- /dev/null +++ b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.20; + +// External Dependencies +import {Test} from "forge-std/Test.sol"; +import {Clones} from "@oz/proxy/Clones.sol"; + +// Internal Dependencies +import {PP_Connext_Crosschain_v1} from + "src/modules/paymentProcessor/bridging/PP_Connext_Crosschain_v1.sol"; +import {ConnextBridgeLogic} from + "src/modules/paymentProcessor/bridging/ConnextBridgeLogic.sol"; +import {CrosschainBase_v1} from "src/templates/modules/CrosschainBase_v1.sol"; +import {IERC20PaymentClientBase_v1} from + "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; + +// Tests and Mocks +import {Mock_EverclearPayment} from + "src/templates/tests/unit/mocks/Mock_EverclearPayment.sol"; +import { + IERC20PaymentClientBase_v1, + ERC20PaymentClientBaseV1Mock, + ERC20Mock +} from "test/utils/mocks/modules/paymentClient/ERC20PaymentClientBaseV1Mock.sol"; +import { + ModuleTest, + IModule_v1, + IOrchestrator_v1 +} from "test/modules/ModuleTest.sol"; +import {OZErrors} from "test/utils/errors/OZErrors.sol"; + +contract PP_Connext_Crosschain_v1_Test is ModuleTest { + PP_Connext_Crosschain_v1 public processor; + ConnextBridgeLogic public bridgeLogic; + Mock_EverclearPayment public everclearPaymentMock; + ERC20Mock public token; + ERC20PaymentClientBaseV1Mock paymentClient; + CrosschainBase_v1 public paymentProcessor; + + // Test addresses + address public recipient = address(0x123); + uint public constant CHAIN_ID = 1; + + function setUp() public { + // Deploy token + token = new ERC20Mock("Test Token", "TEST"); + + // Deploy and setup mock payment client + everclearPaymentMock = new Mock_EverclearPayment(); + + address impl = address(new CrosschainBase_v1()); + paymentProcessor = CrosschainBase_v1(Clones.clone(impl)); + + //Setup the module to test + _setUpOrchestrator(paymentProcessor); + + //General setup for other contracts in the workflow + _authorizer.setIsAuthorized(address(this), true); + + //Initiate the PP with the medata and config data + paymentProcessor.init(_orchestrator, _METADATA, abi.encode(1)); + + //Setup other modules needed in the unit tests. + //In this case a payment client is needed to test the PP_Template_v1. + impl = address(new ERC20PaymentClientBaseV1Mock()); + paymentClient = ERC20PaymentClientBaseV1Mock(Clones.clone(impl)); + //Adding the payment client is done through a timelock mechanism + _orchestrator.initiateAddModuleWithTimelock(address(paymentClient)); + vm.warp(block.timestamp + _orchestrator.MODULE_UPDATE_TIMELOCK()); + _orchestrator.executeAddModule(address(paymentClient)); + //Init payment client + paymentClient.init(_orchestrator, _METADATA, bytes("")); + paymentClient.setIsAuthorized(address(paymentProcessor), true); + + paymentClient.setToken(_token); + + // Deploy bridge logic and processor + bridgeLogic = new ConnextBridgeLogic( + address(everclearPaymentMock), address(token) + ); + processor = new PP_Connext_Crosschain_v1(CHAIN_ID, address(bridgeLogic)); + + // Setup token approvals and initial balances + token.mint(address(this), 1000 ether); + token.approve(address(processor), type(uint).max); + + // Setup mock payment orders that will be returned by the mock + IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = + new IERC20PaymentClientBase_v1.PaymentOrder[](1); + orders[0] = IERC20PaymentClientBase_v1.PaymentOrder({ + recipient: recipient, + paymentToken: address(token), + amount: 100 ether, + start: block.timestamp, + cliff: 0, + end: block.timestamp + 1 days + }); + paymentClient.addPaymentOrders(orders); + } + + function testInit() public override(ModuleTest) { + assertEq( + address(paymentProcessor.orchestrator()), address(_orchestrator) + ); + } + + function testReinitFails() public override(ModuleTest) { + vm.expectRevert(OZErrors.Initializable__InvalidInitialization); + paymentProcessor.init(_orchestrator, _METADATA, abi.encode(1)); + } + + function test_ProcessPayments() public { + // Get the client interface + IERC20PaymentClientBase_v1 client = + IERC20PaymentClientBase_v1(address(paymentClient)); + + // Process payments + processor.processPayments(client); + } +} From d8f88b9648b141ac2d6ad20bec5391c494399b3c Mon Sep 17 00:00:00 2001 From: Zuhaib Mohammed Date: Fri, 8 Nov 2024 07:11:29 -0800 Subject: [PATCH 25/93] unit test processPayments & xcall fix merge conflicts fix file structure fix merge conflicts finish rebasing --- .../bridging/ConnextBridgeLogic.sol | 67 +++++++++++++++++++ .../bridging/PP_Connext_Crosschain_v1.sol | 63 +++++++++++++++++ .../bridging/PP_Connext_Bridge.t.sol | 62 +++++++++++++++-- .../PP_Connext_Crosschain_v1_Test.t.sol | 7 ++ 4 files changed, 194 insertions(+), 5 deletions(-) create mode 100644 src/modules/paymentProcessor/bridging/ConnextBridgeLogic.sol create mode 100644 src/modules/paymentProcessor/bridging/PP_Connext_Crosschain_v1.sol diff --git a/src/modules/paymentProcessor/bridging/ConnextBridgeLogic.sol b/src/modules/paymentProcessor/bridging/ConnextBridgeLogic.sol new file mode 100644 index 000000000..765bc0ddf --- /dev/null +++ b/src/modules/paymentProcessor/bridging/ConnextBridgeLogic.sol @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity 0.8.23; + +import {CrosschainBase_v1} from + "src/modules/paymentProcessor/abstract/CrosschainBase_v1.sol"; +import {IERC20PaymentClientBase_v1} from + "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; +import {IERC20} from "@oz/token/ERC20/IERC20.sol"; + +interface IEverclearSpoke { + function newIntent( + uint32[] memory destinations, + address to, + address inputAsset, + address outputAsset, + uint amount, + uint24 maxFee, + uint48 ttl, + bytes memory data + ) external returns (bytes32 intentId, uint amountOut); +} + +contract ConnextBridgeLogic { + IEverclearSpoke public everClearSpoke; + address public immutable weth; + + constructor(address _everclearSpoke, address _weth) { + everClearSpoke = IEverclearSpoke(_everclearSpoke); + weth = _weth; + } + + function xcall( + IERC20PaymentClientBase_v1.PaymentOrder memory order, + bytes memory executionData + ) external payable returns (bytes32 intentId) { + // Decode any additional parameters from executionData + (uint maxFee, uint ttl) = abi.decode(executionData, (uint, uint)); + + // Wrap ETH into WETH to send with the xcall + IERC20(order.paymentToken).transferFrom( + msg.sender, address(this), order.amount + ); + + // This contract approves transfer to EverClearSpoke + IERC20(order.paymentToken).approve( + address(everClearSpoke), order.amount + ); + + // Create destinations array with the target chain + uint32[] memory destinations = new uint32[](1); + destinations[0] = 8453; // @note -> hardcode for now -> order.destinationChainId; + + // Call newIntent on the EverClearSpoke contract + (bytes32 intentId,) = everClearSpoke.newIntent( + destinations, + order.recipient, // to + order.paymentToken, // inputAsset + address(weth), // outputAsset (assuming same asset on destination) + order.amount, // amount + uint24(maxFee), // maxFee (cast to uint24) + uint48(ttl), // ttl (cast to uint48) + "" // empty data field, modify if needed + ); + + return intentId; + } +} diff --git a/src/modules/paymentProcessor/bridging/PP_Connext_Crosschain_v1.sol b/src/modules/paymentProcessor/bridging/PP_Connext_Crosschain_v1.sol new file mode 100644 index 000000000..cd0fd2df8 --- /dev/null +++ b/src/modules/paymentProcessor/bridging/PP_Connext_Crosschain_v1.sol @@ -0,0 +1,63 @@ +pragma solidity ^0.8.20; + +import {CrosschainBase_v1} from + "src/modules/paymentProcessor/abstract/CrosschainBase_v1.sol"; +import {ICrossChainBase_v1} from + "src/modules/paymentProcessor/abstract/ICrosschainBase_v1.sol"; +import {ConnextBridgeLogic} from "./ConnextBridgeLogic.sol"; +import {IERC20PaymentClientBase_v1} from + "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; + +contract PP_Connext_Crosschain_v1 is CrosschainBase_v1 { + ConnextBridgeLogic public connextBridgeLogic; + + constructor(uint chainId_, address connextBridgeLogic_) + CrosschainBase_v1() + { + connextBridgeLogic = ConnextBridgeLogic(connextBridgeLogic_); + } + + /// @notice Execute the cross-chain bridge transfer + /// @dev Override this function to implement specific bridge logic + /// @param order The payment order containing all necessary transfer details + /// @return bridgeData Arbitrary data returned by the bridge implementation + function _executeBridgeTransfer( + IERC20PaymentClientBase_v1.PaymentOrder memory order, + bytes memory executionData + ) internal override returns (bytes memory) { + //@notice call the connextBridgeLogic to execute the bridge transfer + bytes32 intentId = connextBridgeLogic.xcall(order, executionData); + return abi.encode(intentId); + } + + function processPayments(IERC20PaymentClientBase_v1 client) + external + override + { + // To encode maxFee and ttl: + uint maxFee = 0; + uint ttl = 0; + bytes memory executionData = abi.encode(maxFee, ttl); + + // Collect orders from the client + IERC20PaymentClientBase_v1.PaymentOrder[] memory orders; + (orders,,) = client.collectPaymentOrders(); + + for (uint i = 0; i < orders.length; i++) { + bytes memory bridgeData = + _executeBridgeTransfer(orders[i], executionData); + _bridgeData[_paymentId] = bridgeData; + + emit PaymentProcessed( + _paymentId, + orders[i].recipient, + orders[i].paymentToken, + orders[i].amount + ); + _paymentId++; + + // Inform the client about the processed amount + client.amountPaid(orders[i].paymentToken, orders[i].amount); + } + } +} diff --git a/test/modules/paymentProcessor/bridging/PP_Connext_Bridge.t.sol b/test/modules/paymentProcessor/bridging/PP_Connext_Bridge.t.sol index 213c18c6e..05e3aac9f 100644 --- a/test/modules/paymentProcessor/bridging/PP_Connext_Bridge.t.sol +++ b/test/modules/paymentProcessor/bridging/PP_Connext_Bridge.t.sol @@ -8,10 +8,13 @@ import { IOrchestrator_v1 } from "test/modules/ModuleTest.sol"; import {OZErrors} from "test/utils/errors/OZErrors.sol"; + import {ICrossChainBase_v1} from "src/modules/paymentProcessor/interfaces/ICrosschainBase_v1.sol"; import {CrosschainBase_v1} from "src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol"; + + //External Dependencies import {Clones} from "@oz/proxy/Clones.sol"; @@ -23,6 +26,7 @@ import { ERC20Mock } from "test/utils/mocks/modules/paymentClient/ERC20PaymentClientBaseV1Mock.sol"; + // //import exposed // import {PP_CrossChain_v1_Exposed} from // "../../tests/unit/PP_CrossChain_v1_Exposed.sol"; @@ -37,6 +41,9 @@ import { import {ICrossChainBase_v1} from "src/modules/paymentProcessor/interfaces/ICrosschainBase_v1.sol"; +======= + "src/modules/paymentProcessor/abstract/ICrosschainBase_v1.sol"; +>>>>>>> 4aec33f (fix file structure) /** * @title Inverter Template Payment Processor * @@ -69,17 +76,62 @@ contract PP_Connext_Bridge_Test is ModuleTest { //-------------------------------------------------------------------------- //Setup - function setUp() public {} + function setUp() public { + //This function is used to setup the unit test + //Deploy the SuT + address impl = address(new CrosschainBase_v1()); + paymentProcessor = CrosschainBase_v1(Clones.clone(impl)); + + //Setup the module to test + _setUpOrchestrator(paymentProcessor); + + //General setup for other contracts in the workflow + _authorizer.setIsAuthorized(address(this), true); + + //Initiate the PP with the medata and config data + paymentProcessor.init( + _orchestrator, _METADATA, abi.encode(_payoutAmountMultiplier) + ); + + //Setup other modules needed in the unit tests. + //In this case a payment client is needed to test the PP_Template_v1. + impl = address(new ERC20PaymentClientBaseV1Mock()); + paymentClient = ERC20PaymentClientBaseV1Mock(Clones.clone(impl)); + //Adding the payment client is done through a timelock mechanism + _orchestrator.initiateAddModuleWithTimelock(address(paymentClient)); + vm.warp(block.timestamp + _orchestrator.MODULE_UPDATE_TIMELOCK()); + _orchestrator.executeAddModule(address(paymentClient)); + //Init payment client + paymentClient.init(_orchestrator, _METADATA, bytes("")); + paymentClient.setIsAuthorized(address(paymentProcessor), true); + paymentClient.setToken(_token); + } //-------------------------------------------------------------------------- //Test: Initialization - //Test if the orchestrator is correctly set - function testInit() public override(ModuleTest) {} + + function testInit() public override(ModuleTest) { + assertEq( + address(paymentProcessor.orchestrator()), address(_orchestrator) + ); + } //Test the interface support - function testSupportsInterface() public {} + function testSupportsInterface() public { + assertTrue( + paymentProcessor.supportsInterface( + type(ICrossChainBase_v1).interfaceId + ) + ); + } //Test the reinit function - function testReinitFails() public override(ModuleTest) {} + function testReinitFails() public override(ModuleTest) { + vm.expectRevert(OZErrors.Initializable__InvalidInitialization); + paymentProcessor.init( + _orchestrator, _METADATA, abi.encode(_payoutAmountMultiplier) + ); + } + } diff --git a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol index 35772051b..73465608d 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol @@ -79,10 +79,17 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { address(everclearPaymentMock), address(token) ); processor = new PP_Connext_Crosschain_v1(CHAIN_ID, address(bridgeLogic)); + paymentClient.setIsAuthorized(address(processor), true); // Setup token approvals and initial balances token.mint(address(this), 1000 ether); token.approve(address(processor), type(uint).max); + token.approve(address(bridgeLogic), type(uint).max); + + // Add these lines to ensure proper token flow + token.mint(address(processor), 1000 ether); // Mint tokens to processor + vm.prank(address(processor)); + token.approve(address(bridgeLogic), type(uint).max); // Processor approves bridge logic // Setup mock payment orders that will be returned by the mock IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = From 46df46ad306c819d03fa80eb66f3ecc10b72ef55 Mon Sep 17 00:00:00 2001 From: Leandro Faria Date: Mon, 11 Nov 2024 22:04:05 -0500 Subject: [PATCH 26/93] fix directory structure --- .../bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol index 73465608d..e9220b9ca 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol @@ -10,13 +10,14 @@ import {PP_Connext_Crosschain_v1} from "src/modules/paymentProcessor/bridging/PP_Connext_Crosschain_v1.sol"; import {ConnextBridgeLogic} from "src/modules/paymentProcessor/bridging/ConnextBridgeLogic.sol"; -import {CrosschainBase_v1} from "src/templates/modules/CrosschainBase_v1.sol"; +import {CrosschainBase_v1} from + "src/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.sol"; import {IERC20PaymentClientBase_v1} from "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; // Tests and Mocks import {Mock_EverclearPayment} from - "src/templates/tests/unit/mocks/Mock_EverclearPayment.sol"; + "test/modules/paymentProcessor/bridging/abstracts/mocks/Mock_EverclearPayment.sol"; import { IERC20PaymentClientBase_v1, ERC20PaymentClientBaseV1Mock, From a5a10bf9621317fbb00e3965befd84abe220fa29 Mon Sep 17 00:00:00 2001 From: Zuhaib Mohammed Date: Sun, 10 Nov 2024 20:24:49 -0800 Subject: [PATCH 27/93] unit test chainid test --- .../abstracts/CrosschainBase_v1.sol | 5 +++ .../bridging/PP_Connext_Crosschain_v1.sol | 2 +- .../bridging/PP_Connext_Bridge.t.sol | 9 +----- .../abstracts/CrosschainBase_v1.t.sol | 31 ++++++++++++++++++- .../abstracts/CrosschainBase_v1_Exposed.sol | 1 + .../PP_Connext_Crosschain_v1_Test.t.sol | 14 +++++++-- 6 files changed, 49 insertions(+), 13 deletions(-) diff --git a/src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol b/src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol index 6024323ba..fb585c96d 100644 --- a/src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol +++ b/src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol @@ -55,6 +55,11 @@ abstract contract CrosschainBase_v1 is ICrossChainBase_v1, Module_v1 { uint indexed paymentId, address recipient, address token, uint amount ); + constructor(uint chainId_) { + //_chainId = chainId_; + _chainId = block.chainid; + } + //-------------------------------------------------------------------------- // Virtual Functions diff --git a/src/modules/paymentProcessor/bridging/PP_Connext_Crosschain_v1.sol b/src/modules/paymentProcessor/bridging/PP_Connext_Crosschain_v1.sol index cd0fd2df8..86773f4a7 100644 --- a/src/modules/paymentProcessor/bridging/PP_Connext_Crosschain_v1.sol +++ b/src/modules/paymentProcessor/bridging/PP_Connext_Crosschain_v1.sol @@ -12,7 +12,7 @@ contract PP_Connext_Crosschain_v1 is CrosschainBase_v1 { ConnextBridgeLogic public connextBridgeLogic; constructor(uint chainId_, address connextBridgeLogic_) - CrosschainBase_v1() + CrosschainBase_v1(chainId_) { connextBridgeLogic = ConnextBridgeLogic(connextBridgeLogic_); } diff --git a/test/modules/paymentProcessor/bridging/PP_Connext_Bridge.t.sol b/test/modules/paymentProcessor/bridging/PP_Connext_Bridge.t.sol index 05e3aac9f..bc67bb7f0 100644 --- a/test/modules/paymentProcessor/bridging/PP_Connext_Bridge.t.sol +++ b/test/modules/paymentProcessor/bridging/PP_Connext_Bridge.t.sol @@ -10,11 +10,10 @@ import { import {OZErrors} from "test/utils/errors/OZErrors.sol"; import {ICrossChainBase_v1} from - "src/modules/paymentProcessor/interfaces/ICrosschainBase_v1.sol"; + "src/modules/paymentProcessor/abstract/ICrosschainBase_v1.sol"; import {CrosschainBase_v1} from "src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol"; - //External Dependencies import {Clones} from "@oz/proxy/Clones.sol"; @@ -26,7 +25,6 @@ import { ERC20Mock } from "test/utils/mocks/modules/paymentClient/ERC20PaymentClientBaseV1Mock.sol"; - // //import exposed // import {PP_CrossChain_v1_Exposed} from // "../../tests/unit/PP_CrossChain_v1_Exposed.sol"; @@ -40,10 +38,7 @@ import { // "../../../src/orchestrator/interfaces/IOrchestrator_v1.sol"; import {ICrossChainBase_v1} from - "src/modules/paymentProcessor/interfaces/ICrosschainBase_v1.sol"; -======= "src/modules/paymentProcessor/abstract/ICrosschainBase_v1.sol"; ->>>>>>> 4aec33f (fix file structure) /** * @title Inverter Template Payment Processor * @@ -110,7 +105,6 @@ contract PP_Connext_Bridge_Test is ModuleTest { //-------------------------------------------------------------------------- //Test: Initialization - function testInit() public override(ModuleTest) { assertEq( address(paymentProcessor.orchestrator()), address(_orchestrator) @@ -133,5 +127,4 @@ contract PP_Connext_Bridge_Test is ModuleTest { _orchestrator, _METADATA, abi.encode(_payoutAmountMultiplier) ); } - } diff --git a/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.t.sol b/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.t.sol index 55efda6c8..6f23c51a1 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.t.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.t.sol @@ -71,7 +71,36 @@ contract CrosschainBase_v1_Test is ModuleTest { //-------------------------------------------------------------------------- //Setup - function setUp() public {} + function setUp() public { + //This function is used to setup the unit test + //Deploy the SuT + address impl = address(new CrosschainBase_v1(block.chainid)); + paymentProcessor = CrosschainBase_v1(Clones.clone(impl)); + + //Setup the module to test + _setUpOrchestrator(paymentProcessor); + + //General setup for other contracts in the workflow + _authorizer.setIsAuthorized(address(this), true); + + //Initiate the PP with the medata and config data + paymentProcessor.init( + _orchestrator, _METADATA, abi.encode(_payoutAmountMultiplier) + ); + + //Setup other modules needed in the unit tests. + //In this case a payment client is needed to test the PP_Template_v1. + impl = address(new ERC20PaymentClientBaseV1Mock()); + paymentClient = ERC20PaymentClientBaseV1Mock(Clones.clone(impl)); + //Adding the payment client is done through a timelock mechanism + _orchestrator.initiateAddModuleWithTimelock(address(paymentClient)); + vm.warp(block.timestamp + _orchestrator.MODULE_UPDATE_TIMELOCK()); + _orchestrator.executeAddModule(address(paymentClient)); + //Init payment client + paymentClient.init(_orchestrator, _METADATA, bytes("")); + paymentClient.setIsAuthorized(address(paymentProcessor), true); + paymentClient.setToken(_token); + } //-------------------------------------------------------------------------- //Test: Initialization diff --git a/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1_Exposed.sol b/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1_Exposed.sol index 6ec9df1d3..1f502c975 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1_Exposed.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1_Exposed.sol @@ -10,6 +10,7 @@ import {IERC20PaymentClientBase_v1} from contract CrosschainBase_v1_Exposed is CrosschainBase_v1 { /// @notice Implementation of the bridge transfer logic using EverClear ///// @inheritdoc CrosschainBase_v1 + function exposed_executeBridgeTransfer( IERC20PaymentClientBase_v1.PaymentOrder memory order, bytes memory executionData diff --git a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol index e9220b9ca..927737aeb 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol @@ -29,6 +29,7 @@ import { IOrchestrator_v1 } from "test/modules/ModuleTest.sol"; import {OZErrors} from "test/utils/errors/OZErrors.sol"; +import "forge-std/console2.sol"; contract PP_Connext_Crosschain_v1_Test is ModuleTest { PP_Connext_Crosschain_v1 public processor; @@ -40,16 +41,19 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { // Test addresses address public recipient = address(0x123); - uint public constant CHAIN_ID = 1; + uint public chainId; function setUp() public { + //Set the chainId + chainId = block.chainid; + // Deploy token token = new ERC20Mock("Test Token", "TEST"); // Deploy and setup mock payment client everclearPaymentMock = new Mock_EverclearPayment(); - address impl = address(new CrosschainBase_v1()); + address impl = address(new CrosschainBase_v1(chainId)); paymentProcessor = CrosschainBase_v1(Clones.clone(impl)); //Setup the module to test @@ -79,7 +83,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { bridgeLogic = new ConnextBridgeLogic( address(everclearPaymentMock), address(token) ); - processor = new PP_Connext_Crosschain_v1(CHAIN_ID, address(bridgeLogic)); + processor = new PP_Connext_Crosschain_v1(chainId, address(bridgeLogic)); paymentClient.setIsAuthorized(address(processor), true); // Setup token approvals and initial balances @@ -125,4 +129,8 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { // Process payments processor.processPayments(client); } + + function test_getChainId() public { + assertEq(processor.getChainId(), chainId); + } } From 5a78d5febb174b270874aefc89edd4c524832261 Mon Sep 17 00:00:00 2001 From: Zuhaib Mohammed Date: Sun, 10 Nov 2024 21:32:19 -0800 Subject: [PATCH 28/93] unit test processpayments test --- .../abstracts/CrosschainBase_v1.sol | 27 +++++- .../PP_Connext_Crosschain_v1_Test.t.sol | 89 ++++++++++++++++--- 2 files changed, 101 insertions(+), 15 deletions(-) diff --git a/src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol b/src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol index fb585c96d..c79d5b21e 100644 --- a/src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol +++ b/src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol @@ -70,5 +70,30 @@ abstract contract CrosschainBase_v1 is ICrossChainBase_v1, Module_v1 { function _executeBridgeTransfer( IERC20PaymentClientBase_v1.PaymentOrder memory order, bytes memory executionData - ) internal virtual returns (bytes memory) {} + ) internal virtual returns (bytes memory) { + emit BridgeTransferExecuted(executionData); + return bytes(""); + } + + function getChainId() external view returns (uint) { + return _chainId; + } + + /// @notice Process payments for a given payment client + /// @param client The payment client to process payments for + function processPayments(IERC20PaymentClientBase_v1 client) + external + virtual + {} + + /// @notice Get the bridge data for a given payment ID + /// @param paymentId The ID of the payment to get the bridge data for + /// @return The bridge data for the given payment ID + function getBridgeData(uint paymentId) + external + view + returns (bytes memory) + { + return _bridgeData[paymentId]; + } } diff --git a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol index 927737aeb..783f48ca4 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol @@ -95,19 +95,6 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { token.mint(address(processor), 1000 ether); // Mint tokens to processor vm.prank(address(processor)); token.approve(address(bridgeLogic), type(uint).max); // Processor approves bridge logic - - // Setup mock payment orders that will be returned by the mock - IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = - new IERC20PaymentClientBase_v1.PaymentOrder[](1); - orders[0] = IERC20PaymentClientBase_v1.PaymentOrder({ - recipient: recipient, - paymentToken: address(token), - amount: 100 ether, - start: block.timestamp, - cliff: 0, - end: block.timestamp + 1 days - }); - paymentClient.addPaymentOrders(orders); } function testInit() public override(ModuleTest) { @@ -121,16 +108,90 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { paymentProcessor.init(_orchestrator, _METADATA, abi.encode(1)); } - function test_ProcessPayments() public { + function test_ProcessPayments_singlePayment() public { + // Setup mock payment orders that will be returned by the mock + address[] memory setupRecipients = new address[](1); + setupRecipients[0] = recipient; + uint[] memory setupAmounts = new uint[](1); + setupAmounts[0] = 100 ether; + IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = + _createPaymentOrders(1, setupRecipients, setupAmounts); + paymentClient.addPaymentOrders(orders); + + // Get the client interface + IERC20PaymentClientBase_v1 client = + IERC20PaymentClientBase_v1(address(paymentClient)); + + // Process payments + processor.processPayments(client); + assertTrue( + keccak256(processor.getBridgeData(0)) != keccak256(bytes("")), + "Bridge data should not be empty" + ); + } + + function test_ProcessPayments_multiplePayment() public { + // Setup mock payment orders that will be returned by the mock + address[] memory setupRecipients = new address[](3); + setupRecipients[0] = address(0x1); + setupRecipients[1] = address(0x2); + setupRecipients[2] = address(0x3); + uint[] memory setupAmounts = new uint[](3); + setupAmounts[0] = 100 ether; + setupAmounts[1] = 125 ether; + setupAmounts[2] = 150 ether; + IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = + _createPaymentOrders(3, setupRecipients, setupAmounts); + paymentClient.addPaymentOrders(orders); + // Get the client interface IERC20PaymentClientBase_v1 client = IERC20PaymentClientBase_v1(address(paymentClient)); // Process payments processor.processPayments(client); + for (uint i = 0; i < setupRecipients.length; i++) { + assertTrue( + keccak256(processor.getBridgeData(i)) != keccak256(bytes("")), + "Bridge data should not be empty" + ); + } } function test_getChainId() public { assertEq(processor.getChainId(), chainId); } + + // Helper functions + function _createPaymentOrders( + uint orderCount, + address[] memory recipients, + uint[] memory amounts + ) + internal + view + returns (IERC20PaymentClientBase_v1.PaymentOrder[] memory) + { + // Sanity checks for array lengths + require( + recipients.length == orderCount && amounts.length == orderCount, + "Array lengths must match orderCount" + ); + + IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = + new IERC20PaymentClientBase_v1.PaymentOrder[](orderCount); + + for (uint i = 0; i < orderCount; i++) { + orders[i] = IERC20PaymentClientBase_v1.PaymentOrder({ + recipient: recipients[i], + paymentToken: address(token), + amount: amounts[i], + start: block.timestamp, + cliff: 0, + end: block.timestamp + 1 days + }); + } + + return orders; + } } From ac662d5ea2031e63f61984e5356106bf7a7e10b4 Mon Sep 17 00:00:00 2001 From: Zuhaib Mohammed Date: Mon, 11 Nov 2024 03:48:42 -0800 Subject: [PATCH 29/93] unit test paymentId check and test --- .../PP_Connext_Crosschain_v1_Test.t.sol | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol index 783f48ca4..dafb70d17 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol @@ -122,7 +122,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { IERC20PaymentClientBase_v1 client = IERC20PaymentClientBase_v1(address(paymentClient)); - // Process payments + // Process payments and verify _bridgeData mapping is updated processor.processPayments(client); assertTrue( keccak256(processor.getBridgeData(0)) != keccak256(bytes("")), @@ -148,7 +148,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { IERC20PaymentClientBase_v1 client = IERC20PaymentClientBase_v1(address(paymentClient)); - // Process payments + // Process payments and verify _bridgeData mapping is updated for each paymentId processor.processPayments(client); for (uint i = 0; i < setupRecipients.length; i++) { assertTrue( @@ -158,7 +158,19 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { } } + function test_ProcessPayments_noPayments() public { + // Process payments and verify _bridgeData mapping is not updated + processor.processPayments( + IERC20PaymentClientBase_v1(address(paymentClient)) + ); + assertTrue( + keccak256(processor.getBridgeData(0)) == keccak256(bytes("")), + "Bridge data should be empty" + ); + } + function test_getChainId() public { + // Verify the chainId is correct assertEq(processor.getChainId(), chainId); } From 4b74169bab248c3865be0f55d8d8c05f6cfa0bd9 Mon Sep 17 00:00:00 2001 From: Zuhaib Mohammed Date: Mon, 11 Nov 2024 23:12:29 -0800 Subject: [PATCH 30/93] unit test chainId and ProcessPayment --- .../abstracts/CrosschainBase_v1.sol | 4 +++ .../abstracts/CrosschainBase_v1_Exposed.sol | 2 ++ .../PP_Connext_Crosschain_v1_Test.t.sol | 35 +++++++------------ 3 files changed, 18 insertions(+), 23 deletions(-) diff --git a/src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol b/src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol index c79d5b21e..0bd493d14 100644 --- a/src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol +++ b/src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol @@ -60,6 +60,10 @@ abstract contract CrosschainBase_v1 is ICrossChainBase_v1, Module_v1 { _chainId = block.chainid; } + constructor(uint chainId_) { + //_chainId = chainId_; + _chainId = block.chainid; + } //-------------------------------------------------------------------------- // Virtual Functions diff --git a/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1_Exposed.sol b/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1_Exposed.sol index 1f502c975..0d6c89826 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1_Exposed.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1_Exposed.sol @@ -8,6 +8,8 @@ import {IERC20PaymentClientBase_v1} from "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; contract CrosschainBase_v1_Exposed is CrosschainBase_v1 { + constructor(uint chainId_) CrosschainBase_v1(chainId_) {} + /// @notice Implementation of the bridge transfer logic using EverClear ///// @inheritdoc CrosschainBase_v1 diff --git a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol index dafb70d17..64d201c16 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol @@ -1,4 +1,5 @@ // SPDX-License-Identifier: UNLICENSED + pragma solidity ^0.8.20; // External Dependencies @@ -42,18 +43,22 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { // Test addresses address public recipient = address(0x123); uint public chainId; + uint public chainId; function setUp() public { //Set the chainId chainId = block.chainid; + // Set the chainId + chainId = block.chainid; + // Deploy token token = new ERC20Mock("Test Token", "TEST"); // Deploy and setup mock payment client everclearPaymentMock = new Mock_EverclearPayment(); - address impl = address(new CrosschainBase_v1(chainId)); + address impl = address(new CrosschainBase_v1(block.chainid)); paymentProcessor = CrosschainBase_v1(Clones.clone(impl)); //Setup the module to test @@ -84,6 +89,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { address(everclearPaymentMock), address(token) ); processor = new PP_Connext_Crosschain_v1(chainId, address(bridgeLogic)); + processor = new PP_Connext_Crosschain_v1(chainId, address(bridgeLogic)); paymentClient.setIsAuthorized(address(processor), true); // Setup token approvals and initial balances @@ -108,6 +114,10 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { paymentProcessor.init(_orchestrator, _METADATA, abi.encode(1)); } + function test_getChainId() public { + assertEq(processor.getChainId(), chainId); + } + function test_ProcessPayments_singlePayment() public { // Setup mock payment orders that will be returned by the mock address[] memory setupRecipients = new address[](1); @@ -121,8 +131,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { // Get the client interface IERC20PaymentClientBase_v1 client = IERC20PaymentClientBase_v1(address(paymentClient)); - - // Process payments and verify _bridgeData mapping is updated + // Process payments processor.processPayments(client); assertTrue( keccak256(processor.getBridgeData(0)) != keccak256(bytes("")), @@ -143,7 +152,6 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = _createPaymentOrders(3, setupRecipients, setupAmounts); paymentClient.addPaymentOrders(orders); - // Get the client interface IERC20PaymentClientBase_v1 client = IERC20PaymentClientBase_v1(address(paymentClient)); @@ -158,22 +166,6 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { } } - function test_ProcessPayments_noPayments() public { - // Process payments and verify _bridgeData mapping is not updated - processor.processPayments( - IERC20PaymentClientBase_v1(address(paymentClient)) - ); - assertTrue( - keccak256(processor.getBridgeData(0)) == keccak256(bytes("")), - "Bridge data should be empty" - ); - } - - function test_getChainId() public { - // Verify the chainId is correct - assertEq(processor.getChainId(), chainId); - } - // Helper functions function _createPaymentOrders( uint orderCount, @@ -189,10 +181,8 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { recipients.length == orderCount && amounts.length == orderCount, "Array lengths must match orderCount" ); - IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = new IERC20PaymentClientBase_v1.PaymentOrder[](orderCount); - for (uint i = 0; i < orderCount; i++) { orders[i] = IERC20PaymentClientBase_v1.PaymentOrder({ recipient: recipients[i], @@ -203,7 +193,6 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { end: block.timestamp + 1 days }); } - return orders; } } From 32b2c05dab11130c06dfcab50ba0cb1b8c615a78 Mon Sep 17 00:00:00 2001 From: Zuhaib Mohammed Date: Tue, 12 Nov 2024 01:14:22 -0800 Subject: [PATCH 31/93] unit test paymentId check and test --- .../abstracts/PP_Connext_Crosschain_v1_Test.t.sol | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol index 64d201c16..9dc10c4cb 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol @@ -131,7 +131,8 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { // Get the client interface IERC20PaymentClientBase_v1 client = IERC20PaymentClientBase_v1(address(paymentClient)); - // Process payments + // Process payments and verify _bridgeData mapping is updated + // Process payments and verify _bridgeData mapping is updated for each paymentId processor.processPayments(client); assertTrue( keccak256(processor.getBridgeData(0)) != keccak256(bytes("")), @@ -166,6 +167,17 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { } } + function test_ProcessPayments_noPayments() public { + // Process payments and verify _bridgeData mapping is not updated + processor.processPayments( + IERC20PaymentClientBase_v1(address(paymentClient)) + ); + assertTrue( + keccak256(processor.getBridgeData(0)) == keccak256(bytes("")), + "Bridge data should be empty" + ); + } + // Helper functions function _createPaymentOrders( uint orderCount, From 567815e9d953c1cf61342accc32c48cfd17d51df Mon Sep 17 00:00:00 2001 From: Zuhaib Mohammed Date: Tue, 12 Nov 2024 20:25:19 -0800 Subject: [PATCH 32/93] resolve conflicts rebasing rebasing --- .vscode/settings.json | 14 +++++++++++++- .../abstracts/CrosschainBase_v1.sol | 13 ++++--------- .../abstracts/PP_Connext_Crosschain_v1_Test.t.sol | 4 ---- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index c89d041c6..5c9e0f9e8 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,5 +7,17 @@ "search.exclude": { "lib": true }, - "editor.formatOnSave": true + "editor.formatOnSave": true, + "wake.compiler.solc.remappings": [ + "@oz-up/=lib/openzeppelin-contracts-upgradeable/contracts/", + "@oz/=lib/openzeppelin-contracts/contracts/", + "@df/=lib/deterministic-factory/src/", + "@uma/=lib/uma-protocol/packages/core/contracts/", + "@ex/=src/external/", + "@fm/=src/modules/fundingManager/", + "@pp/=src/modules/paymentProcessor/", + "@lm/=src/modules/logicModule/", + "@lm_pc/=src/modules/logicModule/paymentClient/", + "@aut/=src/modules/authorizer/" + ] } \ No newline at end of file diff --git a/src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol b/src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol index 0bd493d14..5210c1c21 100644 --- a/src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol +++ b/src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol @@ -31,6 +31,10 @@ import {ICrossChainBase_v1} from "../interfaces/ICrosschainBase_v1.sol"; */ abstract contract CrosschainBase_v1 is ICrossChainBase_v1, Module_v1 { + constructor(uint chainId_) { + _chainId = block.chainid; + } + /// @inheritdoc ERC165Upgradeable function supportsInterface(bytes4 interfaceId_) public @@ -55,15 +59,6 @@ abstract contract CrosschainBase_v1 is ICrossChainBase_v1, Module_v1 { uint indexed paymentId, address recipient, address token, uint amount ); - constructor(uint chainId_) { - //_chainId = chainId_; - _chainId = block.chainid; - } - - constructor(uint chainId_) { - //_chainId = chainId_; - _chainId = block.chainid; - } //-------------------------------------------------------------------------- // Virtual Functions diff --git a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol index 9dc10c4cb..eeb2e793e 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol @@ -43,12 +43,8 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { // Test addresses address public recipient = address(0x123); uint public chainId; - uint public chainId; function setUp() public { - //Set the chainId - chainId = block.chainid; - // Set the chainId chainId = block.chainid; From af9df306912334de8dab24c798650802246cf0c7 Mon Sep 17 00:00:00 2001 From: Zuhaib Mohammed Date: Tue, 12 Nov 2024 22:15:53 -0800 Subject: [PATCH 33/93] add unit test verify bridgeData --- .../PP_Connext_Crosschain_v1_Test.t.sol | 69 ++++++++++++++++--- 1 file changed, 59 insertions(+), 10 deletions(-) diff --git a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol index eeb2e793e..ad0d61f2a 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol @@ -44,6 +44,14 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { address public recipient = address(0x123); uint public chainId; + // Add this event definition at the contract level + event PaymentProcessed( + uint indexed paymentId, + address recipient, + address paymentToken, + uint amount + ); + function setUp() public { // Set the chainId chainId = block.chainid; @@ -127,13 +135,18 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { // Get the client interface IERC20PaymentClientBase_v1 client = IERC20PaymentClientBase_v1(address(paymentClient)); - // Process payments and verify _bridgeData mapping is updated - // Process payments and verify _bridgeData mapping is updated for each paymentId - processor.processPayments(client); - assertTrue( - keccak256(processor.getBridgeData(0)) != keccak256(bytes("")), - "Bridge data should not be empty" + + // Expect the event + vm.expectEmit(true, true, true, true); + emit PaymentProcessed( + 0, // paymentId + recipient, + address(token), + 100 ether ); + + // Process payments + processor.processPayments(client); } function test_ProcessPayments_multiplePayment() public { @@ -154,13 +167,17 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { IERC20PaymentClientBase_v1(address(paymentClient)); // Process payments and verify _bridgeData mapping is updated for each paymentId - processor.processPayments(client); + // Expect the event for (uint i = 0; i < setupRecipients.length; i++) { - assertTrue( - keccak256(processor.getBridgeData(i)) != keccak256(bytes("")), - "Bridge data should not be empty" + vm.expectEmit(true, true, true, true); + emit PaymentProcessed( + i, // paymentId + setupRecipients[i], + address(token), + setupAmounts[i] ); } + processor.processPayments(client); } function test_ProcessPayments_noPayments() public { @@ -174,6 +191,38 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { ); } + function test_returnsCorrectBridgeData() public { + // Setup mock payment orders that will be returned by the mock + address[] memory setupRecipients = new address[](1); + setupRecipients[0] = recipient; + uint[] memory setupAmounts = new uint[](1); + setupAmounts[0] = 100 ether; + IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = + _createPaymentOrders(1, setupRecipients, setupAmounts); + paymentClient.addPaymentOrders(orders); + + // Get the client interface + IERC20PaymentClientBase_v1 client = + IERC20PaymentClientBase_v1(address(paymentClient)); + // Process payments and verify _bridgeData mapping is updated + processor.processPayments(client); + assertTrue( + keccak256(processor.getBridgeData(0)) != keccak256(bytes("")), + "Bridge data should not be empty" + ); + } + + function test_returnsEmptyBridgeData() public { + IERC20PaymentClientBase_v1 client = + IERC20PaymentClientBase_v1(address(paymentClient)); + // Process payments and verify _bridgeData mapping is updated + processor.processPayments(client); + assertTrue( + keccak256(processor.getBridgeData(0)) == keccak256(bytes("")), + "Bridge data should be empty" + ); + } + // Helper functions function _createPaymentOrders( uint orderCount, From 2c96138cfd5981494ce0242d4d55ad46980c67e3 Mon Sep 17 00:00:00 2001 From: Zuhaib Mohammed Date: Wed, 13 Nov 2024 03:16:37 -0800 Subject: [PATCH 34/93] add unit test process payments insufficient balance --- .../PP_Connext_Crosschain_v1_Test.t.sol | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol index ad0d61f2a..70b7d987d 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol @@ -30,6 +30,8 @@ import { IOrchestrator_v1 } from "test/modules/ModuleTest.sol"; import {OZErrors} from "test/utils/errors/OZErrors.sol"; +import {IERC20Errors} from "@oz/interfaces/draft-IERC6093.sol"; + import "forge-std/console2.sol"; contract PP_Connext_Crosschain_v1_Test is ModuleTest { @@ -223,6 +225,30 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { ); } + function test_ProcessPayments_InsufficientBalance() public { + // Setup payment order with amount larger than processor's balance + address[] memory setupRecipients = new address[](1); + setupRecipients[0] = recipient; + uint[] memory setupAmounts = new uint[](1); + setupAmounts[0] = 2000 ether; // More than the 1000 ether minted in setup + IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = + _createPaymentOrders(1, setupRecipients, setupAmounts); + paymentClient.addPaymentOrders(orders); + + IERC20PaymentClientBase_v1 client = + IERC20PaymentClientBase_v1(address(paymentClient)); + + vm.expectRevert( + abi.encodeWithSelector( + IERC20Errors.ERC20InsufficientBalance.selector, + address(processor), + token.balanceOf(address(processor)), + setupAmounts[0] + ) + ); + processor.processPayments(client); + } + // Helper functions function _createPaymentOrders( uint orderCount, From 330cbc240058d3dc9420cee96a2a8a98502346f4 Mon Sep 17 00:00:00 2001 From: Leandro Faria Date: Mon, 18 Nov 2024 11:21:26 -0500 Subject: [PATCH 35/93] add feedback comment fix paths after rebase rebased and add init --- .../PP_Connext_Crosschain_v1.sol | 26 ++-- .../abstracts/CrosschainBase_v1.sol | 15 +- .../abstracts/PP_Crosschain_v1.sol | 11 +- .../bridging/ConnextBridgeLogic.sol | 67 --------- .../bridging/PP_Connext_Crosschain_v1.sol | 63 --------- .../bridging/PP_Connext_Bridge.t.sol | 130 ------------------ .../abstracts/CrosschainBase_v1.t.sol | 7 +- .../abstracts/CrosschainBase_v1_Exposed.sol | 2 +- .../PP_Connext_Crosschain_v1_Test.t.sol | 85 +++++++----- 9 files changed, 82 insertions(+), 324 deletions(-) delete mode 100644 src/modules/paymentProcessor/bridging/ConnextBridgeLogic.sol delete mode 100644 src/modules/paymentProcessor/bridging/PP_Connext_Crosschain_v1.sol delete mode 100644 test/modules/paymentProcessor/bridging/PP_Connext_Bridge.t.sol diff --git a/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol b/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol index 7aead5e0e..82b84e9d8 100644 --- a/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol +++ b/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol @@ -23,14 +23,10 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { IOrchestrator_v1 orchestrator_, Metadata memory metadata, bytes memory configData - ) external override initializer { + ) external override(PP_Crosschain_v1) initializer { __Module_init(orchestrator_, metadata); - ( - uint chainId_, - address connextBridgeLogic_, - address everClearSpoke_, - address weth_ - ) = abi.decode(configData, (uint, address, address, address)); + (address everClearSpoke_, address weth_) = + abi.decode(configData, (address, address)); everClearSpoke = IEverclearSpoke(everClearSpoke_); weth = IWETH(weth_); @@ -80,7 +76,7 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { function xcall( IERC20PaymentClientBase_v1.PaymentOrder memory order, bytes memory executionData - ) internal returns (bytes32 intentId) { + ) internal returns (bytes32) { // Decode any additional parameters from executionData (uint maxFee, uint ttl) = abi.decode(executionData, (uint, uint)); @@ -101,13 +97,13 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { // Call newIntent on the EverClearSpoke contract (intentId,) = everClearSpoke.newIntent( destinations, - order.recipient, // to - order.paymentToken, // inputAsset - address(weth), // outputAsset (assuming same asset on destination) - order.amount, // amount - uint24(maxFee), // maxFee (cast to uint24) - uint48(ttl), // ttl (cast to uint48) - "" // empty data field, modify if needed + order.recipient, + order.paymentToken, + address(weth), + order.amount, + uint24(maxFee), + uint48(ttl), + "" ); return intentId; diff --git a/src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol b/src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol index 5210c1c21..2b01a4403 100644 --- a/src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol +++ b/src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol @@ -31,11 +31,9 @@ import {ICrossChainBase_v1} from "../interfaces/ICrosschainBase_v1.sol"; */ abstract contract CrosschainBase_v1 is ICrossChainBase_v1, Module_v1 { - constructor(uint chainId_) { - _chainId = block.chainid; - } - + mapping(uint => bytes) internal _bridgeData; /// @inheritdoc ERC165Upgradeable + function supportsInterface(bytes4 interfaceId_) public view @@ -74,12 +72,15 @@ abstract contract CrosschainBase_v1 is ICrossChainBase_v1, Module_v1 { return bytes(""); } - function getChainId() external view returns (uint) { - return _chainId; + function init(IOrchestrator_v1 orchestrator_, Metadata memory metadata) + internal + initializer + { + __Module_init(orchestrator_, metadata); } - /// @notice Process payments for a given payment client /// @param client The payment client to process payments for + function processPayments(IERC20PaymentClientBase_v1 client) external virtual diff --git a/src/modules/paymentProcessor/abstracts/PP_Crosschain_v1.sol b/src/modules/paymentProcessor/abstracts/PP_Crosschain_v1.sol index fa1dbc4c1..a2ea0faff 100644 --- a/src/modules/paymentProcessor/abstracts/PP_Crosschain_v1.sol +++ b/src/modules/paymentProcessor/abstracts/PP_Crosschain_v1.sol @@ -54,6 +54,15 @@ abstract contract PP_Crosschain_v1 is CrosschainBase_v1, IPP_Crosschain_v1 { || super.supportsInterface(interfaceId_); } + function init(IOrchestrator_v1 orchestrator_, Metadata memory metadata) + external + virtual + override(Module_v1) + initializer + { + __Module_init(orchestrator_, metadata); + } + //-------------------------------------------------------------------------- // Modifiers @@ -89,7 +98,7 @@ abstract contract PP_Crosschain_v1 is CrosschainBase_v1, IPP_Crosschain_v1 { function processPayments(IERC20PaymentClientBase_v1 client) external virtual - override(IPaymentProcessor_v1) + override(IPaymentProcessor_v1, CrosschainBase_v1) {} /// @inheritdoc IPaymentProcessor_v1 diff --git a/src/modules/paymentProcessor/bridging/ConnextBridgeLogic.sol b/src/modules/paymentProcessor/bridging/ConnextBridgeLogic.sol deleted file mode 100644 index 765bc0ddf..000000000 --- a/src/modules/paymentProcessor/bridging/ConnextBridgeLogic.sol +++ /dev/null @@ -1,67 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity 0.8.23; - -import {CrosschainBase_v1} from - "src/modules/paymentProcessor/abstract/CrosschainBase_v1.sol"; -import {IERC20PaymentClientBase_v1} from - "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; -import {IERC20} from "@oz/token/ERC20/IERC20.sol"; - -interface IEverclearSpoke { - function newIntent( - uint32[] memory destinations, - address to, - address inputAsset, - address outputAsset, - uint amount, - uint24 maxFee, - uint48 ttl, - bytes memory data - ) external returns (bytes32 intentId, uint amountOut); -} - -contract ConnextBridgeLogic { - IEverclearSpoke public everClearSpoke; - address public immutable weth; - - constructor(address _everclearSpoke, address _weth) { - everClearSpoke = IEverclearSpoke(_everclearSpoke); - weth = _weth; - } - - function xcall( - IERC20PaymentClientBase_v1.PaymentOrder memory order, - bytes memory executionData - ) external payable returns (bytes32 intentId) { - // Decode any additional parameters from executionData - (uint maxFee, uint ttl) = abi.decode(executionData, (uint, uint)); - - // Wrap ETH into WETH to send with the xcall - IERC20(order.paymentToken).transferFrom( - msg.sender, address(this), order.amount - ); - - // This contract approves transfer to EverClearSpoke - IERC20(order.paymentToken).approve( - address(everClearSpoke), order.amount - ); - - // Create destinations array with the target chain - uint32[] memory destinations = new uint32[](1); - destinations[0] = 8453; // @note -> hardcode for now -> order.destinationChainId; - - // Call newIntent on the EverClearSpoke contract - (bytes32 intentId,) = everClearSpoke.newIntent( - destinations, - order.recipient, // to - order.paymentToken, // inputAsset - address(weth), // outputAsset (assuming same asset on destination) - order.amount, // amount - uint24(maxFee), // maxFee (cast to uint24) - uint48(ttl), // ttl (cast to uint48) - "" // empty data field, modify if needed - ); - - return intentId; - } -} diff --git a/src/modules/paymentProcessor/bridging/PP_Connext_Crosschain_v1.sol b/src/modules/paymentProcessor/bridging/PP_Connext_Crosschain_v1.sol deleted file mode 100644 index 86773f4a7..000000000 --- a/src/modules/paymentProcessor/bridging/PP_Connext_Crosschain_v1.sol +++ /dev/null @@ -1,63 +0,0 @@ -pragma solidity ^0.8.20; - -import {CrosschainBase_v1} from - "src/modules/paymentProcessor/abstract/CrosschainBase_v1.sol"; -import {ICrossChainBase_v1} from - "src/modules/paymentProcessor/abstract/ICrosschainBase_v1.sol"; -import {ConnextBridgeLogic} from "./ConnextBridgeLogic.sol"; -import {IERC20PaymentClientBase_v1} from - "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; - -contract PP_Connext_Crosschain_v1 is CrosschainBase_v1 { - ConnextBridgeLogic public connextBridgeLogic; - - constructor(uint chainId_, address connextBridgeLogic_) - CrosschainBase_v1(chainId_) - { - connextBridgeLogic = ConnextBridgeLogic(connextBridgeLogic_); - } - - /// @notice Execute the cross-chain bridge transfer - /// @dev Override this function to implement specific bridge logic - /// @param order The payment order containing all necessary transfer details - /// @return bridgeData Arbitrary data returned by the bridge implementation - function _executeBridgeTransfer( - IERC20PaymentClientBase_v1.PaymentOrder memory order, - bytes memory executionData - ) internal override returns (bytes memory) { - //@notice call the connextBridgeLogic to execute the bridge transfer - bytes32 intentId = connextBridgeLogic.xcall(order, executionData); - return abi.encode(intentId); - } - - function processPayments(IERC20PaymentClientBase_v1 client) - external - override - { - // To encode maxFee and ttl: - uint maxFee = 0; - uint ttl = 0; - bytes memory executionData = abi.encode(maxFee, ttl); - - // Collect orders from the client - IERC20PaymentClientBase_v1.PaymentOrder[] memory orders; - (orders,,) = client.collectPaymentOrders(); - - for (uint i = 0; i < orders.length; i++) { - bytes memory bridgeData = - _executeBridgeTransfer(orders[i], executionData); - _bridgeData[_paymentId] = bridgeData; - - emit PaymentProcessed( - _paymentId, - orders[i].recipient, - orders[i].paymentToken, - orders[i].amount - ); - _paymentId++; - - // Inform the client about the processed amount - client.amountPaid(orders[i].paymentToken, orders[i].amount); - } - } -} diff --git a/test/modules/paymentProcessor/bridging/PP_Connext_Bridge.t.sol b/test/modules/paymentProcessor/bridging/PP_Connext_Bridge.t.sol deleted file mode 100644 index bc67bb7f0..000000000 --- a/test/modules/paymentProcessor/bridging/PP_Connext_Bridge.t.sol +++ /dev/null @@ -1,130 +0,0 @@ -//SPDX-License-Identifier: LGPL-3.0-only -pragma solidity ^0.8.0; - -//Internal Dependencies -import { - ModuleTest, - IModule_v1, - IOrchestrator_v1 -} from "test/modules/ModuleTest.sol"; -import {OZErrors} from "test/utils/errors/OZErrors.sol"; - -import {ICrossChainBase_v1} from - "src/modules/paymentProcessor/abstract/ICrosschainBase_v1.sol"; -import {CrosschainBase_v1} from - "src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol"; - -//External Dependencies -import {Clones} from "@oz/proxy/Clones.sol"; - -//Tests and Mocks -// import cr -import { - IERC20PaymentClientBase_v1, - ERC20PaymentClientBaseV1Mock, - ERC20Mock -} from "test/utils/mocks/modules/paymentClient/ERC20PaymentClientBaseV1Mock.sol"; - -// //import exposed -// import {PP_CrossChain_v1_Exposed} from -// "../../tests/unit/PP_CrossChain_v1_Exposed.sol"; - -//System under test (SuT) -// import { -// PP_Connext_Crosschain_v1, -// IPP_Connext_Crosschain_v1 -// } from "../../../src/templates/modules/Connext_Bridge.sol"; -// import {IPaymentProcessor_v1} from -// "../../../src/orchestrator/interfaces/IOrchestrator_v1.sol"; - -import {ICrossChainBase_v1} from - "src/modules/paymentProcessor/abstract/ICrosschainBase_v1.sol"; -/** - * @title Inverter Template Payment Processor - * - * @notice Basic template payment processor used to showcase the unit testing setup - * - * @dev Not all functions are tested in this template. Placeholders of the functions that are not tested are added - * into the contract. This test showcases the following: - * - Inherit from the ModuleTest contract to enable interaction with the Inverter workflow. - * - Showcases the setup of the workflow, uses in test unit tests. - * - Pre-defined layout for all setup and functions to be tested. - * - Shows the use of Gherkin for documenting the testing. VS Code extension used for formatting is recommended. - * - Shows the use of the modifierInPlace pattern to test the modifier placement. - * - * @custom:security-contact security@inverter.network - * In case of any concerns or findings, please refer to our Security Policy - * at security.inverter.network or email us directly! - * - * @author Inverter Network - */ - -contract PP_Connext_Bridge_Test is ModuleTest { - //-------------------------------------------------------------------------- - //Constants - - //-------------------------------------------------------------------------- - //State - - //-------------------------------------------------------------------------- - //Mocks - - //-------------------------------------------------------------------------- - //Setup - function setUp() public { - //This function is used to setup the unit test - //Deploy the SuT - address impl = address(new CrosschainBase_v1()); - paymentProcessor = CrosschainBase_v1(Clones.clone(impl)); - - //Setup the module to test - _setUpOrchestrator(paymentProcessor); - - //General setup for other contracts in the workflow - _authorizer.setIsAuthorized(address(this), true); - - //Initiate the PP with the medata and config data - paymentProcessor.init( - _orchestrator, _METADATA, abi.encode(_payoutAmountMultiplier) - ); - - //Setup other modules needed in the unit tests. - //In this case a payment client is needed to test the PP_Template_v1. - impl = address(new ERC20PaymentClientBaseV1Mock()); - paymentClient = ERC20PaymentClientBaseV1Mock(Clones.clone(impl)); - //Adding the payment client is done through a timelock mechanism - _orchestrator.initiateAddModuleWithTimelock(address(paymentClient)); - vm.warp(block.timestamp + _orchestrator.MODULE_UPDATE_TIMELOCK()); - _orchestrator.executeAddModule(address(paymentClient)); - //Init payment client - paymentClient.init(_orchestrator, _METADATA, bytes("")); - paymentClient.setIsAuthorized(address(paymentProcessor), true); - paymentClient.setToken(_token); - } - - //-------------------------------------------------------------------------- - //Test: Initialization - - function testInit() public override(ModuleTest) { - assertEq( - address(paymentProcessor.orchestrator()), address(_orchestrator) - ); - } - - //Test the interface support - function testSupportsInterface() public { - assertTrue( - paymentProcessor.supportsInterface( - type(ICrossChainBase_v1).interfaceId - ) - ); - } - - //Test the reinit function - function testReinitFails() public override(ModuleTest) { - vm.expectRevert(OZErrors.Initializable__InvalidInitialization); - paymentProcessor.init( - _orchestrator, _METADATA, abi.encode(_payoutAmountMultiplier) - ); - } -} diff --git a/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.t.sol b/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.t.sol index 6f23c51a1..d583a7c36 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.t.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.t.sol @@ -74,7 +74,7 @@ contract CrosschainBase_v1_Test is ModuleTest { function setUp() public { //This function is used to setup the unit test //Deploy the SuT - address impl = address(new CrosschainBase_v1(block.chainid)); + address impl = address(new CrosschainBase_v1_Exposed(block.chainid)); paymentProcessor = CrosschainBase_v1(Clones.clone(impl)); //Setup the module to test @@ -83,11 +83,6 @@ contract CrosschainBase_v1_Test is ModuleTest { //General setup for other contracts in the workflow _authorizer.setIsAuthorized(address(this), true); - //Initiate the PP with the medata and config data - paymentProcessor.init( - _orchestrator, _METADATA, abi.encode(_payoutAmountMultiplier) - ); - //Setup other modules needed in the unit tests. //In this case a payment client is needed to test the PP_Template_v1. impl = address(new ERC20PaymentClientBaseV1Mock()); diff --git a/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1_Exposed.sol b/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1_Exposed.sol index 0d6c89826..1a9521d8e 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1_Exposed.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1_Exposed.sol @@ -8,7 +8,7 @@ import {IERC20PaymentClientBase_v1} from "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; contract CrosschainBase_v1_Exposed is CrosschainBase_v1 { - constructor(uint chainId_) CrosschainBase_v1(chainId_) {} + constructor(uint chainId_) CrosschainBase_v1() {} /// @notice Implementation of the bridge transfer logic using EverClear ///// @inheritdoc CrosschainBase_v1 diff --git a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol index 70b7d987d..a47ec17ce 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol @@ -8,11 +8,12 @@ import {Clones} from "@oz/proxy/Clones.sol"; // Internal Dependencies import {PP_Connext_Crosschain_v1} from - "src/modules/paymentProcessor/bridging/PP_Connext_Crosschain_v1.sol"; -import {ConnextBridgeLogic} from - "src/modules/paymentProcessor/bridging/ConnextBridgeLogic.sol"; + "src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol"; + import {CrosschainBase_v1} from - "src/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.sol"; + "src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol"; +import {CrosschainBase_v1_Exposed} from + "test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1_Exposed.sol"; import {IERC20PaymentClientBase_v1} from "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; @@ -35,8 +36,7 @@ import {IERC20Errors} from "@oz/interfaces/draft-IERC6093.sol"; import "forge-std/console2.sol"; contract PP_Connext_Crosschain_v1_Test is ModuleTest { - PP_Connext_Crosschain_v1 public processor; - ConnextBridgeLogic public bridgeLogic; + PP_Connext_Crosschain_v1 public crossChainManager; Mock_EverclearPayment public everclearPaymentMock; ERC20Mock public token; ERC20PaymentClientBaseV1Mock paymentClient; @@ -54,6 +54,11 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { uint amount ); + // Add these as contract state variables + address public mockConnextBridge; + address public mockEverClearSpoke; + address public mockWeth; + function setUp() public { // Set the chainId chainId = block.chainid; @@ -64,8 +69,8 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { // Deploy and setup mock payment client everclearPaymentMock = new Mock_EverclearPayment(); - address impl = address(new CrosschainBase_v1(block.chainid)); - paymentProcessor = CrosschainBase_v1(Clones.clone(impl)); + address impl = address(new CrosschainBase_v1_Exposed(block.chainid)); + paymentProcessor = CrosschainBase_v1_Exposed(Clones.clone(impl)); //Setup the module to test _setUpOrchestrator(paymentProcessor); @@ -90,23 +95,36 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { paymentClient.setToken(_token); - // Deploy bridge logic and processor - bridgeLogic = new ConnextBridgeLogic( - address(everclearPaymentMock), address(token) + crossChainManager = new PP_Connext_Crosschain_v1(); + //init the processor + // Initialize with proper config data + bytes memory configData = abi.encode( + address(mockEverClearSpoke), // address of your EverClear spoke contract + address(mockWeth) // address of your WETH contract ); - processor = new PP_Connext_Crosschain_v1(chainId, address(bridgeLogic)); - processor = new PP_Connext_Crosschain_v1(chainId, address(bridgeLogic)); - paymentClient.setIsAuthorized(address(processor), true); + + crossChainManager.init(_orchestrator, _METADATA, configData); + paymentClient.setIsAuthorized(address(crossChainManager), true); // Setup token approvals and initial balances token.mint(address(this), 1000 ether); - token.approve(address(processor), type(uint).max); - token.approve(address(bridgeLogic), type(uint).max); + token.approve(address(crossChainManager), type(uint).max); + token.approve(address(crossChainManager), type(uint).max); // Add these lines to ensure proper token flow - token.mint(address(processor), 1000 ether); // Mint tokens to processor - vm.prank(address(processor)); - token.approve(address(bridgeLogic), type(uint).max); // Processor approves bridge logic + token.mint(address(crossChainManager), 1000 ether); // Mint tokens to processor + vm.prank(address(crossChainManager)); + token.approve(address(crossChainManager), type(uint).max); // Processor approves bridge logic + + // Setup mock addresses + mockConnextBridge = address(0x123456); + mockEverClearSpoke = address(everclearPaymentMock); // Using the existing mock + mockWeth = address(0x789012); // Or deploy a mock WETH contract if needed + + crossChainManager = new PP_Connext_Crosschain_v1(); + + crossChainManager.init(_orchestrator, _METADATA, configData); + paymentClient.setIsAuthorized(address(crossChainManager), true); } function testInit() public override(ModuleTest) { @@ -120,10 +138,6 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { paymentProcessor.init(_orchestrator, _METADATA, abi.encode(1)); } - function test_getChainId() public { - assertEq(processor.getChainId(), chainId); - } - function test_ProcessPayments_singlePayment() public { // Setup mock payment orders that will be returned by the mock address[] memory setupRecipients = new address[](1); @@ -148,7 +162,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { ); // Process payments - processor.processPayments(client); + crossChainManager.processPayments(client); } function test_ProcessPayments_multiplePayment() public { @@ -179,16 +193,17 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { setupAmounts[i] ); } - processor.processPayments(client); + crossChainManager.processPayments(client); } function test_ProcessPayments_noPayments() public { // Process payments and verify _bridgeData mapping is not updated - processor.processPayments( + crossChainManager.processPayments( IERC20PaymentClientBase_v1(address(paymentClient)) ); assertTrue( - keccak256(processor.getBridgeData(0)) == keccak256(bytes("")), + keccak256(crossChainManager.getBridgeData(0)) + == keccak256(bytes("")), "Bridge data should be empty" ); } @@ -207,9 +222,10 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { IERC20PaymentClientBase_v1 client = IERC20PaymentClientBase_v1(address(paymentClient)); // Process payments and verify _bridgeData mapping is updated - processor.processPayments(client); + crossChainManager.processPayments(client); assertTrue( - keccak256(processor.getBridgeData(0)) != keccak256(bytes("")), + keccak256(crossChainManager.getBridgeData(0)) + != keccak256(bytes("")), "Bridge data should not be empty" ); } @@ -218,9 +234,10 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { IERC20PaymentClientBase_v1 client = IERC20PaymentClientBase_v1(address(paymentClient)); // Process payments and verify _bridgeData mapping is updated - processor.processPayments(client); + crossChainManager.processPayments(client); assertTrue( - keccak256(processor.getBridgeData(0)) == keccak256(bytes("")), + keccak256(crossChainManager.getBridgeData(0)) + == keccak256(bytes("")), "Bridge data should be empty" ); } @@ -241,12 +258,12 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { vm.expectRevert( abi.encodeWithSelector( IERC20Errors.ERC20InsufficientBalance.selector, - address(processor), - token.balanceOf(address(processor)), + address(crossChainManager), + token.balanceOf(address(crossChainManager)), setupAmounts[0] ) ); - processor.processPayments(client); + crossChainManager.processPayments(client); } // Helper functions From be2fc27f6a18c244e1d4419accb3a3e2a3d2ed42 Mon Sep 17 00:00:00 2001 From: Zuhaib Mohammed Date: Tue, 19 Nov 2024 08:16:04 -0800 Subject: [PATCH 36/93] fixed unit tests after rebase remove init functions --- .../PP_Connext_Crosschain_v1.sol | 7 +++++- .../abstracts/CrosschainBase_v1.sol | 7 ------ .../abstracts/PP_Crosschain_v1.sol | 9 ------- .../PP_Connext_Crosschain_v1_Test.t.sol | 24 +++++++------------ 4 files changed, 15 insertions(+), 32 deletions(-) diff --git a/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol b/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol index 82b84e9d8..dc09ffe8e 100644 --- a/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol +++ b/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol @@ -14,6 +14,7 @@ import {IEverclearSpoke} from "src/modules/paymentProcessor/interfaces/IEverclear.sol"; import {IOrchestrator_v1} from "src/orchestrator/interfaces/IOrchestrator_v1.sol"; +import {Module_v1} from "src/modules/base/Module_v1.sol"; contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { IEverclearSpoke public everClearSpoke; @@ -23,7 +24,7 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { IOrchestrator_v1 orchestrator_, Metadata memory metadata, bytes memory configData - ) external override(PP_Crosschain_v1) initializer { + ) external override(Module_v1) initializer { __Module_init(orchestrator_, metadata); (address everClearSpoke_, address weth_) = abi.decode(configData, (address, address)); @@ -49,6 +50,10 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { external override { + uint maxFee = 0; + uint ttl = 0; + bytes memory executionData = abi.encode(maxFee, ttl); + // Collect orders from the client IERC20PaymentClientBase_v1.PaymentOrder[] memory orders; (orders,,) = client.collectPaymentOrders(); diff --git a/src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol b/src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol index 2b01a4403..1578c1682 100644 --- a/src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol +++ b/src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol @@ -71,13 +71,6 @@ abstract contract CrosschainBase_v1 is ICrossChainBase_v1, Module_v1 { emit BridgeTransferExecuted(executionData); return bytes(""); } - - function init(IOrchestrator_v1 orchestrator_, Metadata memory metadata) - internal - initializer - { - __Module_init(orchestrator_, metadata); - } /// @notice Process payments for a given payment client /// @param client The payment client to process payments for diff --git a/src/modules/paymentProcessor/abstracts/PP_Crosschain_v1.sol b/src/modules/paymentProcessor/abstracts/PP_Crosschain_v1.sol index a2ea0faff..540fcbe0e 100644 --- a/src/modules/paymentProcessor/abstracts/PP_Crosschain_v1.sol +++ b/src/modules/paymentProcessor/abstracts/PP_Crosschain_v1.sol @@ -54,15 +54,6 @@ abstract contract PP_Crosschain_v1 is CrosschainBase_v1, IPP_Crosschain_v1 { || super.supportsInterface(interfaceId_); } - function init(IOrchestrator_v1 orchestrator_, Metadata memory metadata) - external - virtual - override(Module_v1) - initializer - { - __Module_init(orchestrator_, metadata); - } - //-------------------------------------------------------------------------- // Modifiers diff --git a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol index a47ec17ce..1cacab263 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol @@ -89,13 +89,18 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { _orchestrator.initiateAddModuleWithTimelock(address(paymentClient)); vm.warp(block.timestamp + _orchestrator.MODULE_UPDATE_TIMELOCK()); _orchestrator.executeAddModule(address(paymentClient)); + //Init payment client paymentClient.init(_orchestrator, _METADATA, bytes("")); paymentClient.setIsAuthorized(address(paymentProcessor), true); + paymentClient.setToken(token); + impl = address(new PP_Connext_Crosschain_v1()); + crossChainManager = PP_Connext_Crosschain_v1(Clones.clone(impl)); + // Setup mock addresses + mockConnextBridge = address(0x123456); + mockEverClearSpoke = address(everclearPaymentMock); // Using the existing mock + mockWeth = address(0x789012); // Or deploy a mock WETH contract if needed - paymentClient.setToken(_token); - - crossChainManager = new PP_Connext_Crosschain_v1(); //init the processor // Initialize with proper config data bytes memory configData = abi.encode( @@ -109,22 +114,11 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { // Setup token approvals and initial balances token.mint(address(this), 1000 ether); token.approve(address(crossChainManager), type(uint).max); - token.approve(address(crossChainManager), type(uint).max); // Add these lines to ensure proper token flow token.mint(address(crossChainManager), 1000 ether); // Mint tokens to processor vm.prank(address(crossChainManager)); token.approve(address(crossChainManager), type(uint).max); // Processor approves bridge logic - - // Setup mock addresses - mockConnextBridge = address(0x123456); - mockEverClearSpoke = address(everclearPaymentMock); // Using the existing mock - mockWeth = address(0x789012); // Or deploy a mock WETH contract if needed - - crossChainManager = new PP_Connext_Crosschain_v1(); - - crossChainManager.init(_orchestrator, _METADATA, configData); - paymentClient.setIsAuthorized(address(crossChainManager), true); } function testInit() public override(ModuleTest) { @@ -258,7 +252,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { vm.expectRevert( abi.encodeWithSelector( IERC20Errors.ERC20InsufficientBalance.selector, - address(crossChainManager), + address(this), token.balanceOf(address(crossChainManager)), setupAmounts[0] ) From 6928ec0f2d986e6184bb8098f1636d726411af3c Mon Sep 17 00:00:00 2001 From: Leandro Faria Date: Tue, 19 Nov 2024 20:20:09 -0500 Subject: [PATCH 37/93] add comments and fuzz tests add notes --- .../PP_Connext_Crosschain_v1.sol | 9 +- .../interfaces/ICrosschainBase_v1.sol | 3 + .../abstracts/CrosschainBase_v1.t.sol | 4 +- .../PP_Connext_Crosschain_v1_Test.t.sol | 104 ++++++++++++++++++ 4 files changed, 116 insertions(+), 4 deletions(-) diff --git a/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol b/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol index dc09ffe8e..b092d31d6 100644 --- a/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol +++ b/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol @@ -82,9 +82,10 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { IERC20PaymentClientBase_v1.PaymentOrder memory order, bytes memory executionData ) internal returns (bytes32) { - // Decode any additional parameters from executionData (uint maxFee, uint ttl) = abi.decode(executionData, (uint, uint)); - + if (ttl == 0) { + revert ICrossChainBase_v1.Module__CrossChainBase_InvalidTTL(); + } // Wrap ETH into WETH to send with the xcall IERC20(order.paymentToken).transferFrom( msg.sender, address(this), order.amount @@ -97,7 +98,9 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { // Create destinations array with the target chain uint32[] memory destinations = new uint32[](1); - destinations[0] = 8453; // @note -> hardcode for now -> order.destinationChainId; + destinations[0] = 8453; + // @note -> hardcode for now -> order.destinationChainId when the + // new struct is created for us // Call newIntent on the EverClearSpoke contract (intentId,) = everClearSpoke.newIntent( diff --git a/src/modules/paymentProcessor/interfaces/ICrosschainBase_v1.sol b/src/modules/paymentProcessor/interfaces/ICrosschainBase_v1.sol index 9346ad2a4..4deb7732c 100644 --- a/src/modules/paymentProcessor/interfaces/ICrosschainBase_v1.sol +++ b/src/modules/paymentProcessor/interfaces/ICrosschainBase_v1.sol @@ -38,6 +38,9 @@ interface ICrossChainBase_v1 { /// @notice Message has already been executed error Module__CrossChainBase_MessageAlreadyExecuted(); + /// @notice Invalid TTL provided + error Module__CrossChainBase_InvalidTTL(); + /// @notice Invalid chain ID provided error Module__CrossChainBase_InvalidChainId(); diff --git a/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.t.sol b/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.t.sol index d583a7c36..c3c9e2954 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.t.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.t.sol @@ -99,7 +99,8 @@ contract CrosschainBase_v1_Test is ModuleTest { //-------------------------------------------------------------------------- //Test: Initialization - + //@zuhaib - we need some basic tests for the base, we just need to init it and + //make sure that the executeBridgeTransfer is correctly set and returns empty bytes //Test if the orchestrator is correctly set function testInit() public override(ModuleTest) {} @@ -108,6 +109,7 @@ contract CrosschainBase_v1_Test is ModuleTest { //Test the reinit function function testReinitFails() public override(ModuleTest) {} + // -----ALL below this we're keeping for reference, but not testing //-------------------------------------------------------------------------- //Test: Modifiers diff --git a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol index 1cacab263..5a2d38b7f 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol @@ -260,7 +260,111 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { crossChainManager.processPayments(client); } + //@zuhaib - let's add some tests for unhappy paths here + // testMaxFeeTooHigh() + // testInvalidTt() + // testInvavil + // Validate that these fuzz tests I wrote are actually helpful they may be redundant + + function testFuzz_ProcessPayments_SinglePayment( + address fuzzRecipient, + uint96 amount // Using uint96 to avoid overflow issues + ) public { + // Assumptions + vm.assume(fuzzRecipient != address(0)); + vm.assume(amount > 0 && amount < 1000 ether); // Keeping within our minted balance + + // Setup + address[] memory setupRecipients = new address[](1); + setupRecipients[0] = fuzzRecipient; + uint[] memory setupAmounts = new uint[](1); + setupAmounts[0] = amount; + + IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = + _createPaymentOrders(1, setupRecipients, setupAmounts); + paymentClient.addPaymentOrders(orders); + + // Expectations + vm.expectEmit(true, true, true, true); + emit PaymentProcessed(0, fuzzRecipient, address(token), amount); + + // Action + crossChainManager.processPayments( + IERC20PaymentClientBase_v1(address(paymentClient)) + ); + } + + function testFuzz_ProcessPayments_MultiplePayments( + uint8 numPayments, // Using uint8 to keep the number of payments reasonable + uint96 baseAmount // Base amount that will be varied for each payment + ) public { + // Assumptions + vm.assume(numPayments > 0 && numPayments <= 10); // Limiting max payments + vm.assume(baseAmount > 0 && baseAmount < 100 ether); // Ensuring total won't exceed balance + + // Setup + address[] memory setupRecipients = new address[](numPayments); + uint[] memory setupAmounts = new uint[](numPayments); + + for (uint i = 0; i < numPayments; i++) { + setupRecipients[i] = address(uint160(i + 1)); // Creating unique addresses + setupAmounts[i] = baseAmount + (i * 1 ether); // Varying amounts + } + + IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = + _createPaymentOrders(numPayments, setupRecipients, setupAmounts); + paymentClient.addPaymentOrders(orders); + + // Expectations + for (uint i = 0; i < numPayments; i++) { + vm.expectEmit(true, true, true, true); + emit PaymentProcessed( + i, setupRecipients[i], address(token), setupAmounts[i] + ); + } + + // Action + crossChainManager.processPayments( + IERC20PaymentClientBase_v1(address(paymentClient)) + ); + } + + function testFuzz_ProcessPayments_EdgeCaseAmounts(uint96 amount) public { + // Assumptions + vm.assume(amount > 0 && amount <= 1000 ether); + vm.assume(recipient != address(0)); + + // Setup - Clear existing balance + uint currentBalance = token.balanceOf(address(crossChainManager)); + if (currentBalance > 0) { + vm.prank(address(crossChainManager)); + token.transfer(address(1), currentBalance); + } + + // Setup - Mint exact amount needed + token.mint(address(crossChainManager), amount); + + address[] memory setupRecipients = new address[](1); + setupRecipients[0] = recipient; + uint[] memory setupAmounts = new uint[](1); + setupAmounts[0] = amount; + + IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = + _createPaymentOrders(1, setupRecipients, setupAmounts); + paymentClient.addPaymentOrders(orders); + + // Expectations + vm.expectEmit(true, true, true, true); + emit PaymentProcessed(0, recipient, address(token), amount); + + // Action + crossChainManager.processPayments( + IERC20PaymentClientBase_v1(address(paymentClient)) + ); + } + // Helper functions + function _createPaymentOrders( uint orderCount, address[] memory recipients, From ca1b6a82af12d664f8d11629cf2e1d468f12fdc6 Mon Sep 17 00:00:00 2001 From: Zuhaib Mohammed Date: Wed, 20 Nov 2024 05:18:01 -0800 Subject: [PATCH 38/93] refrator test file and add unit tests --- .../PP_Connext_Crosschain_v1.sol | 18 ++++ .../interfaces/ICrosschainBase_v1.sol | 6 ++ .../PP_Connext_Crosschain_v1_Test.t.sol | 97 +++++++++++++------ 3 files changed, 94 insertions(+), 27 deletions(-) diff --git a/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol b/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol index b092d31d6..d1c7c9692 100644 --- a/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol +++ b/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol @@ -82,6 +82,24 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { IERC20PaymentClientBase_v1.PaymentOrder memory order, bytes memory executionData ) internal returns (bytes32) { + // @zuhaib - lets add validation here for ttl in this function + // be sure to use the errors that were inherited from the base + // we can ust check that ttl is not 0 + // What should we do here about maxFee? + + if (executionData.length == 0) { + revert + ICrossChainBase_v1 + .Module__CrossChainBase_InvalidExecutionData(); + } + + if (order.amount == 0) { + revert ICrossChainBase_v1.Module__CrossChainBase__InvalidAmount(); + } + if (order.recipient == address(0)) { + revert ICrossChainBase_v1.Module__CrossChainBase__InvalidRecipient(); + } + (uint maxFee, uint ttl) = abi.decode(executionData, (uint, uint)); if (ttl == 0) { revert ICrossChainBase_v1.Module__CrossChainBase_InvalidTTL(); diff --git a/src/modules/paymentProcessor/interfaces/ICrosschainBase_v1.sol b/src/modules/paymentProcessor/interfaces/ICrosschainBase_v1.sol index 4deb7732c..17fd04707 100644 --- a/src/modules/paymentProcessor/interfaces/ICrosschainBase_v1.sol +++ b/src/modules/paymentProcessor/interfaces/ICrosschainBase_v1.sol @@ -46,4 +46,10 @@ interface ICrossChainBase_v1 { /// @notice Message verification failed error Module__CrossChainBase_MessageVerificationFailed(); + + /// @notice Invalid execution data + error Module__CrossChainBase_InvalidExecutionData(); + + /// @notice Invalid recipient + error Module__CrossChainBase__InvalidRecipient(); } diff --git a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol index 5a2d38b7f..d501b645c 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol @@ -16,6 +16,8 @@ import {CrosschainBase_v1_Exposed} from "test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1_Exposed.sol"; import {IERC20PaymentClientBase_v1} from "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; +import {ICrossChainBase_v1} from + "src/modules/paymentProcessor/interfaces/ICrosschainBase_v1.sol"; // Tests and Mocks import {Mock_EverclearPayment} from @@ -133,14 +135,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { } function test_ProcessPayments_singlePayment() public { - // Setup mock payment orders that will be returned by the mock - address[] memory setupRecipients = new address[](1); - setupRecipients[0] = recipient; - uint[] memory setupAmounts = new uint[](1); - setupAmounts[0] = 100 ether; - IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = - _createPaymentOrders(1, setupRecipients, setupAmounts); - paymentClient.addPaymentOrders(orders); + _setupSinglePayment(recipient, 100 ether); // Get the client interface IERC20PaymentClientBase_v1 client = @@ -202,16 +197,61 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { ); } - function test_returnsCorrectBridgeData() public { - // Setup mock payment orders that will be returned by the mock - address[] memory setupRecipients = new address[](1); - setupRecipients[0] = recipient; - uint[] memory setupAmounts = new uint[](1); - setupAmounts[0] = 100 ether; - IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = - _createPaymentOrders(1, setupRecipients, setupAmounts); - paymentClient.addPaymentOrders(orders); + function test_ProcessPayments_invalidExecutionData() public { + _setupSinglePayment(recipient, 100 ether); + // Get the client interface + IERC20PaymentClientBase_v1 client = + IERC20PaymentClientBase_v1(address(paymentClient)); + + // Process payments + vm.expectRevert(); + crossChainManager.processPayments(client, invalidExecutionData); + } + + function test_ProcessPayments_emptyExecutionData() public { + _setupSinglePayment(recipient, 100 ether); + // Get the client interface + IERC20PaymentClientBase_v1 client = + IERC20PaymentClientBase_v1(address(paymentClient)); + + // Process payments + vm.expectRevert( + ICrossChainBase_v1 + .Module__CrossChainBase_InvalidExecutionData + .selector + ); + crossChainManager.processPayments(client, bytes("")); + } + + function test_ProcessPayments_invalidRecipient() public { + _setupSinglePayment(address(0), 100 ether); + // Get the client interface + IERC20PaymentClientBase_v1 client = + IERC20PaymentClientBase_v1(address(paymentClient)); + + // Process payments + vm.expectRevert( + ICrossChainBase_v1.Module__CrossChainBase__InvalidRecipient.selector + ); + crossChainManager.processPayments(client, executionData); + } + + function test_ProcessPayments_invalidAmount() public { + _setupSinglePayment(recipient, 0); + // Get the client interface + IERC20PaymentClientBase_v1 client = + IERC20PaymentClientBase_v1(address(paymentClient)); + + // Process payments + vm.expectRevert( + ICrossChainBase_v1.Module__CrossChainBase__InvalidAmount.selector + ); + crossChainManager.processPayments(client, executionData); + } + + function test_returnsCorrectBridgeData() public { + _setupSinglePayment(recipient, 100 ether); // Get the client interface IERC20PaymentClientBase_v1 client = IERC20PaymentClientBase_v1(address(paymentClient)); @@ -237,15 +277,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { } function test_ProcessPayments_InsufficientBalance() public { - // Setup payment order with amount larger than processor's balance - address[] memory setupRecipients = new address[](1); - setupRecipients[0] = recipient; - uint[] memory setupAmounts = new uint[](1); - setupAmounts[0] = 2000 ether; // More than the 1000 ether minted in setup - IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = - _createPaymentOrders(1, setupRecipients, setupAmounts); - paymentClient.addPaymentOrders(orders); - + _setupSinglePayment(recipient, 2000 ether); IERC20PaymentClientBase_v1 client = IERC20PaymentClientBase_v1(address(paymentClient)); @@ -254,7 +286,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { IERC20Errors.ERC20InsufficientBalance.selector, address(this), token.balanceOf(address(crossChainManager)), - setupAmounts[0] + 2000 ether ) ); crossChainManager.processPayments(client); @@ -365,6 +397,17 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { // Helper functions + function _setupSinglePayment(address _recipient, uint _amount) internal { + address[] memory setupRecipients = new address[](1); + setupRecipients[0] = _recipient; + uint[] memory setupAmounts = new uint[](1); + setupAmounts[0] = _amount; + + IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = + _createPaymentOrders(1, setupRecipients, setupAmounts); + paymentClient.addPaymentOrders(orders); + } + function _createPaymentOrders( uint orderCount, address[] memory recipients, From 3468df9c7b4848981dd798fe7caa9049cdd05a68 Mon Sep 17 00:00:00 2001 From: Zuhaib Mohammed Date: Wed, 20 Nov 2024 02:22:13 -0800 Subject: [PATCH 39/93] verify executionData & add unit tests --- .../PP_Connext_Crosschain_v1.sol | 13 +++-- .../PP_Connext_Crosschain_v1_Test.t.sol | 51 +++++++++++++++---- 2 files changed, 47 insertions(+), 17 deletions(-) diff --git a/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol b/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol index d1c7c9692..6e4a8e3e4 100644 --- a/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol +++ b/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol @@ -46,13 +46,12 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { return abi.encode(intentId); } - function processPayments(IERC20PaymentClientBase_v1 client) - external - override - { - uint maxFee = 0; - uint ttl = 0; - bytes memory executionData = abi.encode(maxFee, ttl); + function processPayments( + IERC20PaymentClientBase_v1 client, + bytes memory executionData + ) external { + //override { + //@33audits - how do we handle the processPayments function, since after adding the executionData, we dont need to override it! // Collect orders from the client IERC20PaymentClientBase_v1.PaymentOrder[] memory orders; diff --git a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol index d501b645c..07312cc90 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol @@ -61,10 +61,18 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { address public mockEverClearSpoke; address public mockWeth; + uint maxFee = 0; + uint ttl = 0; + bytes executionData; + bytes invalidExecutionData; + function setUp() public { // Set the chainId chainId = block.chainid; + //Set the execution data + executionData = abi.encode(maxFee, ttl); + invalidExecutionData = abi.encode(address(0)); // Deploy token token = new ERC20Mock("Test Token", "TEST"); @@ -151,7 +159,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { ); // Process payments - crossChainManager.processPayments(client); + crossChainManager.processPayments(client, executionData); } function test_ProcessPayments_multiplePayment() public { @@ -182,13 +190,13 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { setupAmounts[i] ); } - crossChainManager.processPayments(client); + crossChainManager.processPayments(client, executionData); } function test_ProcessPayments_noPayments() public { // Process payments and verify _bridgeData mapping is not updated crossChainManager.processPayments( - IERC20PaymentClientBase_v1(address(paymentClient)) + IERC20PaymentClientBase_v1(address(paymentClient)), executionData ); assertTrue( keccak256(crossChainManager.getBridgeData(0)) @@ -198,7 +206,15 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { } function test_ProcessPayments_invalidExecutionData() public { - _setupSinglePayment(recipient, 100 ether); + // Setup mock payment orders that will be returned by the mock + address[] memory setupRecipients = new address[](1); + setupRecipients[0] = recipient; + uint[] memory setupAmounts = new uint[](1); + setupAmounts[0] = 100 ether; + IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = + _createPaymentOrders(1, setupRecipients, setupAmounts); + paymentClient.addPaymentOrders(orders); + // Get the client interface IERC20PaymentClientBase_v1 client = IERC20PaymentClientBase_v1(address(paymentClient)); @@ -208,6 +224,21 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { crossChainManager.processPayments(client, invalidExecutionData); } + function test_returnsCorrectBridgeData() public { + // Setup mock payment orders that will be returned by the mock + address[] memory setupRecipients = new address[](1); + setupRecipients[0] = recipient; + uint[] memory setupAmounts = new uint[](1); + setupAmounts[0] = 100 ether; + IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = + _createPaymentOrders(1, setupRecipients, setupAmounts); + paymentClient.addPaymentOrders(orders); + + // Process payments + vm.expectRevert(); + crossChainManager.processPayments(client, invalidExecutionData); + } + function test_ProcessPayments_emptyExecutionData() public { _setupSinglePayment(recipient, 100 ether); @@ -256,7 +287,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { IERC20PaymentClientBase_v1 client = IERC20PaymentClientBase_v1(address(paymentClient)); // Process payments and verify _bridgeData mapping is updated - crossChainManager.processPayments(client); + crossChainManager.processPayments(client, executionData); assertTrue( keccak256(crossChainManager.getBridgeData(0)) != keccak256(bytes("")), @@ -268,7 +299,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { IERC20PaymentClientBase_v1 client = IERC20PaymentClientBase_v1(address(paymentClient)); // Process payments and verify _bridgeData mapping is updated - crossChainManager.processPayments(client); + crossChainManager.processPayments(client, executionData); assertTrue( keccak256(crossChainManager.getBridgeData(0)) == keccak256(bytes("")), @@ -289,7 +320,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { 2000 ether ) ); - crossChainManager.processPayments(client); + crossChainManager.processPayments(client, executionData); } //@zuhaib - let's add some tests for unhappy paths here @@ -322,7 +353,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { // Action crossChainManager.processPayments( - IERC20PaymentClientBase_v1(address(paymentClient)) + IERC20PaymentClientBase_v1(address(paymentClient)), executionData ); } @@ -357,7 +388,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { // Action crossChainManager.processPayments( - IERC20PaymentClientBase_v1(address(paymentClient)) + IERC20PaymentClientBase_v1(address(paymentClient)), executionData ); } @@ -391,7 +422,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { // Action crossChainManager.processPayments( - IERC20PaymentClientBase_v1(address(paymentClient)) + IERC20PaymentClientBase_v1(address(paymentClient)), executionData ); } From 293db4fb24bfe0a12eace4da6aee41d296ac4aca Mon Sep 17 00:00:00 2001 From: Leandro Faria Date: Thu, 21 Nov 2024 18:56:29 -0500 Subject: [PATCH 40/93] add ttl validation --- .../bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol index 07312cc90..995845766 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol @@ -62,7 +62,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { address public mockWeth; uint maxFee = 0; - uint ttl = 0; + uint ttl = 1; bytes executionData; bytes invalidExecutionData; @@ -224,7 +224,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { crossChainManager.processPayments(client, invalidExecutionData); } - function test_returnsCorrectBridgeData() public { + function test_returnsCorrectBridgeDataRevert() public { // Setup mock payment orders that will be returned by the mock address[] memory setupRecipients = new address[](1); setupRecipients[0] = recipient; @@ -234,6 +234,9 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { _createPaymentOrders(1, setupRecipients, setupAmounts); paymentClient.addPaymentOrders(orders); + IERC20PaymentClientBase_v1 client = + IERC20PaymentClientBase_v1(address(paymentClient)); + // Process payments vm.expectRevert(); crossChainManager.processPayments(client, invalidExecutionData); From e96b3fb9ab175c14e612e59dc6d906872fa44135 Mon Sep 17 00:00:00 2001 From: Zuhaib Mohammed Date: Thu, 21 Nov 2024 22:23:14 -0800 Subject: [PATCH 41/93] refractor unit tests --- .../PP_Connext_Crosschain_v1.sol | 9 +++-- .../PP_Connext_Crosschain_v1_Test.t.sol | 38 ++++++------------- 2 files changed, 16 insertions(+), 31 deletions(-) diff --git a/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol b/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol index 6e4a8e3e4..bfdba7ede 100644 --- a/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol +++ b/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol @@ -83,7 +83,7 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { ) internal returns (bytes32) { // @zuhaib - lets add validation here for ttl in this function // be sure to use the errors that were inherited from the base - // we can ust check that ttl is not 0 + // we can ust check that ttl is not 0 -> @33audits - in case of everclear, both maxFee and ttl can be zero, please check https://docs.everclear.org/developers/guides/xerc20#newintent-called-on-spoke-contract // What should we do here about maxFee? if (executionData.length == 0) { @@ -100,9 +100,10 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { } (uint maxFee, uint ttl) = abi.decode(executionData, (uint, uint)); - if (ttl == 0) { - revert ICrossChainBase_v1.Module__CrossChainBase_InvalidTTL(); - } + // if (ttl == 0) { + // revert ICrossChainBase_v1.Module__CrossChainBase_InvalidTTL(); + // } @33audits - in case of everclear, both maxFee and ttl can be zero, please check https://docs.everclear.org/developers/guides/xerc20#newintent-called-on-spoke-contract + // Wrap ETH into WETH to send with the xcall IERC20(order.paymentToken).transferFrom( msg.sender, address(this), order.amount diff --git a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol index 995845766..76a2a69a1 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol @@ -62,7 +62,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { address public mockWeth; uint maxFee = 0; - uint ttl = 1; + uint ttl = 0; bytes executionData; bytes invalidExecutionData; @@ -225,15 +225,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { } function test_returnsCorrectBridgeDataRevert() public { - // Setup mock payment orders that will be returned by the mock - address[] memory setupRecipients = new address[](1); - setupRecipients[0] = recipient; - uint[] memory setupAmounts = new uint[](1); - setupAmounts[0] = 100 ether; - IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = - _createPaymentOrders(1, setupRecipients, setupAmounts); - paymentClient.addPaymentOrders(orders); - + _setupSinglePayment(recipient, 100 ether); IERC20PaymentClientBase_v1 client = IERC20PaymentClientBase_v1(address(paymentClient)); @@ -296,6 +288,12 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { != keccak256(bytes("")), "Bridge data should not be empty" ); + + bytes32 intentId = bytes32(crossChainManager.getBridgeData(0)); + assertEq( + uint(everclearPaymentMock.status(intentId)), + uint(Mock_EverclearPayment.IntentStatus.ADDED) + ); } function test_returnsEmptyBridgeData() public { @@ -329,7 +327,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { //@zuhaib - let's add some tests for unhappy paths here // testMaxFeeTooHigh() // testInvalidTt() - // testInvavil + // testInvavil @33audits - in case of everclear, both maxFee and ttl can be zero, please check https://docs.everclear.org/developers/guides/xerc20#newintent-called-on-spoke-contract // Validate that these fuzz tests I wrote are actually helpful they may be redundant function testFuzz_ProcessPayments_SinglePayment( @@ -341,14 +339,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { vm.assume(amount > 0 && amount < 1000 ether); // Keeping within our minted balance // Setup - address[] memory setupRecipients = new address[](1); - setupRecipients[0] = fuzzRecipient; - uint[] memory setupAmounts = new uint[](1); - setupAmounts[0] = amount; - - IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = - _createPaymentOrders(1, setupRecipients, setupAmounts); - paymentClient.addPaymentOrders(orders); + _setupSinglePayment(fuzzRecipient, amount); // Expectations vm.expectEmit(true, true, true, true); @@ -410,14 +401,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { // Setup - Mint exact amount needed token.mint(address(crossChainManager), amount); - address[] memory setupRecipients = new address[](1); - setupRecipients[0] = recipient; - uint[] memory setupAmounts = new uint[](1); - setupAmounts[0] = amount; - - IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = - _createPaymentOrders(1, setupRecipients, setupAmounts); - paymentClient.addPaymentOrders(orders); + _setupSinglePayment(recipient, amount); // Expectations vm.expectEmit(true, true, true, true); From 45816bf8988f216df8a0fc23b08fea2aed2d0fd2 Mon Sep 17 00:00:00 2001 From: Zuhaib Mohammed Date: Fri, 22 Nov 2024 05:18:00 -0800 Subject: [PATCH 42/93] add unit tests CrosschainBase_v1 --- .../abstracts/CrosschainBase_v1.t.sol | 79 +++++++++++++++++-- .../PP_Connext_Crosschain_v1_Test.t.sol | 27 ++++++- 2 files changed, 99 insertions(+), 7 deletions(-) diff --git a/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.t.sol b/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.t.sol index c3c9e2954..a9128e07f 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.t.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.t.sol @@ -35,6 +35,7 @@ import {IPaymentProcessor_v1} from "src/orchestrator/interfaces/IOrchestrator_v1.sol"; import {ICrossChainBase_v1} from "src/modules/paymentProcessor/interfaces/ICrosschainBase_v1.sol"; +import {OZErrors} from "test/utils/errors/OZErrors.sol"; /** * @title Inverter Template Payment Processor Tests @@ -66,8 +67,8 @@ contract CrosschainBase_v1_Test is ModuleTest { ERC20PaymentClientBaseV1Mock paymentClient; //System under test (SuT) - CrosschainBase_v1 public paymentProcessor; - //PP_CrossChain_v1_Exposed public paymentProcessor; + //CrosschainBase_v1 public paymentProcessor; + CrosschainBase_v1_Exposed public paymentProcessor; //-------------------------------------------------------------------------- //Setup @@ -75,7 +76,7 @@ contract CrosschainBase_v1_Test is ModuleTest { //This function is used to setup the unit test //Deploy the SuT address impl = address(new CrosschainBase_v1_Exposed(block.chainid)); - paymentProcessor = CrosschainBase_v1(Clones.clone(impl)); + paymentProcessor = CrosschainBase_v1_Exposed(Clones.clone(impl)); //Setup the module to test _setUpOrchestrator(paymentProcessor); @@ -83,6 +84,9 @@ contract CrosschainBase_v1_Test is ModuleTest { //General setup for other contracts in the workflow _authorizer.setIsAuthorized(address(this), true); + //Initiate the PP with the medata and config data + paymentProcessor.init(_orchestrator, _METADATA, abi.encode(1)); + //Setup other modules needed in the unit tests. //In this case a payment client is needed to test the PP_Template_v1. impl = address(new ERC20PaymentClientBaseV1Mock()); @@ -102,13 +106,47 @@ contract CrosschainBase_v1_Test is ModuleTest { //@zuhaib - we need some basic tests for the base, we just need to init it and //make sure that the executeBridgeTransfer is correctly set and returns empty bytes //Test if the orchestrator is correctly set - function testInit() public override(ModuleTest) {} + function testInit() public override(ModuleTest) { + assertEq( + address(paymentProcessor.orchestrator()), address(_orchestrator) + ); + } //Test the interface support - function testSupportsInterface() public {} + function testSupportsInterface() public { + // Test for ICrossChainBase_v1 interface support + bytes4 interfaceId = type(ICrossChainBase_v1).interfaceId; + assertTrue(paymentProcessor.supportsInterface(interfaceId)); + + // Test for random interface ID (should return false) + bytes4 randomInterfaceId = bytes4(keccak256("random()")); + assertFalse(paymentProcessor.supportsInterface(randomInterfaceId)); + } //Test the reinit function - function testReinitFails() public override(ModuleTest) {} + function testReinitFails() public override(ModuleTest) { + vm.expectRevert(OZErrors.Initializable__InvalidInitialization); + paymentProcessor.init(_orchestrator, _METADATA, abi.encode(1)); + } + + //Test executeBridgeTransfer returns empty bytes + function testExecuteBridgeTransfer() public { + address[] memory setupRecipients = new address[](1); + setupRecipients[0] = address(1); + uint[] memory setupAmounts = new uint[](1); + setupAmounts[0] = 100 ether; + + IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = + _createPaymentOrders(1, setupRecipients, setupAmounts); + paymentClient.addPaymentOrders(orders); + + bytes memory executionData = abi.encode(0, 0); //maxFee and ttl setup + + bytes memory result = paymentProcessor.exposed_executeBridgeTransfer( + orders[0], executionData + ); + assertEq(result, bytes("")); + } // -----ALL below this we're keeping for reference, but not testing //-------------------------------------------------------------------------- @@ -157,4 +195,33 @@ contract CrosschainBase_v1_Test is ModuleTest { //-------------------------------------------------------------------------- //Helper Functions + + function _createPaymentOrders( + uint orderCount, + address[] memory recipients, + uint[] memory amounts + ) + internal + view + returns (IERC20PaymentClientBase_v1.PaymentOrder[] memory) + { + // Sanity checks for array lengths + require( + recipients.length == orderCount && amounts.length == orderCount, + "Array lengths must match orderCount" + ); + IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = + new IERC20PaymentClientBase_v1.PaymentOrder[](orderCount); + for (uint i = 0; i < orderCount; i++) { + orders[i] = IERC20PaymentClientBase_v1.PaymentOrder({ + recipient: recipients[i], + paymentToken: address(0xabcd), + amount: amounts[i], + start: block.timestamp, + cliff: 0, + end: block.timestamp + 1 days + }); + } + return orders; + } } diff --git a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol index 76a2a69a1..66738a382 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol @@ -137,6 +137,23 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { ); } + function testSupportsInterface() public { + // Test for IModule_v1 interface + assertTrue( + crossChainManager.supportsInterface(type(IModule_v1).interfaceId) + ); + + // Test for ICrossChainBase_v1 interface + assertTrue( + crossChainManager.supportsInterface( + type(ICrossChainBase_v1).interfaceId + ) + ); + + // Test for a non-supported interface (using a random interface ID) + assertFalse(crossChainManager.supportsInterface(0xffffffff)); + } + function testReinitFails() public override(ModuleTest) { vm.expectRevert(OZErrors.Initializable__InvalidInitialization); paymentProcessor.init(_orchestrator, _METADATA, abi.encode(1)); @@ -225,7 +242,15 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { } function test_returnsCorrectBridgeDataRevert() public { - _setupSinglePayment(recipient, 100 ether); + // Setup mock payment orders that will be returned by the mock + address[] memory setupRecipients = new address[](1); + setupRecipients[0] = recipient; + uint[] memory setupAmounts = new uint[](1); + setupAmounts[0] = 100 ether; + IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = + _createPaymentOrders(1, setupRecipients, setupAmounts); + paymentClient.addPaymentOrders(orders); + IERC20PaymentClientBase_v1 client = IERC20PaymentClientBase_v1(address(paymentClient)); From f460be354c276ace4bc18779861b9bd2bfbb8cf3 Mon Sep 17 00:00:00 2001 From: Zuhaib Mohammed Date: Mon, 25 Nov 2024 04:58:08 -0800 Subject: [PATCH 43/93] singlePayment fuzz tests added --- .../PP_Connext_Crosschain_v1_Test.t.sol | 141 ++++++++++-------- 1 file changed, 76 insertions(+), 65 deletions(-) diff --git a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol index 66738a382..90485923c 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol @@ -44,8 +44,6 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { ERC20PaymentClientBaseV1Mock paymentClient; CrosschainBase_v1 public paymentProcessor; - // Test addresses - address public recipient = address(0x123); uint public chainId; // Add this event definition at the contract level @@ -66,6 +64,8 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { bytes executionData; bytes invalidExecutionData; + uint constant MINTED_SUPPLY = 1000 ether; + function setUp() public { // Set the chainId chainId = block.chainid; @@ -122,11 +122,11 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { paymentClient.setIsAuthorized(address(crossChainManager), true); // Setup token approvals and initial balances - token.mint(address(this), 1000 ether); + token.mint(address(this), MINTED_SUPPLY); token.approve(address(crossChainManager), type(uint).max); // Add these lines to ensure proper token flow - token.mint(address(crossChainManager), 1000 ether); // Mint tokens to processor + token.mint(address(crossChainManager), MINTED_SUPPLY); // Mint tokens to processor vm.prank(address(crossChainManager)); token.approve(address(crossChainManager), type(uint).max); // Processor approves bridge logic } @@ -159,8 +159,14 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { paymentProcessor.init(_orchestrator, _METADATA, abi.encode(1)); } - function test_ProcessPayments_singlePayment() public { - _setupSinglePayment(recipient, 100 ether); + function testFuzz_ProcessPayments_singlePayment( + address testRecipient, + uint testAmount + ) public { + vm.assume(testRecipient != address(0)); + vm.assume(testAmount > 0 && testAmount < MINTED_SUPPLY); // Keeping within our minted balance + + _setupSinglePayment(testRecipient, testAmount); // Get the client interface IERC20PaymentClientBase_v1 client = @@ -170,9 +176,9 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { vm.expectEmit(true, true, true, true); emit PaymentProcessed( 0, // paymentId - recipient, + testRecipient, address(token), - 100 ether + testAmount ); // Process payments @@ -222,15 +228,14 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { ); } - function test_ProcessPayments_invalidExecutionData() public { - // Setup mock payment orders that will be returned by the mock - address[] memory setupRecipients = new address[](1); - setupRecipients[0] = recipient; - uint[] memory setupAmounts = new uint[](1); - setupAmounts[0] = 100 ether; - IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = - _createPaymentOrders(1, setupRecipients, setupAmounts); - paymentClient.addPaymentOrders(orders); + function testFuzz_ProcessPayments_invalidExecutionData( + address testRecipient, + uint testAmount + ) public { + vm.assume(testRecipient != address(0)); + vm.assume(testAmount > 0 && testAmount < MINTED_SUPPLY); // Keeping within our minted balance + + _setupSinglePayment(testRecipient, testAmount); // Get the client interface IERC20PaymentClientBase_v1 client = @@ -241,15 +246,13 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { crossChainManager.processPayments(client, invalidExecutionData); } - function test_returnsCorrectBridgeDataRevert() public { - // Setup mock payment orders that will be returned by the mock - address[] memory setupRecipients = new address[](1); - setupRecipients[0] = recipient; - uint[] memory setupAmounts = new uint[](1); - setupAmounts[0] = 100 ether; - IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = - _createPaymentOrders(1, setupRecipients, setupAmounts); - paymentClient.addPaymentOrders(orders); + function testFuzz_returnsCorrectBridgeDataRevert( + address testRecipient, + uint testAmount + ) public { + vm.assume(testRecipient != address(0)); + vm.assume(testAmount > 0 && testAmount < MINTED_SUPPLY); // Keeping within our minted balance + _setupSinglePayment(testRecipient, testAmount); IERC20PaymentClientBase_v1 client = IERC20PaymentClientBase_v1(address(paymentClient)); @@ -259,8 +262,14 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { crossChainManager.processPayments(client, invalidExecutionData); } - function test_ProcessPayments_emptyExecutionData() public { - _setupSinglePayment(recipient, 100 ether); + function testFuzz_ProcessPayments_emptyExecutionData( + address testRecipient, + uint testAmount + ) public { + vm.assume(testRecipient != address(0)); + vm.assume(testAmount > 0 && testAmount < MINTED_SUPPLY); // Keeping within our minted balance + + _setupSinglePayment(testRecipient, testAmount); // Get the client interface IERC20PaymentClientBase_v1 client = @@ -275,8 +284,12 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { crossChainManager.processPayments(client, bytes("")); } - function test_ProcessPayments_invalidRecipient() public { - _setupSinglePayment(address(0), 100 ether); + function testFuzz_ProcessPayments_invalidRecipient(uint testAmount) + public + { + vm.assume(testAmount > 0 && testAmount < MINTED_SUPPLY); // Keeping within our minted balance + + _setupSinglePayment(address(0), testAmount); // Get the client interface IERC20PaymentClientBase_v1 client = IERC20PaymentClientBase_v1(address(paymentClient)); @@ -288,8 +301,12 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { crossChainManager.processPayments(client, executionData); } - function test_ProcessPayments_invalidAmount() public { - _setupSinglePayment(recipient, 0); + function testFuzz_ProcessPayments_invalidAmount(address testRecipient) + public + { + vm.assume(testRecipient != address(0)); + + _setupSinglePayment(testRecipient, 0); // Get the client interface IERC20PaymentClientBase_v1 client = IERC20PaymentClientBase_v1(address(paymentClient)); @@ -301,8 +318,14 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { crossChainManager.processPayments(client, executionData); } - function test_returnsCorrectBridgeData() public { - _setupSinglePayment(recipient, 100 ether); + function testFuzz_returnsCorrectBridgeData( + address testRecipient, + uint testAmount + ) public { + vm.assume(testRecipient != address(0)); + vm.assume(testAmount > 0 && testAmount < MINTED_SUPPLY); // Keeping within our minted balance + + _setupSinglePayment(testRecipient, testAmount); // Get the client interface IERC20PaymentClientBase_v1 client = IERC20PaymentClientBase_v1(address(paymentClient)); @@ -333,8 +356,14 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { ); } - function test_ProcessPayments_InsufficientBalance() public { - _setupSinglePayment(recipient, 2000 ether); + function testFuzz_ProcessPayments_InsufficientBalance( + address testRecipient, + uint testAmount + ) public { + vm.assume(testRecipient != address(0)); + vm.assume(testAmount > MINTED_SUPPLY && testAmount <= type(uint96).max); + + _setupSinglePayment(testRecipient, testAmount); IERC20PaymentClientBase_v1 client = IERC20PaymentClientBase_v1(address(paymentClient)); @@ -343,7 +372,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { IERC20Errors.ERC20InsufficientBalance.selector, address(this), token.balanceOf(address(crossChainManager)), - 2000 ether + testAmount ) ); crossChainManager.processPayments(client, executionData); @@ -355,27 +384,6 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { // testInvavil @33audits - in case of everclear, both maxFee and ttl can be zero, please check https://docs.everclear.org/developers/guides/xerc20#newintent-called-on-spoke-contract // Validate that these fuzz tests I wrote are actually helpful they may be redundant - function testFuzz_ProcessPayments_SinglePayment( - address fuzzRecipient, - uint96 amount // Using uint96 to avoid overflow issues - ) public { - // Assumptions - vm.assume(fuzzRecipient != address(0)); - vm.assume(amount > 0 && amount < 1000 ether); // Keeping within our minted balance - - // Setup - _setupSinglePayment(fuzzRecipient, amount); - - // Expectations - vm.expectEmit(true, true, true, true); - emit PaymentProcessed(0, fuzzRecipient, address(token), amount); - - // Action - crossChainManager.processPayments( - IERC20PaymentClientBase_v1(address(paymentClient)), executionData - ); - } - function testFuzz_ProcessPayments_MultiplePayments( uint8 numPayments, // Using uint8 to keep the number of payments reasonable uint96 baseAmount // Base amount that will be varied for each payment @@ -411,10 +419,13 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { ); } - function testFuzz_ProcessPayments_EdgeCaseAmounts(uint96 amount) public { - // Assumptions - vm.assume(amount > 0 && amount <= 1000 ether); - vm.assume(recipient != address(0)); + function testFuzz_ProcessPayments_EdgeCaseAmounts( + address testRecipient, + uint96 testAmount + ) public { + vm.assume(testAmount > 0 && testAmount <= MINTED_SUPPLY); + // Assumption + vm.assume(testRecipient != address(0)); // Setup - Clear existing balance uint currentBalance = token.balanceOf(address(crossChainManager)); @@ -424,13 +435,13 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { } // Setup - Mint exact amount needed - token.mint(address(crossChainManager), amount); + token.mint(address(crossChainManager), testAmount); - _setupSinglePayment(recipient, amount); + _setupSinglePayment(testRecipient, testAmount); // Expectations vm.expectEmit(true, true, true, true); - emit PaymentProcessed(0, recipient, address(token), amount); + emit PaymentProcessed(0, testRecipient, address(token), testAmount); // Action crossChainManager.processPayments( From c251997c6c9f7cd99d34f89512afbd144846ad70 Mon Sep 17 00:00:00 2001 From: Zuhaib Mohammed Date: Mon, 25 Nov 2024 05:54:05 -0800 Subject: [PATCH 44/93] move balance setup internal function --- .../PP_Connext_Crosschain_v1_Test.t.sol | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol index 90485923c..9dcd3ce01 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol @@ -121,14 +121,8 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { crossChainManager.init(_orchestrator, _METADATA, configData); paymentClient.setIsAuthorized(address(crossChainManager), true); - // Setup token approvals and initial balances - token.mint(address(this), MINTED_SUPPLY); - token.approve(address(crossChainManager), type(uint).max); - - // Add these lines to ensure proper token flow - token.mint(address(crossChainManager), MINTED_SUPPLY); // Mint tokens to processor - vm.prank(address(crossChainManager)); - token.approve(address(crossChainManager), type(uint).max); // Processor approves bridge logic + // Call the new function for balances setup + _setupInitialBalances(); } function testInit() public override(ModuleTest) { @@ -490,4 +484,14 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { } return orders; } + + function _setupInitialBalances() internal { + // Setup token approvals and initial balances + token.mint(address(this), MINTED_SUPPLY); + token.approve(address(crossChainManager), type(uint).max); + + token.mint(address(crossChainManager), MINTED_SUPPLY); // Mint tokens to processor + vm.prank(address(crossChainManager)); + token.approve(address(crossChainManager), type(uint).max); // Processor approves bridge logic + } } From 01f5ff8703ad85b9bd04df342468a153cf9c3988 Mon Sep 17 00:00:00 2001 From: Zuhaib Mohammed Date: Mon, 25 Nov 2024 07:38:30 -0800 Subject: [PATCH 45/93] multiplePayment fuzz tests added --- .../PP_Connext_Crosschain_v1_Test.t.sol | 75 +++++++------------ 1 file changed, 26 insertions(+), 49 deletions(-) diff --git a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol index 9dcd3ce01..952adc451 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol @@ -179,26 +179,36 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { crossChainManager.processPayments(client, executionData); } - function test_ProcessPayments_multiplePayment() public { - // Setup mock payment orders that will be returned by the mock - address[] memory setupRecipients = new address[](3); - setupRecipients[0] = address(0x1); - setupRecipients[1] = address(0x2); - setupRecipients[2] = address(0x3); - uint[] memory setupAmounts = new uint[](3); - setupAmounts[0] = 100 ether; - setupAmounts[1] = 125 ether; - setupAmounts[2] = 150 ether; + function testFuzz_ProcessPayments_multiplePayment( + uint8 numRecipients, + address testRecipient, + uint96 baseAmount + ) public { + // Assumptions to keep the test manageable and within bounds + vm.assume(numRecipients > 0 && numRecipients <= type(uint8).max); // Limit array size + vm.assume(testRecipient != address(0)); + vm.assume(baseAmount > 0 && baseAmount <= MINTED_SUPPLY / numRecipients); // Ensure total amount won't exceed MINTED_SUPPLY + + // Setup mock payment orders + address[] memory setupRecipients = new address[](numRecipients); + uint[] memory setupAmounts = new uint[](numRecipients); + + for (uint i = 0; i < numRecipients; i++) { + setupRecipients[i] = testRecipient; + setupAmounts[i] = + 1 + (uint(keccak256(abi.encode(i, baseAmount))) % baseAmount); + } + IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = - _createPaymentOrders(3, setupRecipients, setupAmounts); + _createPaymentOrders(numRecipients, setupRecipients, setupAmounts); paymentClient.addPaymentOrders(orders); + // Get the client interface IERC20PaymentClientBase_v1 client = IERC20PaymentClientBase_v1(address(paymentClient)); - // Process payments and verify _bridgeData mapping is updated for each paymentId - // Expect the event - for (uint i = 0; i < setupRecipients.length; i++) { + // Expect events for each payment + for (uint i = 0; i < numRecipients; i++) { vm.expectEmit(true, true, true, true); emit PaymentProcessed( i, // paymentId @@ -207,6 +217,8 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { setupAmounts[i] ); } + + // Process payments crossChainManager.processPayments(client, executionData); } @@ -378,41 +390,6 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { // testInvavil @33audits - in case of everclear, both maxFee and ttl can be zero, please check https://docs.everclear.org/developers/guides/xerc20#newintent-called-on-spoke-contract // Validate that these fuzz tests I wrote are actually helpful they may be redundant - function testFuzz_ProcessPayments_MultiplePayments( - uint8 numPayments, // Using uint8 to keep the number of payments reasonable - uint96 baseAmount // Base amount that will be varied for each payment - ) public { - // Assumptions - vm.assume(numPayments > 0 && numPayments <= 10); // Limiting max payments - vm.assume(baseAmount > 0 && baseAmount < 100 ether); // Ensuring total won't exceed balance - - // Setup - address[] memory setupRecipients = new address[](numPayments); - uint[] memory setupAmounts = new uint[](numPayments); - - for (uint i = 0; i < numPayments; i++) { - setupRecipients[i] = address(uint160(i + 1)); // Creating unique addresses - setupAmounts[i] = baseAmount + (i * 1 ether); // Varying amounts - } - - IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = - _createPaymentOrders(numPayments, setupRecipients, setupAmounts); - paymentClient.addPaymentOrders(orders); - - // Expectations - for (uint i = 0; i < numPayments; i++) { - vm.expectEmit(true, true, true, true); - emit PaymentProcessed( - i, setupRecipients[i], address(token), setupAmounts[i] - ); - } - - // Action - crossChainManager.processPayments( - IERC20PaymentClientBase_v1(address(paymentClient)), executionData - ); - } - function testFuzz_ProcessPayments_EdgeCaseAmounts( address testRecipient, uint96 testAmount From 8e0b414ffc4767830085544653d9d5e4d3173626 Mon Sep 17 00:00:00 2001 From: Zuhaib Mohammed Date: Mon, 25 Nov 2024 21:32:40 -0800 Subject: [PATCH 46/93] camelcase changes for CrossChainBase contract --- .../PP_Connext_Crosschain_v1.sol | 4 +- .../abstracts/CrossChainBase_v1.sol | 92 +++++++++++++++++++ .../abstracts/CrosschainBase_v1.sol | 2 +- .../abstracts/PP_Crosschain_v1.sol | 8 +- ...nBase_v1.t.sol => CrossChainBase_v1.t.sol} | 16 ++-- ...osed.sol => CrossChainBase_v1_Exposed.sol} | 10 +- .../PP_Connext_Crosschain_v1_Test.t.sol | 14 +-- 7 files changed, 119 insertions(+), 27 deletions(-) create mode 100644 src/modules/paymentProcessor/abstracts/CrossChainBase_v1.sol rename test/modules/paymentProcessor/bridging/abstracts/{CrosschainBase_v1.t.sol => CrossChainBase_v1.t.sol} (94%) rename test/modules/paymentProcessor/bridging/abstracts/{CrosschainBase_v1_Exposed.sol => CrossChainBase_v1_Exposed.sol} (70%) diff --git a/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol b/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol index bfdba7ede..f0dba4512 100644 --- a/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol +++ b/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol @@ -1,7 +1,7 @@ pragma solidity ^0.8.20; -import {CrosschainBase_v1} from - "src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol"; +import {CrossChainBase_v1} from + "src/modules/paymentProcessor/abstracts/CrossChainBase_v1.sol"; import {ICrossChainBase_v1} from "src/modules/paymentProcessor/interfaces/ICrosschainBase_v1.sol"; import {IERC20PaymentClientBase_v1} from diff --git a/src/modules/paymentProcessor/abstracts/CrossChainBase_v1.sol b/src/modules/paymentProcessor/abstracts/CrossChainBase_v1.sol new file mode 100644 index 000000000..78f987b6e --- /dev/null +++ b/src/modules/paymentProcessor/abstracts/CrossChainBase_v1.sol @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity 0.8.23; + +import {IOrchestrator_v1} from + "src/orchestrator/interfaces/IOrchestrator_v1.sol"; +import {IPaymentProcessor_v1} from "@pp/IPaymentProcessor_v1.sol"; +import {IERC20PaymentClientBase_v1} from + "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; +import {Module_v1} from "src/modules/base/Module_v1.sol"; +//import {IPP_CrossChain_v1} from "./IPP_Template_v1.sol"; +import {ERC165Upgradeable, Module_v1} from "src/modules/base/Module_v1.sol"; +import {ICrossChainBase_v1} from "../interfaces/ICrosschainBase_v1.sol"; +/** + * @title Inverter Template Payment Processor + * + * @notice Basic template payment processor used as base for developing new payment processors. + * + * @dev This contract is used to showcase a basic setup for a payment processor. The contract showcases the + * following: + * - Inherit from the Module_v1 contract to enable interaction with the Inverter workflow. + * - Use of the IPaymentProcessor_v1 interface to facilitate interaction with a payment client. + * - Implement custom interface which has all the public facing functions, errors, events and structs. + * - Pre-defined layout for all contract functions, modifiers, state variables etc. + * - Use of the ERC165Upgradeable contract to check for interface support. + * + * @custom:security-contact security@inverter.network + * In case of any concerns or findings, please refer to our Security Policy + * at security.inverter.network or email us directly! + * + * @author Inverter Network + */ + +abstract contract CrossChainBase_v1 is ICrossChainBase_v1, Module_v1 { + mapping(uint => bytes) internal _bridgeData; + /// @inheritdoc ERC165Upgradeable + + function supportsInterface(bytes4 interfaceId_) + public + view + virtual + override(Module_v1) + returns (bool) + { + return interfaceId_ == type(ICrossChainBase_v1).interfaceId + || super.supportsInterface(interfaceId_); + } + //-------------------------------------------------------------------------- + // State + + /// @dev The number of payment orders. + uint internal _paymentId; + + //-------------------------------------------------------------------------- + // Events + + event PaymentProcessed( + uint indexed paymentId, address recipient, address token, uint amount + ); + + //-------------------------------------------------------------------------- + // Virtual Functions + + /// @notice Execute the cross-chain bridge transfer + /// @dev Override this function to implement specific bridge logic + /// @param order The payment order containing all necessary transfer details + /// @return bridgeData Arbitrary data returned by the bridge implementation + function _executeBridgeTransfer( + IERC20PaymentClientBase_v1.PaymentOrder memory order, + bytes memory executionData + ) internal virtual returns (bytes memory) { + emit BridgeTransferExecuted(executionData); + return bytes(""); + } + /// @notice Process payments for a given payment client + /// @param client The payment client to process payments for + + function processPayments(IERC20PaymentClientBase_v1 client) + external + virtual + {} + + /// @notice Get the bridge data for a given payment ID + /// @param paymentId The ID of the payment to get the bridge data for + /// @return The bridge data for the given payment ID + function getBridgeData(uint paymentId) + external + view + returns (bytes memory) + { + return _bridgeData[paymentId]; + } +} diff --git a/src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol b/src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol index 1578c1682..78f987b6e 100644 --- a/src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol +++ b/src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol @@ -30,7 +30,7 @@ import {ICrossChainBase_v1} from "../interfaces/ICrosschainBase_v1.sol"; * @author Inverter Network */ -abstract contract CrosschainBase_v1 is ICrossChainBase_v1, Module_v1 { +abstract contract CrossChainBase_v1 is ICrossChainBase_v1, Module_v1 { mapping(uint => bytes) internal _bridgeData; /// @inheritdoc ERC165Upgradeable diff --git a/src/modules/paymentProcessor/abstracts/PP_Crosschain_v1.sol b/src/modules/paymentProcessor/abstracts/PP_Crosschain_v1.sol index 540fcbe0e..ebe49d1b2 100644 --- a/src/modules/paymentProcessor/abstracts/PP_Crosschain_v1.sol +++ b/src/modules/paymentProcessor/abstracts/PP_Crosschain_v1.sol @@ -8,7 +8,7 @@ import {IERC20PaymentClientBase_v1} from "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; import {ERC165Upgradeable, Module_v1} from "src/modules/base/Module_v1.sol"; -import {CrosschainBase_v1} from "./CrosschainBase_v1.sol"; +import {CrossChainBase_v1} from "./CrossChainBase_v1.sol"; // External Interfaces import {IERC20} from "@oz/token/ERC20/IERC20.sol"; @@ -41,13 +41,13 @@ import {IPP_Crosschain_v1} from "../interfaces/IPP_Crosschain_v1.sol"; * * @author Inverter Network */ -abstract contract PP_Crosschain_v1 is CrosschainBase_v1, IPP_Crosschain_v1 { +abstract contract PP_Crosschain_v1 is CrossChainBase_v1, IPP_Crosschain_v1 { /// @inheritdoc ERC165Upgradeable function supportsInterface(bytes4 interfaceId_) public view virtual - override(CrosschainBase_v1) + override(CrossChainBase_v1) returns (bool) { return interfaceId_ == type(IPP_Crosschain_v1).interfaceId @@ -89,7 +89,7 @@ abstract contract PP_Crosschain_v1 is CrosschainBase_v1, IPP_Crosschain_v1 { function processPayments(IERC20PaymentClientBase_v1 client) external virtual - override(IPaymentProcessor_v1, CrosschainBase_v1) + override(IPaymentProcessor_v1, CrossChainBase_v1) {} /// @inheritdoc IPaymentProcessor_v1 diff --git a/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.t.sol b/test/modules/paymentProcessor/bridging/abstracts/CrossChainBase_v1.t.sol similarity index 94% rename from test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.t.sol rename to test/modules/paymentProcessor/bridging/abstracts/CrossChainBase_v1.t.sol index a9128e07f..33975b053 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1.t.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/CrossChainBase_v1.t.sol @@ -10,8 +10,8 @@ import { import {OZErrors} from "test/utils/errors/OZErrors.sol"; import {ICrossChainBase_v1} from "src/modules/paymentProcessor/interfaces/ICrosschainBase_v1.sol"; -import {CrosschainBase_v1} from - "src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol"; +import {CrossChainBase_v1} from + "src/modules/paymentProcessor/abstracts/CrossChainBase_v1.sol"; //External Dependencies import {Clones} from "@oz/proxy/Clones.sol"; @@ -23,7 +23,7 @@ import { ERC20Mock } from "test/utils/mocks/modules/paymentClient/ERC20PaymentClientBaseV1Mock.sol"; //import exposed -import {CrosschainBase_v1_Exposed} from "./CrosschainBase_v1_Exposed.sol"; +import {CrossChainBase_v1_Exposed} from "./CrossChainBase_v1_Exposed.sol"; //System under test (SuT) // import { @@ -57,7 +57,7 @@ import {OZErrors} from "test/utils/errors/OZErrors.sol"; * * @author Inverter Network */ -contract CrosschainBase_v1_Test is ModuleTest { +contract CrossChainBase_v1_Test is ModuleTest { //-------------------------------------------------------------------------- //Constants //-------------------------------------------------------------------------- @@ -67,16 +67,16 @@ contract CrosschainBase_v1_Test is ModuleTest { ERC20PaymentClientBaseV1Mock paymentClient; //System under test (SuT) - //CrosschainBase_v1 public paymentProcessor; - CrosschainBase_v1_Exposed public paymentProcessor; + //CrossChainBase_v1 public paymentProcessor; + CrossChainBase_v1_Exposed public paymentProcessor; //-------------------------------------------------------------------------- //Setup function setUp() public { //This function is used to setup the unit test //Deploy the SuT - address impl = address(new CrosschainBase_v1_Exposed(block.chainid)); - paymentProcessor = CrosschainBase_v1_Exposed(Clones.clone(impl)); + address impl = address(new CrossChainBase_v1_Exposed(block.chainid)); + paymentProcessor = CrossChainBase_v1_Exposed(Clones.clone(impl)); //Setup the module to test _setUpOrchestrator(paymentProcessor); diff --git a/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1_Exposed.sol b/test/modules/paymentProcessor/bridging/abstracts/CrossChainBase_v1_Exposed.sol similarity index 70% rename from test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1_Exposed.sol rename to test/modules/paymentProcessor/bridging/abstracts/CrossChainBase_v1_Exposed.sol index 1a9521d8e..5669db712 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1_Exposed.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/CrossChainBase_v1_Exposed.sol @@ -2,16 +2,16 @@ // Internal Dependencies //import {PP_CrossChain_v1} from "src/templates/modules/PP_Template_v1.sol"; -import {CrosschainBase_v1} from - "src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol"; +import {CrossChainBase_v1} from + "src/modules/paymentProcessor/abstracts/CrossChainBase_v1.sol"; import {IERC20PaymentClientBase_v1} from "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; -contract CrosschainBase_v1_Exposed is CrosschainBase_v1 { - constructor(uint chainId_) CrosschainBase_v1() {} +contract CrossChainBase_v1_Exposed is CrossChainBase_v1 { + constructor(uint chainId_) CrossChainBase_v1() {} /// @notice Implementation of the bridge transfer logic using EverClear - ///// @inheritdoc CrosschainBase_v1 + ///// @inheritdoc CrossChainBase_v1 function exposed_executeBridgeTransfer( IERC20PaymentClientBase_v1.PaymentOrder memory order, diff --git a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol index 952adc451..8fe94a937 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol @@ -10,10 +10,10 @@ import {Clones} from "@oz/proxy/Clones.sol"; import {PP_Connext_Crosschain_v1} from "src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol"; -import {CrosschainBase_v1} from - "src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol"; -import {CrosschainBase_v1_Exposed} from - "test/modules/paymentProcessor/bridging/abstracts/CrosschainBase_v1_Exposed.sol"; +import {CrossChainBase_v1} from + "src/modules/paymentProcessor/abstracts/CrossChainBase_v1.sol"; +import {CrossChainBase_v1_Exposed} from + "test/modules/paymentProcessor/bridging/abstracts/CrossChainBase_v1_Exposed.sol"; import {IERC20PaymentClientBase_v1} from "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; import {ICrossChainBase_v1} from @@ -42,7 +42,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { Mock_EverclearPayment public everclearPaymentMock; ERC20Mock public token; ERC20PaymentClientBaseV1Mock paymentClient; - CrosschainBase_v1 public paymentProcessor; + CrossChainBase_v1 public paymentProcessor; uint public chainId; @@ -79,8 +79,8 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { // Deploy and setup mock payment client everclearPaymentMock = new Mock_EverclearPayment(); - address impl = address(new CrosschainBase_v1_Exposed(block.chainid)); - paymentProcessor = CrosschainBase_v1_Exposed(Clones.clone(impl)); + address impl = address(new CrossChainBase_v1_Exposed(block.chainid)); + paymentProcessor = CrossChainBase_v1_Exposed(Clones.clone(impl)); //Setup the module to test _setUpOrchestrator(paymentProcessor); From 6300df5474592735fc24fc9c9887c3b3236862f1 Mon Sep 17 00:00:00 2001 From: Leandro Faria Date: Tue, 26 Nov 2024 19:08:46 -0500 Subject: [PATCH 47/93] add gherking comments --- .../abstracts/CrossChainBase_v1.t.sol | 97 ++++++------------- 1 file changed, 27 insertions(+), 70 deletions(-) diff --git a/test/modules/paymentProcessor/bridging/abstracts/CrossChainBase_v1.t.sol b/test/modules/paymentProcessor/bridging/abstracts/CrossChainBase_v1.t.sol index 33975b053..735a89e96 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/CrossChainBase_v1.t.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/CrossChainBase_v1.t.sol @@ -38,22 +38,19 @@ import {ICrossChainBase_v1} from import {OZErrors} from "test/utils/errors/OZErrors.sol"; /** - * @title Inverter Template Payment Processor Tests + * @title CrossChainBase Test Suite * - * @notice Basic template payment processor used to showcase the unit testing - * setup + * @notice Test suite for the CrossChainBase_v1 abstract contract * - * @dev Not all functions are tested in this template. Placeholders of the - * functions that are not tested are added into the contract. This test - * showcases the following: - * - Inherit from the ModuleTest contract to enable interaction with - * the Inverter workflow. - * - Showcases the setup of the workflow, uses in test unit tests. - * - Pre-defined layout for all setup and functions to be tested. - * - Shows the use of Gherkin for documenting the testing. VS Code - * extension used for formatting is recommended. - * - Shows the use of the modifierInPlace pattern to test the modifier - * placement. + * @dev Tests the core functionality of the CrossChainBase contract including: + * - Contract initialization and reinitialization protection + * - Interface support verification + * - Bridge transfer execution + * - Integration with the Inverter workflow through ModuleTest + * + * @custom:security-contact security@inverter.network + * In case of any concerns or findings, please refer to our Security Policy + * at security.inverter.network or email us directly! * * @author Inverter Network */ @@ -61,13 +58,8 @@ contract CrossChainBase_v1_Test is ModuleTest { //-------------------------------------------------------------------------- //Constants //-------------------------------------------------------------------------- - //State - //Mocks ERC20PaymentClientBaseV1Mock paymentClient; - - //System under test (SuT) - //CrossChainBase_v1 public paymentProcessor; CrossChainBase_v1_Exposed public paymentProcessor; //-------------------------------------------------------------------------- @@ -100,19 +92,29 @@ contract CrossChainBase_v1_Test is ModuleTest { paymentClient.setIsAuthorized(address(paymentProcessor), true); paymentClient.setToken(_token); } + /* Test CrossChainBase functionality + ├── Given the contract is initialized + │ └── When checking interface support + │ └── Then it should support ICrossChainBase_v1 + │ └── Then it should not support random interfaces + ├── Given the contract is already initialized + │ └── When trying to reinitialize + │ └── Then it should revert + └── Given a valid payment order + └── When executeBridgeTransfer is called + └── Then it should return empty bytes + */ //-------------------------------------------------------------------------- //Test: Initialization - //@zuhaib - we need some basic tests for the base, we just need to init it and - //make sure that the executeBridgeTransfer is correctly set and returns empty bytes - //Test if the orchestrator is correctly set function testInit() public override(ModuleTest) { assertEq( address(paymentProcessor.orchestrator()), address(_orchestrator) ); } - //Test the interface support + //-------------------------------------------------------------------------- + //Test: Interface Support function testSupportsInterface() public { // Test for ICrossChainBase_v1 interface support bytes4 interfaceId = type(ICrossChainBase_v1).interfaceId; @@ -128,8 +130,9 @@ contract CrossChainBase_v1_Test is ModuleTest { vm.expectRevert(OZErrors.Initializable__InvalidInitialization); paymentProcessor.init(_orchestrator, _METADATA, abi.encode(1)); } + //-------------------------------------------------------------------------- + //Test: executeBridgeTransfer - //Test executeBridgeTransfer returns empty bytes function testExecuteBridgeTransfer() public { address[] memory setupRecipients = new address[](1); setupRecipients[0] = address(1); @@ -147,52 +150,6 @@ contract CrossChainBase_v1_Test is ModuleTest { ); assertEq(result, bytes("")); } - - // -----ALL below this we're keeping for reference, but not testing - //-------------------------------------------------------------------------- - //Test: Modifiers - - /* Test validClient modifier in place (extensive testing done through internal modifier functions) - └── Given the modifier is in place - └── When the function processPayment() is called - └── Then it should revert - */ - // function testProcessPayments_modifierInPlace() public { - // } - // - //-------------------------------------------------------------------------- - //Test: External (public & external)Cros - - //Test external processPayments() function - - //Test external cancelRunningPayments() function - - //Test external unclaimable() function - - //Test external claimPreviouslyUnclaimable() function - - //Test external validPaymentOrder() function - - //-------------------------------------------------------------------------- - //Test: Internal (tested through exposed_functions) - - /* test internal _setPayoutAmountMultiplier() - ├── Given the newPayoutAmount == 0 - │ └── When the function _setPayoutAmountMultiplier() is called - │ └── Then it should revert - └── Given the newPayoutAmount != 0 - └── When the function _setPayoutAmountMultiplier() is called - └── Then it should emit the event - └── And it should set the state correctly - */ - - //function testInternalSetPayoutAmountMultiplier_FailsGivenZero() public { - // } - - //Test the internal _validPaymentReceiver() function - - //Test the internal _validClientModifier() function - //-------------------------------------------------------------------------- //Helper Functions From 084e5940831c5a32ad1490e36de8dc0c96449182 Mon Sep 17 00:00:00 2001 From: Leandro Faria Date: Tue, 26 Nov 2024 19:34:50 -0500 Subject: [PATCH 48/93] remove unecessary variables and add comments --- .../abstracts/CrossChainBase_v1.t.sol | 22 ++-- .../PP_Connext_Crosschain_v1_Test.t.sol | 110 +++++++----------- 2 files changed, 55 insertions(+), 77 deletions(-) diff --git a/test/modules/paymentProcessor/bridging/abstracts/CrossChainBase_v1.t.sol b/test/modules/paymentProcessor/bridging/abstracts/CrossChainBase_v1.t.sol index 735a89e96..e62dc638e 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/CrossChainBase_v1.t.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/CrossChainBase_v1.t.sol @@ -60,7 +60,7 @@ contract CrossChainBase_v1_Test is ModuleTest { //-------------------------------------------------------------------------- //Mocks ERC20PaymentClientBaseV1Mock paymentClient; - CrossChainBase_v1_Exposed public paymentProcessor; + CrossChainBase_v1_Exposed public crossChainBase; //-------------------------------------------------------------------------- //Setup @@ -68,16 +68,16 @@ contract CrossChainBase_v1_Test is ModuleTest { //This function is used to setup the unit test //Deploy the SuT address impl = address(new CrossChainBase_v1_Exposed(block.chainid)); - paymentProcessor = CrossChainBase_v1_Exposed(Clones.clone(impl)); + crossChainBase = CrossChainBase_v1_Exposed(Clones.clone(impl)); //Setup the module to test - _setUpOrchestrator(paymentProcessor); + _setUpOrchestrator(crossChainBase); //General setup for other contracts in the workflow _authorizer.setIsAuthorized(address(this), true); //Initiate the PP with the medata and config data - paymentProcessor.init(_orchestrator, _METADATA, abi.encode(1)); + crossChainBase.init(_orchestrator, _METADATA, abi.encode(1)); //Setup other modules needed in the unit tests. //In this case a payment client is needed to test the PP_Template_v1. @@ -89,7 +89,7 @@ contract CrossChainBase_v1_Test is ModuleTest { _orchestrator.executeAddModule(address(paymentClient)); //Init payment client paymentClient.init(_orchestrator, _METADATA, bytes("")); - paymentClient.setIsAuthorized(address(paymentProcessor), true); + paymentClient.setIsAuthorized(address(crossChainBase), true); paymentClient.setToken(_token); } /* Test CrossChainBase functionality @@ -108,9 +108,7 @@ contract CrossChainBase_v1_Test is ModuleTest { //-------------------------------------------------------------------------- //Test: Initialization function testInit() public override(ModuleTest) { - assertEq( - address(paymentProcessor.orchestrator()), address(_orchestrator) - ); + assertEq(address(crossChainBase.orchestrator()), address(_orchestrator)); } //-------------------------------------------------------------------------- @@ -118,17 +116,17 @@ contract CrossChainBase_v1_Test is ModuleTest { function testSupportsInterface() public { // Test for ICrossChainBase_v1 interface support bytes4 interfaceId = type(ICrossChainBase_v1).interfaceId; - assertTrue(paymentProcessor.supportsInterface(interfaceId)); + assertTrue(crossChainBase.supportsInterface(interfaceId)); // Test for random interface ID (should return false) bytes4 randomInterfaceId = bytes4(keccak256("random()")); - assertFalse(paymentProcessor.supportsInterface(randomInterfaceId)); + assertFalse(crossChainBase.supportsInterface(randomInterfaceId)); } //Test the reinit function function testReinitFails() public override(ModuleTest) { vm.expectRevert(OZErrors.Initializable__InvalidInitialization); - paymentProcessor.init(_orchestrator, _METADATA, abi.encode(1)); + crossChainBase.init(_orchestrator, _METADATA, abi.encode(1)); } //-------------------------------------------------------------------------- //Test: executeBridgeTransfer @@ -145,7 +143,7 @@ contract CrossChainBase_v1_Test is ModuleTest { bytes memory executionData = abi.encode(0, 0); //maxFee and ttl setup - bytes memory result = paymentProcessor.exposed_executeBridgeTransfer( + bytes memory result = crossChainBase.exposed_executeBridgeTransfer( orders[0], executionData ); assertEq(result, bytes("")); diff --git a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol index 8fe94a937..7dcfd7510 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol @@ -34,15 +34,15 @@ import { } from "test/modules/ModuleTest.sol"; import {OZErrors} from "test/utils/errors/OZErrors.sol"; import {IERC20Errors} from "@oz/interfaces/draft-IERC6093.sol"; - +import {IWETH} from "src/modules/paymentProcessor/interfaces/IWETH.sol"; import "forge-std/console2.sol"; contract PP_Connext_Crosschain_v1_Test is ModuleTest { - PP_Connext_Crosschain_v1 public crossChainManager; + PP_Connext_Crosschain_v1 public paymentProcessor; Mock_EverclearPayment public everclearPaymentMock; ERC20Mock public token; ERC20PaymentClientBaseV1Mock paymentClient; - CrossChainBase_v1 public paymentProcessor; + IWETH public weth; uint public chainId; @@ -67,61 +67,44 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { uint constant MINTED_SUPPLY = 1000 ether; function setUp() public { - // Set the chainId + // Set chain ID for test environment chainId = block.chainid; - //Set the execution data + // Prepare execution data for bridge operations executionData = abi.encode(maxFee, ttl); invalidExecutionData = abi.encode(address(0)); - // Deploy token + + // Deploy test token token = new ERC20Mock("Test Token", "TEST"); - // Deploy and setup mock payment client + // Deploy mock contracts and set addresses everclearPaymentMock = new Mock_EverclearPayment(); + mockEverClearSpoke = address(everclearPaymentMock); + mockWeth = address(weth); - address impl = address(new CrossChainBase_v1_Exposed(block.chainid)); - paymentProcessor = CrossChainBase_v1_Exposed(Clones.clone(impl)); + // Deploy payment processor via clone + address impl = address(new PP_Connext_Crosschain_v1()); + paymentProcessor = PP_Connext_Crosschain_v1(Clones.clone(impl)); - //Setup the module to test _setUpOrchestrator(paymentProcessor); - - //General setup for other contracts in the workflow _authorizer.setIsAuthorized(address(this), true); - //Initiate the PP with the medata and config data - paymentProcessor.init(_orchestrator, _METADATA, abi.encode(1)); + // Initialize payment processor with config + bytes memory configData = abi.encode(mockEverClearSpoke, mockWeth); + paymentProcessor.init(_orchestrator, _METADATA, configData); - //Setup other modules needed in the unit tests. - //In this case a payment client is needed to test the PP_Template_v1. + // Deploy and add payment client through timelock process impl = address(new ERC20PaymentClientBaseV1Mock()); paymentClient = ERC20PaymentClientBaseV1Mock(Clones.clone(impl)); - //Adding the payment client is done through a timelock mechanism _orchestrator.initiateAddModuleWithTimelock(address(paymentClient)); vm.warp(block.timestamp + _orchestrator.MODULE_UPDATE_TIMELOCK()); _orchestrator.executeAddModule(address(paymentClient)); - //Init payment client + // Configure payment client paymentClient.init(_orchestrator, _METADATA, bytes("")); paymentClient.setIsAuthorized(address(paymentProcessor), true); paymentClient.setToken(token); - impl = address(new PP_Connext_Crosschain_v1()); - crossChainManager = PP_Connext_Crosschain_v1(Clones.clone(impl)); - // Setup mock addresses - mockConnextBridge = address(0x123456); - mockEverClearSpoke = address(everclearPaymentMock); // Using the existing mock - mockWeth = address(0x789012); // Or deploy a mock WETH contract if needed - - //init the processor - // Initialize with proper config data - bytes memory configData = abi.encode( - address(mockEverClearSpoke), // address of your EverClear spoke contract - address(mockWeth) // address of your WETH contract - ); - - crossChainManager.init(_orchestrator, _METADATA, configData); - paymentClient.setIsAuthorized(address(crossChainManager), true); - // Call the new function for balances setup _setupInitialBalances(); } @@ -134,18 +117,18 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { function testSupportsInterface() public { // Test for IModule_v1 interface assertTrue( - crossChainManager.supportsInterface(type(IModule_v1).interfaceId) + paymentProcessor.supportsInterface(type(IModule_v1).interfaceId) ); // Test for ICrossChainBase_v1 interface assertTrue( - crossChainManager.supportsInterface( + paymentProcessor.supportsInterface( type(ICrossChainBase_v1).interfaceId ) ); // Test for a non-supported interface (using a random interface ID) - assertFalse(crossChainManager.supportsInterface(0xffffffff)); + assertFalse(paymentProcessor.supportsInterface(0xffffffff)); } function testReinitFails() public override(ModuleTest) { @@ -176,7 +159,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { ); // Process payments - crossChainManager.processPayments(client, executionData); + paymentProcessor.processPayments(client, executionData); } function testFuzz_ProcessPayments_multiplePayment( @@ -219,17 +202,16 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { } // Process payments - crossChainManager.processPayments(client, executionData); + paymentProcessor.processPayments(client, executionData); } function test_ProcessPayments_noPayments() public { // Process payments and verify _bridgeData mapping is not updated - crossChainManager.processPayments( + paymentProcessor.processPayments( IERC20PaymentClientBase_v1(address(paymentClient)), executionData ); assertTrue( - keccak256(crossChainManager.getBridgeData(0)) - == keccak256(bytes("")), + keccak256(paymentProcessor.getBridgeData(0)) == keccak256(bytes("")), "Bridge data should be empty" ); } @@ -249,7 +231,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { // Process payments vm.expectRevert(); - crossChainManager.processPayments(client, invalidExecutionData); + paymentProcessor.processPayments(client, invalidExecutionData); } function testFuzz_returnsCorrectBridgeDataRevert( @@ -265,7 +247,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { // Process payments vm.expectRevert(); - crossChainManager.processPayments(client, invalidExecutionData); + paymentProcessor.processPayments(client, invalidExecutionData); } function testFuzz_ProcessPayments_emptyExecutionData( @@ -287,7 +269,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { .Module__CrossChainBase_InvalidExecutionData .selector ); - crossChainManager.processPayments(client, bytes("")); + paymentProcessor.processPayments(client, bytes("")); } function testFuzz_ProcessPayments_invalidRecipient(uint testAmount) @@ -304,7 +286,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { vm.expectRevert( ICrossChainBase_v1.Module__CrossChainBase__InvalidRecipient.selector ); - crossChainManager.processPayments(client, executionData); + paymentProcessor.processPayments(client, executionData); } function testFuzz_ProcessPayments_invalidAmount(address testRecipient) @@ -321,7 +303,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { vm.expectRevert( ICrossChainBase_v1.Module__CrossChainBase__InvalidAmount.selector ); - crossChainManager.processPayments(client, executionData); + paymentProcessor.processPayments(client, executionData); } function testFuzz_returnsCorrectBridgeData( @@ -336,14 +318,13 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { IERC20PaymentClientBase_v1 client = IERC20PaymentClientBase_v1(address(paymentClient)); // Process payments and verify _bridgeData mapping is updated - crossChainManager.processPayments(client, executionData); + paymentProcessor.processPayments(client, executionData); assertTrue( - keccak256(crossChainManager.getBridgeData(0)) - != keccak256(bytes("")), + keccak256(paymentProcessor.getBridgeData(0)) != keccak256(bytes("")), "Bridge data should not be empty" ); - bytes32 intentId = bytes32(crossChainManager.getBridgeData(0)); + bytes32 intentId = bytes32(paymentProcessor.getBridgeData(0)); assertEq( uint(everclearPaymentMock.status(intentId)), uint(Mock_EverclearPayment.IntentStatus.ADDED) @@ -354,10 +335,9 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { IERC20PaymentClientBase_v1 client = IERC20PaymentClientBase_v1(address(paymentClient)); // Process payments and verify _bridgeData mapping is updated - crossChainManager.processPayments(client, executionData); + paymentProcessor.processPayments(client, executionData); assertTrue( - keccak256(crossChainManager.getBridgeData(0)) - == keccak256(bytes("")), + keccak256(paymentProcessor.getBridgeData(0)) == keccak256(bytes("")), "Bridge data should be empty" ); } @@ -377,11 +357,11 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { abi.encodeWithSelector( IERC20Errors.ERC20InsufficientBalance.selector, address(this), - token.balanceOf(address(crossChainManager)), + token.balanceOf(address(paymentProcessor)), testAmount ) ); - crossChainManager.processPayments(client, executionData); + paymentProcessor.processPayments(client, executionData); } //@zuhaib - let's add some tests for unhappy paths here @@ -399,14 +379,14 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { vm.assume(testRecipient != address(0)); // Setup - Clear existing balance - uint currentBalance = token.balanceOf(address(crossChainManager)); + uint currentBalance = token.balanceOf(address(paymentProcessor)); if (currentBalance > 0) { - vm.prank(address(crossChainManager)); + vm.prank(address(paymentProcessor)); token.transfer(address(1), currentBalance); } // Setup - Mint exact amount needed - token.mint(address(crossChainManager), testAmount); + token.mint(address(paymentProcessor), testAmount); _setupSinglePayment(testRecipient, testAmount); @@ -415,7 +395,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { emit PaymentProcessed(0, testRecipient, address(token), testAmount); // Action - crossChainManager.processPayments( + paymentProcessor.processPayments( IERC20PaymentClientBase_v1(address(paymentClient)), executionData ); } @@ -465,10 +445,10 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { function _setupInitialBalances() internal { // Setup token approvals and initial balances token.mint(address(this), MINTED_SUPPLY); - token.approve(address(crossChainManager), type(uint).max); + token.approve(address(paymentProcessor), type(uint).max); - token.mint(address(crossChainManager), MINTED_SUPPLY); // Mint tokens to processor - vm.prank(address(crossChainManager)); - token.approve(address(crossChainManager), type(uint).max); // Processor approves bridge logic + token.mint(address(paymentProcessor), MINTED_SUPPLY); // Mint tokens to processor + vm.prank(address(paymentProcessor)); + token.approve(address(paymentProcessor), type(uint).max); // Processor approves bridge logic } } From 3fff1b7dcdeece41fbb75134dd2d8807854ef80e Mon Sep 17 00:00:00 2001 From: Leandro Faria Date: Tue, 26 Nov 2024 20:08:14 -0500 Subject: [PATCH 49/93] add more gherkin to each test --- .../abstracts/CrossChainBase_v1.t.sol | 26 ----- .../PP_Connext_Crosschain_v1_Test.t.sol | 96 +++++++++++++++++-- 2 files changed, 90 insertions(+), 32 deletions(-) diff --git a/test/modules/paymentProcessor/bridging/abstracts/CrossChainBase_v1.t.sol b/test/modules/paymentProcessor/bridging/abstracts/CrossChainBase_v1.t.sol index e62dc638e..1b86278af 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/CrossChainBase_v1.t.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/CrossChainBase_v1.t.sol @@ -15,45 +15,19 @@ import {CrossChainBase_v1} from //External Dependencies import {Clones} from "@oz/proxy/Clones.sol"; -//Tests and Mocks -// import cr import { IERC20PaymentClientBase_v1, ERC20PaymentClientBaseV1Mock, ERC20Mock } from "test/utils/mocks/modules/paymentClient/ERC20PaymentClientBaseV1Mock.sol"; -//import exposed import {CrossChainBase_v1_Exposed} from "./CrossChainBase_v1_Exposed.sol"; -//System under test (SuT) -// import { -// IPP_CrossChain_v1, -// PP_CrossChain_v1, -// IPaymentProcessor_v1 -// } from "src/templates/modules/PP_Template_v1.sol"; import {IPaymentProcessor_v1} from "src/orchestrator/interfaces/IOrchestrator_v1.sol"; import {ICrossChainBase_v1} from "src/modules/paymentProcessor/interfaces/ICrosschainBase_v1.sol"; import {OZErrors} from "test/utils/errors/OZErrors.sol"; -/** - * @title CrossChainBase Test Suite - * - * @notice Test suite for the CrossChainBase_v1 abstract contract - * - * @dev Tests the core functionality of the CrossChainBase contract including: - * - Contract initialization and reinitialization protection - * - Interface support verification - * - Bridge transfer execution - * - Integration with the Inverter workflow through ModuleTest - * - * @custom:security-contact security@inverter.network - * In case of any concerns or findings, please refer to our Security Policy - * at security.inverter.network or email us directly! - * - * @author Inverter Network - */ contract CrossChainBase_v1_Test is ModuleTest { //-------------------------------------------------------------------------- //Constants diff --git a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol index 7dcfd7510..16f63f8f2 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol @@ -108,12 +108,25 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { _setupInitialBalances(); } + /* Test initialization + └── When the contract is initialized + ├── Then it should set the orchestrator correctly + └── Then it should set up Connext configuration properly + */ function testInit() public override(ModuleTest) { assertEq( address(paymentProcessor.orchestrator()), address(_orchestrator) ); } + /* Test interface support + ├── When checking IModule_v1 interface + │ └── Then it should return true + ├── When checking ICrossChainBase_v1 interface + │ └── Then it should return true + └── When checking random interface + └── Then it should return false + */ function testSupportsInterface() public { // Test for IModule_v1 interface assertTrue( @@ -131,11 +144,25 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { assertFalse(paymentProcessor.supportsInterface(0xffffffff)); } + /* Test reinitialization protection + └── When trying to reinitialize an already initialized contract + └── Then it should revert with InvalidInitialization + */ function testReinitFails() public override(ModuleTest) { vm.expectRevert(OZErrors.Initializable__InvalidInitialization); paymentProcessor.init(_orchestrator, _METADATA, abi.encode(1)); } + /* Test single payment processing + ├── Given a valid payment order + │ ├── When processing through Connext bridge + │ │ ├── Then it should emit PaymentProcessed event + │ │ ├── Then it should transfer tokens correctly + │ │ └── Then it should create Connext intent + │ │ + │ └── When checking bridge data + │ └── Then it should contain valid Connext intent ID + */ function testFuzz_ProcessPayments_singlePayment( address testRecipient, uint testAmount @@ -162,6 +189,16 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { paymentProcessor.processPayments(client, executionData); } + /* Test multiple payment processing + ├── Given multiple valid payment orders + │ ├── When processing through Connext bridge + │ │ ├── Then it should emit PaymentProcessed events for each payment + │ │ ├── Then it should batch transfer tokens correctly + │ │ └── Then it should create multiple Connext intents + │ │ + │ └── When checking bridge data + │ └── Then it should contain valid Connext intent IDs + */ function testFuzz_ProcessPayments_multiplePayment( uint8 numRecipients, address testRecipient, @@ -205,6 +242,11 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { paymentProcessor.processPayments(client, executionData); } + /* Test empty payment processing + └── When processing with no payment orders + ├── Then it should complete successfully + └── Then bridge data should remain empty + */ function test_ProcessPayments_noPayments() public { // Process payments and verify _bridgeData mapping is not updated paymentProcessor.processPayments( @@ -216,6 +258,10 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { ); } + /* Test invalid execution data + └── When processing with invalid Connext parameters + └── Then it should revert + */ function testFuzz_ProcessPayments_invalidExecutionData( address testRecipient, uint testAmount @@ -234,6 +280,12 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { paymentProcessor.processPayments(client, invalidExecutionData); } + /* Test bridge data reversion + ├── Given invalid execution data + │ └── When attempting to process payment + │ ├── Then it should revert + │ └── Then no bridge data should be stored + */ function testFuzz_returnsCorrectBridgeDataRevert( address testRecipient, uint testAmount @@ -250,6 +302,11 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { paymentProcessor.processPayments(client, invalidExecutionData); } + /* Test empty execution data + ├── Given empty execution data bytes + │ └── When attempting to process payment + │ └── Then it should revert with InvalidExecutionData + */ function testFuzz_ProcessPayments_emptyExecutionData( address testRecipient, uint testAmount @@ -272,6 +329,11 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { paymentProcessor.processPayments(client, bytes("")); } + /* Test invalid recipient + ├── Given a payment order with address(0) recipient + │ └── When attempting to process payment + │ └── Then it should revert with InvalidRecipient + */ function testFuzz_ProcessPayments_invalidRecipient(uint testAmount) public { @@ -289,6 +351,11 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { paymentProcessor.processPayments(client, executionData); } + /* Test invalid amount + ├── Given a payment order with zero amount + │ └── When attempting to process payment + │ └── Then it should revert with InvalidAmount + */ function testFuzz_ProcessPayments_invalidAmount(address testRecipient) public { @@ -306,6 +373,13 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { paymentProcessor.processPayments(client, executionData); } + /* Test bridge data storage + ├── Given a valid payment order + │ └── When processing payment + │ ├── Then bridge data should not be empty + │ ├── Then intent ID should be stored correctly + │ └── Then intent status should be ADDED in Everclear spoke + */ function testFuzz_returnsCorrectBridgeData( address testRecipient, uint testAmount @@ -331,6 +405,10 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { ); } + /* Test empty bridge data + └── When checking bridge data with no processed payments + └── Then it should return empty bytes + */ function test_returnsEmptyBridgeData() public { IERC20PaymentClientBase_v1 client = IERC20PaymentClientBase_v1(address(paymentClient)); @@ -342,6 +420,11 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { ); } + /* Test insufficient balance + ├── Given payment amount exceeds available balance + │ └── When attempting to process payment + │ └── Then it should revert with ERC20InsufficientBalance + */ function testFuzz_ProcessPayments_InsufficientBalance( address testRecipient, uint testAmount @@ -364,12 +447,13 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { paymentProcessor.processPayments(client, executionData); } - //@zuhaib - let's add some tests for unhappy paths here - // testMaxFeeTooHigh() - // testInvalidTt() - // testInvavil @33audits - in case of everclear, both maxFee and ttl can be zero, please check https://docs.everclear.org/developers/guides/xerc20#newintent-called-on-spoke-contract - // Validate that these fuzz tests I wrote are actually helpful they may be redundant - + /* Test edge case amounts + ├── Given payment processor has exactly required amount + │ └── When processing payment + │ ├── Then it should process successfully + │ ├── Then it should emit PaymentProcessed event + │ └── Then it should handle exact balance correctly + */ function testFuzz_ProcessPayments_EdgeCaseAmounts( address testRecipient, uint96 testAmount From 8fc2cd8345c41ca60c52b60676599c55e77866fd Mon Sep 17 00:00:00 2001 From: Leandro Faria Date: Tue, 26 Nov 2024 20:36:56 -0500 Subject: [PATCH 50/93] add validation of ttl --- src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol | 6 +++--- .../bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol b/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol index f0dba4512..b040929a2 100644 --- a/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol +++ b/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol @@ -100,9 +100,9 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { } (uint maxFee, uint ttl) = abi.decode(executionData, (uint, uint)); - // if (ttl == 0) { - // revert ICrossChainBase_v1.Module__CrossChainBase_InvalidTTL(); - // } @33audits - in case of everclear, both maxFee and ttl can be zero, please check https://docs.everclear.org/developers/guides/xerc20#newintent-called-on-spoke-contract + if (ttl == 0) { + revert ICrossChainBase_v1.Module__CrossChainBase_InvalidTTL(); + } // Wrap ETH into WETH to send with the xcall IERC20(order.paymentToken).transferFrom( diff --git a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol index 16f63f8f2..3a4935972 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol @@ -60,7 +60,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { address public mockWeth; uint maxFee = 0; - uint ttl = 0; + uint ttl = 1; bytes executionData; bytes invalidExecutionData; From d3d0af3472d764b897ea0edc152506eba9352604 Mon Sep 17 00:00:00 2001 From: Leandro Faria Date: Tue, 26 Nov 2024 22:38:05 -0500 Subject: [PATCH 51/93] add getbridgedata and natspec --- .../PP_Connext_Crosschain_v1.sol | 34 ++++++++++++++----- .../interfaces/IPP_Connext_Crosschain_v1.sol | 34 +++++++++++++++++++ .../interfaces/IPP_Crosschain_v1.sol | 5 --- .../abstracts/CrossChainBase_v1_Exposed.sol | 2 -- 4 files changed, 59 insertions(+), 16 deletions(-) create mode 100644 src/modules/paymentProcessor/interfaces/IPP_Connext_Crosschain_v1.sol diff --git a/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol b/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol index b040929a2..fd999acac 100644 --- a/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol +++ b/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol @@ -16,10 +16,17 @@ import {IOrchestrator_v1} from "src/orchestrator/interfaces/IOrchestrator_v1.sol"; import {Module_v1} from "src/modules/base/Module_v1.sol"; +/// @title PP_Connext_Crosschain_v1 +/// @notice Payment processor implementation for cross-chain payments using Connext +/// @dev Extends PP_Crosschain_v1 to handle cross-chain token transfers contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { IEverclearSpoke public everClearSpoke; IWETH public weth; + /// @notice Initializes the payment processor module + /// @param orchestrator_ The address of the orchestrator contract + /// @param metadata Module metadata + /// @param configData ABI encoded configuration data containing everClearSpoke and WETH addresses function init( IOrchestrator_v1 orchestrator_, Metadata memory metadata, @@ -46,13 +53,13 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { return abi.encode(intentId); } + /// @notice Processes multiple payment orders through the bridge + /// @param client The payment client contract interface + /// @param executionData Additional data needed for execution (encoded maxFee and TTL) function processPayments( IERC20PaymentClientBase_v1 client, bytes memory executionData ) external { - //override { - //@33audits - how do we handle the processPayments function, since after adding the executionData, we dont need to override it! - // Collect orders from the client IERC20PaymentClientBase_v1.PaymentOrder[] memory orders; (orders,,) = client.collectPaymentOrders(); @@ -77,15 +84,14 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { } } + /// @notice Executes a cross-chain transfer through the Connext bridge + /// @param order The payment order to be processed + /// @param executionData Encoded data containing maxFee and TTL for the transfer + /// @return intentId The unique identifier for the cross-chain transfer function xcall( IERC20PaymentClientBase_v1.PaymentOrder memory order, bytes memory executionData ) internal returns (bytes32) { - // @zuhaib - lets add validation here for ttl in this function - // be sure to use the errors that were inherited from the base - // we can ust check that ttl is not 0 -> @33audits - in case of everclear, both maxFee and ttl can be zero, please check https://docs.everclear.org/developers/guides/xerc20#newintent-called-on-spoke-contract - // What should we do here about maxFee? - if (executionData.length == 0) { revert ICrossChainBase_v1 @@ -135,5 +141,15 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { return intentId; } - ///cancelling payments on connext?????? + /// @notice Retrieves the bridge data for a specific payment ID + /// @param paymentId The unique identifier of the payment + /// @return The bridge data associated with the payment (encoded intentId) + function getBridgeData(uint paymentId) + external + view + override + returns (bytes memory) + { + return _bridgeData[paymentId]; + } } diff --git a/src/modules/paymentProcessor/interfaces/IPP_Connext_Crosschain_v1.sol b/src/modules/paymentProcessor/interfaces/IPP_Connext_Crosschain_v1.sol new file mode 100644 index 000000000..795bcdefb --- /dev/null +++ b/src/modules/paymentProcessor/interfaces/IPP_Connext_Crosschain_v1.sol @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity ^0.8.20; + +import {IPaymentProcessor_v1} from + "src/modules/paymentProcessor/IPaymentProcessor_v1.sol"; +import {IERC20PaymentClientBase_v1} from + "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; +import {IEverclearSpoke} from + "src/modules/paymentProcessor/interfaces/IEverclear.sol"; +import {IWETH} from "src/modules/paymentProcessor/interfaces/IWETH.sol"; + +interface IPP_Connext_Crosschain_v1 is IPaymentProcessor_v1 { + /// @notice Returns the Everclear spoke contract instance + function everClearSpoke() external view returns (IEverclearSpoke); + + /// @notice Returns the WETH contract instance + function weth() external view returns (IWETH); + + /// @notice Process payments for a given client with execution data + /// @param client The payment client contract + /// @param executionData The encoded execution parameters (maxFee, ttl) + function processPayments( + IERC20PaymentClientBase_v1 client, + bytes memory executionData + ) external; + + /// @notice Get bridge data for a specific payment ID + /// @param paymentId The ID of the payment + /// @return The bridge data associated with the payment + function getBridgeData(uint paymentId) + external + view + returns (bytes memory); +} diff --git a/src/modules/paymentProcessor/interfaces/IPP_Crosschain_v1.sol b/src/modules/paymentProcessor/interfaces/IPP_Crosschain_v1.sol index 6f9b5e207..bb147c27f 100644 --- a/src/modules/paymentProcessor/interfaces/IPP_Crosschain_v1.sol +++ b/src/modules/paymentProcessor/interfaces/IPP_Crosschain_v1.sol @@ -2,14 +2,9 @@ pragma solidity ^0.8.0; // Internal Interfaces -import {IERC20PaymentClientBase_v1} from - "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; import {IPaymentProcessor_v1} from "src/modules/paymentProcessor/IPaymentProcessor_v1.sol"; -// External Interfaces -import {IERC20} from "@oz/token/ERC20/IERC20.sol"; - interface IPP_Crosschain_v1 is IPaymentProcessor_v1 { /// @notice Thrown when the cross-chain message fails to be delivered /// @param sourceChain The chain ID where the message originated diff --git a/test/modules/paymentProcessor/bridging/abstracts/CrossChainBase_v1_Exposed.sol b/test/modules/paymentProcessor/bridging/abstracts/CrossChainBase_v1_Exposed.sol index 5669db712..8d78f964f 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/CrossChainBase_v1_Exposed.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/CrossChainBase_v1_Exposed.sol @@ -9,8 +9,6 @@ import {IERC20PaymentClientBase_v1} from contract CrossChainBase_v1_Exposed is CrossChainBase_v1 { constructor(uint chainId_) CrossChainBase_v1() {} - - /// @notice Implementation of the bridge transfer logic using EverClear ///// @inheritdoc CrossChainBase_v1 function exposed_executeBridgeTransfer( From 24ba1ce3da1fb7b858a0ff27f13c9639c45a2bc9 Mon Sep 17 00:00:00 2001 From: Leandro Faria Date: Wed, 27 Nov 2024 16:49:27 -0500 Subject: [PATCH 52/93] chore: cleaned code changed event name --- .../PP_Connext_Crosschain_v1.sol | 39 ++++++-- .../abstracts/CrossChainBase_v1.sol | 92 ------------------- .../abstracts/CrosschainBase_v1.sol | 46 +++------- .../abstracts/PP_Crosschain_v1.sol | 20 ++-- .../interfaces/ICrosschainBase_v1.sol | 28 +++--- .../interfaces/IPP_Connext_Crosschain.sol | 13 --- .../interfaces/IPP_Connext_Crosschain_v1.sol | 30 +++++- .../interfaces/IPP_Crosschain_v1.sol | 14 +-- .../abstracts/CrossChainBase_v1.t.sol | 14 ++- .../PP_Connext_Crosschain_v1_Test.t.sol | 41 ++++++--- 10 files changed, 147 insertions(+), 190 deletions(-) delete mode 100644 src/modules/paymentProcessor/abstracts/CrossChainBase_v1.sol delete mode 100644 src/modules/paymentProcessor/interfaces/IPP_Connext_Crosschain.sol diff --git a/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol b/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol index fd999acac..13910bc19 100644 --- a/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol +++ b/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol @@ -4,6 +4,8 @@ import {CrossChainBase_v1} from "src/modules/paymentProcessor/abstracts/CrossChainBase_v1.sol"; import {ICrossChainBase_v1} from "src/modules/paymentProcessor/interfaces/ICrosschainBase_v1.sol"; +import {IPP_Connext_Crosschain_v1} from + "src/modules/paymentProcessor/interfaces/IPP_Connext_Crosschain_v1.sol"; import {IERC20PaymentClientBase_v1} from "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; import {IERC20} from "@oz/token/ERC20/IERC20.sol"; @@ -16,9 +18,23 @@ import {IOrchestrator_v1} from "src/orchestrator/interfaces/IOrchestrator_v1.sol"; import {Module_v1} from "src/modules/base/Module_v1.sol"; -/// @title PP_Connext_Crosschain_v1 -/// @notice Payment processor implementation for cross-chain payments using Connext -/// @dev Extends PP_Crosschain_v1 to handle cross-chain token transfers +/** + * @title Connext Cross-chain Payment Processor + * + * @notice Payment processor implementation for cross-chain payments using the Connext protocol. + * + * @dev This contract implements cross-chain payment processing via Connext and provides: + * - Integration with Connext's EverClear protocol for cross-chain transfers + * - WETH handling for native token wrapping + * - Implementation of bridge-specific transfer logic + * - Payment order processing and validation + * - Bridge data storage and retrieval + * - Support for Base network (chainId: 8453) + * + * @custom:security-contact security@inverter.network + * In case of any concerns or findings, please refer to our Security Policy + * at security.inverter.network + */ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { IEverclearSpoke public everClearSpoke; IWETH public weth; @@ -50,6 +66,11 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { ) internal override returns (bytes memory) { //@notice call the connextBridgeLogic to execute the bridge transfer bytes32 intentId = xcall(order, executionData); + if (intentId == bytes32(0)) { + revert Module__PP_Crosschain__MessageDeliveryFailed( + 8453, 8453, executionData + ); + } return abi.encode(intentId); } @@ -68,10 +89,11 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { bytes memory bridgeData = _executeBridgeTransfer(orders[i], executionData); + _bridgeData[i] = bridgeData; emit PaymentOrderProcessed( address(client), orders[i].recipient, - address(orders[i].paymentToken), + orders[i].paymentToken, orders[i].amount, orders[i].start, orders[i].cliff, @@ -104,10 +126,13 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { if (order.recipient == address(0)) { revert ICrossChainBase_v1.Module__CrossChainBase__InvalidRecipient(); } - + // Decode the execution data (uint maxFee, uint ttl) = abi.decode(executionData, (uint, uint)); + if (ttl == 0) { - revert ICrossChainBase_v1.Module__CrossChainBase_InvalidTTL(); + revert + IPP_Connext_Crosschain_v1 + .Module__PP_Connext_Crosschain__InvalidTTL(); } // Wrap ETH into WETH to send with the xcall @@ -145,7 +170,7 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { /// @param paymentId The unique identifier of the payment /// @return The bridge data associated with the payment (encoded intentId) function getBridgeData(uint paymentId) - external + public view override returns (bytes memory) diff --git a/src/modules/paymentProcessor/abstracts/CrossChainBase_v1.sol b/src/modules/paymentProcessor/abstracts/CrossChainBase_v1.sol deleted file mode 100644 index 78f987b6e..000000000 --- a/src/modules/paymentProcessor/abstracts/CrossChainBase_v1.sol +++ /dev/null @@ -1,92 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity 0.8.23; - -import {IOrchestrator_v1} from - "src/orchestrator/interfaces/IOrchestrator_v1.sol"; -import {IPaymentProcessor_v1} from "@pp/IPaymentProcessor_v1.sol"; -import {IERC20PaymentClientBase_v1} from - "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; -import {Module_v1} from "src/modules/base/Module_v1.sol"; -//import {IPP_CrossChain_v1} from "./IPP_Template_v1.sol"; -import {ERC165Upgradeable, Module_v1} from "src/modules/base/Module_v1.sol"; -import {ICrossChainBase_v1} from "../interfaces/ICrosschainBase_v1.sol"; -/** - * @title Inverter Template Payment Processor - * - * @notice Basic template payment processor used as base for developing new payment processors. - * - * @dev This contract is used to showcase a basic setup for a payment processor. The contract showcases the - * following: - * - Inherit from the Module_v1 contract to enable interaction with the Inverter workflow. - * - Use of the IPaymentProcessor_v1 interface to facilitate interaction with a payment client. - * - Implement custom interface which has all the public facing functions, errors, events and structs. - * - Pre-defined layout for all contract functions, modifiers, state variables etc. - * - Use of the ERC165Upgradeable contract to check for interface support. - * - * @custom:security-contact security@inverter.network - * In case of any concerns or findings, please refer to our Security Policy - * at security.inverter.network or email us directly! - * - * @author Inverter Network - */ - -abstract contract CrossChainBase_v1 is ICrossChainBase_v1, Module_v1 { - mapping(uint => bytes) internal _bridgeData; - /// @inheritdoc ERC165Upgradeable - - function supportsInterface(bytes4 interfaceId_) - public - view - virtual - override(Module_v1) - returns (bool) - { - return interfaceId_ == type(ICrossChainBase_v1).interfaceId - || super.supportsInterface(interfaceId_); - } - //-------------------------------------------------------------------------- - // State - - /// @dev The number of payment orders. - uint internal _paymentId; - - //-------------------------------------------------------------------------- - // Events - - event PaymentProcessed( - uint indexed paymentId, address recipient, address token, uint amount - ); - - //-------------------------------------------------------------------------- - // Virtual Functions - - /// @notice Execute the cross-chain bridge transfer - /// @dev Override this function to implement specific bridge logic - /// @param order The payment order containing all necessary transfer details - /// @return bridgeData Arbitrary data returned by the bridge implementation - function _executeBridgeTransfer( - IERC20PaymentClientBase_v1.PaymentOrder memory order, - bytes memory executionData - ) internal virtual returns (bytes memory) { - emit BridgeTransferExecuted(executionData); - return bytes(""); - } - /// @notice Process payments for a given payment client - /// @param client The payment client to process payments for - - function processPayments(IERC20PaymentClientBase_v1 client) - external - virtual - {} - - /// @notice Get the bridge data for a given payment ID - /// @param paymentId The ID of the payment to get the bridge data for - /// @return The bridge data for the given payment ID - function getBridgeData(uint paymentId) - external - view - returns (bytes memory) - { - return _bridgeData[paymentId]; - } -} diff --git a/src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol b/src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol index 78f987b6e..a954223f0 100644 --- a/src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol +++ b/src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol @@ -7,21 +7,19 @@ import {IPaymentProcessor_v1} from "@pp/IPaymentProcessor_v1.sol"; import {IERC20PaymentClientBase_v1} from "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; import {Module_v1} from "src/modules/base/Module_v1.sol"; -//import {IPP_CrossChain_v1} from "./IPP_Template_v1.sol"; import {ERC165Upgradeable, Module_v1} from "src/modules/base/Module_v1.sol"; import {ICrossChainBase_v1} from "../interfaces/ICrosschainBase_v1.sol"; /** - * @title Inverter Template Payment Processor + * @title Cross-chain Base Contract * - * @notice Basic template payment processor used as base for developing new payment processors. + * @notice Abstract base contract providing core cross-chain functionality for payment processors. * - * @dev This contract is used to showcase a basic setup for a payment processor. The contract showcases the - * following: - * - Inherit from the Module_v1 contract to enable interaction with the Inverter workflow. - * - Use of the IPaymentProcessor_v1 interface to facilitate interaction with a payment client. - * - Implement custom interface which has all the public facing functions, errors, events and structs. - * - Pre-defined layout for all contract functions, modifiers, state variables etc. - * - Use of the ERC165Upgradeable contract to check for interface support. + * @dev This contract implements fundamental cross-chain operations and provides: + * - Bridge data storage and retrieval functionality + * - Abstract interface for bridge transfer execution + * - Integration with the Module_v1 base contract + * - Implementation of ICrossChainBase_v1 interface + * - ERC165 interface support for cross-chain functionality * * @custom:security-contact security@inverter.network * In case of any concerns or findings, please refer to our Security Policy @@ -32,8 +30,8 @@ import {ICrossChainBase_v1} from "../interfaces/ICrosschainBase_v1.sol"; abstract contract CrossChainBase_v1 is ICrossChainBase_v1, Module_v1 { mapping(uint => bytes) internal _bridgeData; - /// @inheritdoc ERC165Upgradeable + /// @inheritdoc ERC165Upgradeable function supportsInterface(bytes4 interfaceId_) public view @@ -44,18 +42,6 @@ abstract contract CrossChainBase_v1 is ICrossChainBase_v1, Module_v1 { return interfaceId_ == type(ICrossChainBase_v1).interfaceId || super.supportsInterface(interfaceId_); } - //-------------------------------------------------------------------------- - // State - - /// @dev The number of payment orders. - uint internal _paymentId; - - //-------------------------------------------------------------------------- - // Events - - event PaymentProcessed( - uint indexed paymentId, address recipient, address token, uint amount - ); //-------------------------------------------------------------------------- // Virtual Functions @@ -67,13 +53,10 @@ abstract contract CrossChainBase_v1 is ICrossChainBase_v1, Module_v1 { function _executeBridgeTransfer( IERC20PaymentClientBase_v1.PaymentOrder memory order, bytes memory executionData - ) internal virtual returns (bytes memory) { - emit BridgeTransferExecuted(executionData); - return bytes(""); - } + ) internal virtual returns (bytes memory) {} + /// @notice Process payments for a given payment client /// @param client The payment client to process payments for - function processPayments(IERC20PaymentClientBase_v1 client) external virtual @@ -83,10 +66,9 @@ abstract contract CrossChainBase_v1 is ICrossChainBase_v1, Module_v1 { /// @param paymentId The ID of the payment to get the bridge data for /// @return The bridge data for the given payment ID function getBridgeData(uint paymentId) - external + public view + virtual returns (bytes memory) - { - return _bridgeData[paymentId]; - } + {} } diff --git a/src/modules/paymentProcessor/abstracts/PP_Crosschain_v1.sol b/src/modules/paymentProcessor/abstracts/PP_Crosschain_v1.sol index ebe49d1b2..3a9f45206 100644 --- a/src/modules/paymentProcessor/abstracts/PP_Crosschain_v1.sol +++ b/src/modules/paymentProcessor/abstracts/PP_Crosschain_v1.sol @@ -23,17 +23,16 @@ import {SafeERC20} from "@oz/token/ERC20/utils/SafeERC20.sol"; import {IPP_Crosschain_v1} from "../interfaces/IPP_Crosschain_v1.sol"; /** - * @title Inverter Template Payment Processor + * @title Cross-chain Payment Processor Base Contract * - * @notice Basic template payment processor used as base for developing new payment processors. + * @notice Abstract base contract for implementing cross-chain payment processing functionality. * - * @dev This contract is used to showcase a basic setup for a payment processor. The contract showcases the - * following: - * - Inherit from the Module_v1 contract to enable interaction with the Inverter workflow. - * - Use of the IPaymentProcessor_v1 interface to facilitate interaction with a payment client. - * - Implement custom interface which has all the public facing functions, errors, events and structs. - * - Pre-defined layout for all contract functions, modifiers, state variables etc. - * - Use of the ERC165Upgradeable contract to check for interface support. + * @dev This contract serves as the base for cross-chain payment processors and provides: + * - Extension of CrossChainBase_v1 for cross-chain functionality + * - Implementation of IPP_Crosschain_v1 interface + * - Core payment validation logic + * - Basic security checks for payment processing + * - Abstract functions for bridge-specific implementations * * @custom:security-contact security@inverter.network * In case of any concerns or findings, please refer to our Security Policy @@ -81,6 +80,9 @@ abstract contract PP_Crosschain_v1 is CrossChainBase_v1, IPP_Crosschain_v1 { /// @dev Gap for possible future upgrades. uint[50] private __gap; + //@dev paymentId + uint public _paymentId; + //-------------------------------------------------------------------------- // Virtual Functions diff --git a/src/modules/paymentProcessor/interfaces/ICrosschainBase_v1.sol b/src/modules/paymentProcessor/interfaces/ICrosschainBase_v1.sol index 17fd04707..5e5c7825f 100644 --- a/src/modules/paymentProcessor/interfaces/ICrosschainBase_v1.sol +++ b/src/modules/paymentProcessor/interfaces/ICrosschainBase_v1.sol @@ -7,11 +7,15 @@ pragma solidity ^0.8.0; import {IERC20PaymentClientBase_v1} from "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; +/// @title ICrossChainBase_v1 +/// @notice Base interface for cross-chain payment processing functionality interface ICrossChainBase_v1 { - //-------------------------------------------------------------------------- - // Structs - /// @notice Struct to hold cross-chain message data + /// @param messageId Unique identifier for the cross-chain message + /// @param sourceChain Address or identifier of the source chain + /// @param targetChain Address or identifier of the target chain + /// @param payload The encoded data being sent across chains + /// @param executed Boolean flag indicating if the message has been processed struct CrossChainMessage { uint messageId; address sourceChain; @@ -24,32 +28,30 @@ interface ICrossChainBase_v1 { // Events /// @notice Emitted when a bridge transfer is executed + /// @param bridgeData The encoded data of the bridge transfer event BridgeTransferExecuted(bytes indexed bridgeData); //-------------------------------------------------------------------------- // Errors - /// @notice Amount can not be zero. + /// @notice Thrown when attempting to process a transfer with zero amount error Module__CrossChainBase__InvalidAmount(); - /// @notice Client is not valid. + /// @notice Thrown when an unauthorized client attempts to interact with the contract error Module__CrossChainBase__NotValidClient(); - /// @notice Message has already been executed + /// @notice Thrown when attempting to execute a message that has already been processed error Module__CrossChainBase_MessageAlreadyExecuted(); - /// @notice Invalid TTL provided - error Module__CrossChainBase_InvalidTTL(); - - /// @notice Invalid chain ID provided + /// @notice Thrown when an unsupported or invalid chain ID is provided error Module__CrossChainBase_InvalidChainId(); - /// @notice Message verification failed + /// @notice Thrown when the cross-chain message fails verification error Module__CrossChainBase_MessageVerificationFailed(); - /// @notice Invalid execution data + /// @notice Thrown when the provided execution data is malformed or invalid error Module__CrossChainBase_InvalidExecutionData(); - /// @notice Invalid recipient + /// @notice Thrown when the recipient address is invalid or not allowed error Module__CrossChainBase__InvalidRecipient(); } diff --git a/src/modules/paymentProcessor/interfaces/IPP_Connext_Crosschain.sol b/src/modules/paymentProcessor/interfaces/IPP_Connext_Crosschain.sol deleted file mode 100644 index dd034d85a..000000000 --- a/src/modules/paymentProcessor/interfaces/IPP_Connext_Crosschain.sol +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity ^0.8.0; - -// Internal Interfaces -import {IERC20PaymentClientBase_v1} from - "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; -import {IPaymentProcessor_v1} from - "src/modules/paymentProcessor/IPaymentProcessor_v1.sol"; - -// External Interfaces -import {IERC20} from "@oz/token/ERC20/IERC20.sol"; - -interface IPP_Connext_Crosschain is IPaymentProcessor_v1 {} diff --git a/src/modules/paymentProcessor/interfaces/IPP_Connext_Crosschain_v1.sol b/src/modules/paymentProcessor/interfaces/IPP_Connext_Crosschain_v1.sol index 795bcdefb..c296de066 100644 --- a/src/modules/paymentProcessor/interfaces/IPP_Connext_Crosschain_v1.sol +++ b/src/modules/paymentProcessor/interfaces/IPP_Connext_Crosschain_v1.sol @@ -1,23 +1,42 @@ // SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; +// Internal Dependencies import {IPaymentProcessor_v1} from "src/modules/paymentProcessor/IPaymentProcessor_v1.sol"; -import {IERC20PaymentClientBase_v1} from - "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; import {IEverclearSpoke} from "src/modules/paymentProcessor/interfaces/IEverclear.sol"; + +// External Dependencies +import {IERC20PaymentClientBase_v1} from + "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; import {IWETH} from "src/modules/paymentProcessor/interfaces/IWETH.sol"; +/// @notice Interface for cross-chain payment processing using Connext protocol interface IPP_Connext_Crosschain_v1 is IPaymentProcessor_v1 { + //-------------------------------------------------------------------------- + // Errors + + /// @notice Thrown when the provided Time-To-Live (TTL) parameter is invalid + error Module__PP_Connext_Crosschain__InvalidTTL(); + + //-------------------------------------------------------------------------- + // View Functions + /// @notice Returns the Everclear spoke contract instance + /// @return The IEverclearSpoke contract interface function everClearSpoke() external view returns (IEverclearSpoke); /// @notice Returns the WETH contract instance + /// @return The IWETH contract interface used for wrapping/unwrapping ETH function weth() external view returns (IWETH); + //-------------------------------------------------------------------------- + // External Functions + /// @notice Process payments for a given client with execution data - /// @param client The payment client contract + /// @dev This function handles the cross-chain payment processing using Connext + /// @param client The payment client contract initiating the payment /// @param executionData The encoded execution parameters (maxFee, ttl) function processPayments( IERC20PaymentClientBase_v1 client, @@ -25,8 +44,9 @@ interface IPP_Connext_Crosschain_v1 is IPaymentProcessor_v1 { ) external; /// @notice Get bridge data for a specific payment ID - /// @param paymentId The ID of the payment - /// @return The bridge data associated with the payment + /// @dev Used to retrieve information about a cross-chain payment + /// @param paymentId The ID of the payment to query + /// @return The bridge data associated with the payment (encoded bytes) function getBridgeData(uint paymentId) external view diff --git a/src/modules/paymentProcessor/interfaces/IPP_Crosschain_v1.sol b/src/modules/paymentProcessor/interfaces/IPP_Crosschain_v1.sol index bb147c27f..0efaba763 100644 --- a/src/modules/paymentProcessor/interfaces/IPP_Crosschain_v1.sol +++ b/src/modules/paymentProcessor/interfaces/IPP_Crosschain_v1.sol @@ -1,17 +1,21 @@ // SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.0; -// Internal Interfaces +// Internal Dependencies import {IPaymentProcessor_v1} from "src/modules/paymentProcessor/IPaymentProcessor_v1.sol"; +/// @notice Interface for cross-chain payment processing functionality interface IPP_Crosschain_v1 is IPaymentProcessor_v1 { + //-------------------------------------------------------------------------- + // Errors + /// @notice Thrown when the cross-chain message fails to be delivered /// @param sourceChain The chain ID where the message originated /// @param destinationChain The chain ID where the message was meant to be delivered - /// @param messageId The unique identifier of the failed message + /// @param executionData The encoded execution parameters (maxFee, ttl) error Module__PP_Crosschain__MessageDeliveryFailed( - uint sourceChain, uint destinationChain, bytes32 messageId + uint sourceChain, uint destinationChain, bytes executionData ); /// @notice Thrown when attempting to process a cross-chain payment with invalid parameters @@ -23,7 +27,5 @@ interface IPP_Crosschain_v1 is IPaymentProcessor_v1 { ); /// @notice Thrown when the cross-chain bridge fees exceed the maximum allowed - /// @param actualFee The actual fee required - /// @param maxFee The maximum fee allowed - error Module__PP_Crosschain__BridgeFeeTooHigh(uint actualFee, uint maxFee); + error Module__PP_Crosschain__InvalidBridgeFee(); } diff --git a/test/modules/paymentProcessor/bridging/abstracts/CrossChainBase_v1.t.sol b/test/modules/paymentProcessor/bridging/abstracts/CrossChainBase_v1.t.sol index 1b86278af..50a51f6cb 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/CrossChainBase_v1.t.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/CrossChainBase_v1.t.sol @@ -81,12 +81,19 @@ contract CrossChainBase_v1_Test is ModuleTest { //-------------------------------------------------------------------------- //Test: Initialization + /* Given the contract is initialized + When checking the initialization + Then it should have the correct orchestrator address */ function testInit() public override(ModuleTest) { assertEq(address(crossChainBase.orchestrator()), address(_orchestrator)); } //-------------------------------------------------------------------------- //Test: Interface Support + /* Given the contract is initialized + When checking interface support + Then it should support ICrossChainBase_v1 + And it should not support random interfaces */ function testSupportsInterface() public { // Test for ICrossChainBase_v1 interface support bytes4 interfaceId = type(ICrossChainBase_v1).interfaceId; @@ -97,7 +104,9 @@ contract CrossChainBase_v1_Test is ModuleTest { assertFalse(crossChainBase.supportsInterface(randomInterfaceId)); } - //Test the reinit function + /* Given the contract is already initialized + When trying to reinitialize + Then it should revert */ function testReinitFails() public override(ModuleTest) { vm.expectRevert(OZErrors.Initializable__InvalidInitialization); crossChainBase.init(_orchestrator, _METADATA, abi.encode(1)); @@ -105,6 +114,9 @@ contract CrossChainBase_v1_Test is ModuleTest { //-------------------------------------------------------------------------- //Test: executeBridgeTransfer + /* Given a valid payment order + When executeBridgeTransfer is called + Then it should return empty bytes */ function testExecuteBridgeTransfer() public { address[] memory setupRecipients = new address[](1); setupRecipients[0] = address(1); diff --git a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol index 3a4935972..02331995f 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol @@ -47,11 +47,14 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { uint public chainId; // Add this event definition at the contract level - event PaymentProcessed( - uint indexed paymentId, - address recipient, - address paymentToken, - uint amount + event PaymentOrderProcessed( + address indexed paymentClient, + address indexed recipient, + address indexed paymentToken, + uint amount, + uint start, + uint cliff, + uint end ); // Add these as contract state variables @@ -178,11 +181,14 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { // Expect the event vm.expectEmit(true, true, true, true); - emit PaymentProcessed( - 0, // paymentId + emit PaymentOrderProcessed( + address(paymentClient), testRecipient, address(token), - testAmount + testAmount, + block.timestamp, + 0, + block.timestamp + 1 days ); // Process payments @@ -230,11 +236,14 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { // Expect events for each payment for (uint i = 0; i < numRecipients; i++) { vm.expectEmit(true, true, true, true); - emit PaymentProcessed( - i, // paymentId + emit PaymentOrderProcessed( + address(paymentClient), setupRecipients[i], address(token), - setupAmounts[i] + setupAmounts[i], + block.timestamp, + 0, + block.timestamp + 1 days ); } @@ -476,7 +485,15 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { // Expectations vm.expectEmit(true, true, true, true); - emit PaymentProcessed(0, testRecipient, address(token), testAmount); + emit PaymentOrderProcessed( + address(paymentClient), + testRecipient, + address(token), + testAmount, + block.timestamp, + 0, + block.timestamp + 1 days + ); // Action paymentProcessor.processPayments( From eb14eeb830d74309bcbdc2ea6c9697e1d8f13584 Mon Sep 17 00:00:00 2001 From: Leandro Faria Date: Wed, 27 Nov 2024 23:10:42 -0500 Subject: [PATCH 53/93] add templates folder --- src/templates/modules/FM_Template_v1.sol | 196 +++++++++++++ src/templates/modules/IFM_Template_v1.sol | 72 +++++ src/templates/modules/IPP_Template_v1.sol | 69 +++++ src/templates/modules/PP_Template_v1.sol | 264 ++++++++++++++++++ src/templates/tests/unit/FM_Template_v1.t.sol | 179 ++++++++++++ .../tests/unit/FM_Template_v1_Exposed.sol | 16 ++ src/templates/tests/unit/PP_Template_v1.t.sol | 213 ++++++++++++++ .../tests/unit/PP_Template_v1_Exposed.sol | 29 ++ 8 files changed, 1038 insertions(+) create mode 100644 src/templates/modules/FM_Template_v1.sol create mode 100644 src/templates/modules/IFM_Template_v1.sol create mode 100644 src/templates/modules/IPP_Template_v1.sol create mode 100644 src/templates/modules/PP_Template_v1.sol create mode 100644 src/templates/tests/unit/FM_Template_v1.t.sol create mode 100644 src/templates/tests/unit/FM_Template_v1_Exposed.sol create mode 100644 src/templates/tests/unit/PP_Template_v1.t.sol create mode 100644 src/templates/tests/unit/PP_Template_v1_Exposed.sol diff --git a/src/templates/modules/FM_Template_v1.sol b/src/templates/modules/FM_Template_v1.sol new file mode 100644 index 000000000..437e7c7d3 --- /dev/null +++ b/src/templates/modules/FM_Template_v1.sol @@ -0,0 +1,196 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity 0.8.23; + +// Internal +import {IOrchestrator_v1} from + "src/orchestrator/interfaces/IOrchestrator_v1.sol"; +import {ERC165Upgradeable, Module_v1} from "src/modules/base/Module_v1.sol"; +import {IFM_Template_v1} from "./IFM_Template_v1.sol"; +import {IFundingManager_v1} from "@fm/IFundingManager_v1.sol"; + +// External +import {IERC20} from "@oz/token/ERC20/IERC20.sol"; +import {SafeERC20} from "@oz/token/ERC20/utils/SafeERC20.sol"; + +/** + * @title Inverter Template Funding Manager + * + * @notice Basic template funding manager used as base for developing new + * funding managers. + * + * @dev This contract is used to showcase a basic setup for a funding + * manager. The contract showcases the following: + * - Inherit from the Module_v1 contract to enable interaction with + * the Inverter workflow. + * - Use of the IFundingManager_v1 interface to facilitate + * interaction as a Funding Manager. + * - Implement custom interface which has all the public facing + * functions, errors, events and structs. + * - Pre-defined layout for all contract functions, modifiers, state + * variables etc. + * - Use of the ERC165Upgradeable contract to check for interface + * support. + * + * @custom:security-contact security@inverter.network + * In case of any concerns or findings, please refer + * to our Security Policy at security.inverter.network + * or email us directly! + * + * @custom:version 1.0.0 + * + * @author Inverter Network + */ +contract FM_Template_v1 is IFM_Template_v1, Module_v1 { + // ========================================================================= + // Libraries + + using SafeERC20 for IERC20; + + // ========================================================================= + // ERC165 + + /// @inheritdoc ERC165Upgradeable + function supportsInterface(bytes4 interfaceId_) + public + view + virtual + override(Module_v1) + returns (bool) + { + return interfaceId_ == type(IFM_Template_v1).interfaceId + || interfaceId_ == type(IFundingManager_v1).interfaceId + || super.supportsInterface(interfaceId_); + } + + // ========================================================================= + // Constants + + // Add constants here + + // ========================================================================= + // State + + /// @notice Mapping of user addresses to their deposited token amounts. + mapping(address user => uint amount) internal _depositedAmounts; + + /// @notice The orchestrator token. + IERC20 internal _orchestratorToken; + + /// @notice Storage gap for future upgrades. + uint[50] private __gap; + + // ========================================================================= + // Modifiers + + // Add modifiers here + + // ========================================================================= + // Constructor & Init + + /// @inheritdoc Module_v1 + function init( + IOrchestrator_v1 orchestrator_, + Metadata memory metadata_, + bytes memory configData_ + ) external override(Module_v1) initializer { + __Module_init(orchestrator_, metadata_); + + // Decode module specific init data through use of configData bytes. + // This value is an example value used to showcase the setters/getters + // and internal functions/state formatting style. + (address orchestratorTokenAddress) = abi.decode(configData_, (address)); + + // Set init state. + _orchestratorToken = IERC20(orchestratorTokenAddress); + } + + // ========================================================================= + // Public - Getters + + /// @inheritdoc IFM_Template_v1 + function getDepositedAmount(address user_) + external + view + virtual + returns (uint amount_) + { + amount_ = _depositedAmounts[user_]; + } + + /// @inheritdoc IFundingManager_v1 + function token() + external + view + override + returns (IERC20 orchestratorToken_) + { + orchestratorToken_ = _orchestratorToken; + } + + // ========================================================================= + // Public - Mutating + + /// @inheritdoc IFM_Template_v1 + function deposit(uint amount_) external virtual { + // Validate parameters. + if (amount_ == 0) { + revert Module__FM_Template_InvalidAmount(); + } + + // Update state. + _depositedAmounts[_msgSender()] += amount_; + + // Transfer tokens. + _orchestratorToken.safeTransferFrom( + _msgSender(), address(this), amount_ + ); + + // Emit event. + emit Deposited(_msgSender(), amount_); + } + + /// @inheritdoc IFundingManager_v1 + /// @dev Only the payment client can call this function. + function transferOrchestratorToken(address to, uint amount) + external + virtual + override + onlyPaymentClient + { + // Validate parameters. + _validateOrchestratorTokenTransfer(to, amount); + + // Transfer tokens. + _orchestratorToken.safeTransfer(to, amount); + + // Emit event. + emit TransferOrchestratorToken(to, amount); + } + + // ========================================================================= + // Internal + + /// @notice Validates the transfer of orchestrator token. + /// @param to_ Address to transfer to. + /// @param amount_ Amount to transfer. + function _validateOrchestratorTokenTransfer(address to_, uint amount_) + internal + view + virtual + { + if (to_ == address(0)) { + revert Module__FM_Template__ReceiverNotValid(); + } + + if (amount_ == 0) { + revert Module__FM_Template_InvalidAmount(); + } + + if (_depositedAmounts[_msgSender()] < amount_) { + revert Module__FM_Template_InvalidAmount(); + } + } + + // ========================================================================= + // Overridden Internal Functions +} diff --git a/src/templates/modules/IFM_Template_v1.sol b/src/templates/modules/IFM_Template_v1.sol new file mode 100644 index 000000000..f644c53df --- /dev/null +++ b/src/templates/modules/IFM_Template_v1.sol @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity ^0.8.0; + +import {IFundingManager_v1} from "@fm/IFundingManager_v1.sol"; + +/** + * @title Inverter Template Funding Manager + * + * @notice Basic template for funding manager inteface used as base for developing new + * funding managers. + * + * @dev This contract is used to showcase a basic setup for a funding + * manager. The contract showcases the following: + * - Inherit from the Module_v1 contract to enable interaction with + * the Inverter workflow. + * - Use of the IFundingManager_v1 interface to facilitate + * interaction as a Funding Manager. + * - Implement custom interface which has all the public facing + * functions, errors, events and structs. + * - Pre-defined layout for all contract functions, modifiers, state + * variables etc. + * - Use of the ERC165Upgradeable contract to check for interface + * support. + * + * @custom:security-contact security@inverter.network + * In case of any concerns or findings, please refer + * to our Security Policy at security.inverter.network + * or email us directly! + * + * @custom:version 1.0.0 + * + * @author Inverter Network + */ +interface IFM_Template_v1 is IFundingManager_v1 { + // ========================================================================= + // Structs + + // ========================================================================= + // Events + + /// @notice Emit when the token amount has been deposited. + /// @param sender_ The address of the depositor. + /// @param amount_ The amount of tokens deposited. + event Deposited(address indexed sender_, uint amount_); + + // ========================================================================= + // Errors + + /// @notice Amount can not be zero. + error Module__FM_Template_InvalidAmount(); + + /// @notice Token receiver is not valid. + error Module__FM_Template__ReceiverNotValid(); + + // ========================================================================= + // Public - Getters + + /// @notice Returns the deposited balance of a specific address. + /// @param user_ The address of the user. + /// @return amount_ Deposited amount of the user. + function getDepositedAmount(address user_) + external + view + returns (uint amount_); + + // ========================================================================= + // Public - Mutating + + /// @notice Deposits tokens to the funding manager. + /// @param amount_ The amount of tokens to deposit. + function deposit(uint amount_) external; +} diff --git a/src/templates/modules/IPP_Template_v1.sol b/src/templates/modules/IPP_Template_v1.sol new file mode 100644 index 000000000..61a8757c8 --- /dev/null +++ b/src/templates/modules/IPP_Template_v1.sol @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity ^0.8.0; + +// Internal +import {IPaymentProcessor_v1} from "@pp/IPaymentProcessor_v1.sol"; + +/** + * @title Inverter Template Payment Processor + * + * @notice Basic template payment processor used as base for developing new + * payment processors. + * + * @dev This contract is used to showcase a basic setup for a payment + * processor. The contract showcases the following: + * - Inherit from the Module_v1 contract to enable interaction with + * the Inverter workflow. + * - Use of the IPaymentProcessor_v1 interface to facilitate + * interaction with a payment client. + * - Implement custom interface which has all the public facing + * functions, errors, events and structs. + * - Pre-defined layout for all contract functions, modifiers, state + * variables etc. + * - Use of the ERC165Upgradeable contract to check for interface + * support. + * + * @custom:security-contact security@inverter.network + * In case of any concerns or findings, please refer + * to our Security Policy at security.inverter.network + * or email us directly! + * + * @custom:version 1.0.0 + * + * @author Inverter Network + */ +interface IPP_Template_v1 is IPaymentProcessor_v1 { + //-------------------------------------------------------------------------- + // Structs + + //-------------------------------------------------------------------------- + // Events + + /// @notice Emit when new payout amount has been set. + /// @param oldPayoutAmount_ Old payout amount. + /// @param newPayoutAmount_ Newly set payout amount. + event NewPayoutAmountMultiplierSet( + uint indexed oldPayoutAmount_, uint indexed newPayoutAmount_ + ); + + //-------------------------------------------------------------------------- + // Errors + + /// @notice Amount can not be zero. + error Module__PP_Template_InvalidAmount(); + + /// @notice Client is not valid. + error Module__PP_Template__ClientNotValid(); + + //-------------------------------------------------------------------------- + // Public (Getter) + + /// @notice Returns the payout amount for each payment order. + /// @return payoutAmountMultiplier_ The payout amount multiplier. + function getPayoutAmountMultiplier() + external + returns (uint payoutAmountMultiplier_); + + //-------------------------------------------------------------------------- + // Public (Mutating) +} diff --git a/src/templates/modules/PP_Template_v1.sol b/src/templates/modules/PP_Template_v1.sol new file mode 100644 index 000000000..f9a61cb9e --- /dev/null +++ b/src/templates/modules/PP_Template_v1.sol @@ -0,0 +1,264 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity 0.8.23; + +// Internal +import {IOrchestrator_v1} from + "src/orchestrator/interfaces/IOrchestrator_v1.sol"; +import {IPaymentProcessor_v1} from "@pp/IPaymentProcessor_v1.sol"; +import {IERC20PaymentClientBase_v1} from + "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; +import {IPP_Template_v1} from "./IPP_Template_v1.sol"; +import {ERC165Upgradeable, Module_v1} from "src/modules/base/Module_v1.sol"; + +// External +import {IERC20} from "@oz/token/ERC20/IERC20.sol"; + +/** + * @title Inverter Template Payment Processor + * + * @notice Basic template payment processor used as base for developing new + * payment processors. + * + * @dev This contract is used to showcase a basic setup for a payment + * processor. The contract showcases the following: + * - Inherit from the Module_v1 contract to enable interaction with + * the Inverter workflow. + * - Use of the IPaymentProcessor_v1 interface to facilitate + * interaction with a payment client. + * - Implement custom interface which has all the public facing + * functions, errors, events and structs. + * - Pre-defined layout for all contract functions, modifiers, state + * variables etc. + * - Use of the ERC165Upgradeable contract to check for interface + * support. + * + * @custom:security-contact security@inverter.network + * In case of any concerns or findings, please refer + * to our Security Policy at security.inverter.network + * or email us directly! + * + * @custom:version 1.0.0 + * + * @author Inverter Network + */ +contract PP_Template_v1 is IPP_Template_v1, Module_v1 { + //-------------------------------------------------------------------------- + // Libraries + + // Add library usage here + + //-------------------------------------------------------------------------- + // ERC165 + + /// @inheritdoc ERC165Upgradeable + function supportsInterface(bytes4 interfaceId_) + public + view + virtual + override(Module_v1) + returns (bool) + { + return interfaceId_ == type(IPP_Template_v1).interfaceId + || interfaceId_ == type(IPaymentProcessor_v1).interfaceId + || super.supportsInterface(interfaceId_); + } + + //-------------------------------------------------------------------------- + // Constants + + // Add constants here + + //-------------------------------------------------------------------------- + // State + + /// @dev Payout amount multiplier. + uint internal _payoutAmountMultiplier; + + /// @dev Payment ID of the last processed payment order. + uint internal _paymentId; + + //-------------------------------------------------------------------------- + // Modifiers + + /// @dev Checks that the client is calling for itself. + modifier clientIsValid(address client_) { + // Modifier logic moved to internal function for contract size reduction. + _ensureValidClient(client_); + _; + } + + //-------------------------------------------------------------------------- + // Constructor & Init + + /// @inheritdoc Module_v1 + function init( + IOrchestrator_v1 orchestrator_, + Metadata memory metadata_, + bytes memory configData_ + ) external override(Module_v1) initializer { + __Module_init(orchestrator_, metadata_); + + // Decode module specific init data through use of configData bytes. + // This value is an example value used to showcase the setters/getters + // and internal functions/state formating style. + (uint payoutAmountMultiplier_) = abi.decode(configData_, (uint)); + + // Set init state. + _setPayoutAmountMultiplier(payoutAmountMultiplier_); + } + + //-------------------------------------------------------------------------- + // Public (Getters) + + /// @inheritdoc IPP_Template_v1 + function getPayoutAmountMultiplier() + external + view + returns (uint payoutAmount_) + { + return _payoutAmountMultiplier; + } + + //-------------------------------------------------------------------------- + // Public (Mutating) + + /// @inheritdoc IPaymentProcessor_v1 + function processPayments(IERC20PaymentClientBase_v1 client_) + external + clientIsValid(address(client_)) + { + // The IERC20PaymentClientBase_v1 client should be used to access + // created payment orders in the Logic Module (LM) implementing the + // interface. The interface should be referenced to see the different + // functionalities provided by the ERC20PaymentClientBase_v1. + + // Collect orders from the client + IERC20PaymentClientBase_v1.PaymentOrder[] memory orders; + (orders,,) = client_.collectPaymentOrders(); + + // Custom logic to proces the payment orders should be implemented + // below. This template implements a straight forward token transfer + // using the first order of the payment order array for simplicity. + + // Get payment order details + address recipient_ = orders[0].recipient; + address token_ = orders[0].paymentToken; + uint amount_ = orders[0].amount * _payoutAmountMultiplier; + _paymentId = _paymentId + 1; + + // Emit event of the IPaymentProcessor_v1. This is used by Inverter's + // Indexer. + emit PaymentOrderProcessed( + address(client_), recipient_, token_, amount_, 0, 0, 0 + ); + + // Transfer tokens from {IERC20PaymentClientBase_v1} to order + // recipients. + // Please note: When processing multiple payment orders and then + // letting the call revert as in this example might not be the best + // solution. Ways to handle this by implementing the `unclaimable` + // function can be found in the other Payment Processor (PP) + // implementations. + IERC20(token_).transferFrom(address(client_), recipient_, amount_); + + // Inform the client about the amount that was released, to keep + // the accounting correct. + client_.amountPaid(token_, amount_); + + // Emit event of the IPaymentProcessor_v1. This is used by Inverter's + // Indexer. + emit TokensReleased(recipient_, token_, amount_); + } + + /// @inheritdoc IPaymentProcessor_v1 + function cancelRunningPayments(IERC20PaymentClientBase_v1 client_) + external + view + clientIsValid(address(client_)) + { + // This function is used to implement custom logic to cancel running + // payments. If the nature of processing payments is one of direct + // processing then this function can be left empty, return nothing. + return; + } + + /// @inheritdoc IPaymentProcessor_v1 + function unclaimable( + address, /*client_*/ + address, /*token_*/ + address /*paymentReceiver_*/ + ) external pure returns (uint amount_) { + // This function is used to check if there are unclaimable tokens for a + // specific client, token and payment receiver. As this template only + // executes one payment order at a time, this function is not utilzed + // and can return 0. + return 0; + } + + /// @inheritdoc IPaymentProcessor_v1 + function claimPreviouslyUnclaimable( + address, /*client_*/ + address, /*token_*/ + address /*receiver_*/ + ) external pure { + return; + } + + /// @inheritdoc IPaymentProcessor_v1 + function validPaymentOrder( + IERC20PaymentClientBase_v1.PaymentOrder memory order_ + ) external view returns (bool) { + // This function is used to validate the payment order created on the + // client side (LM_PC) with the input required by the Payment Processor + // (PP). The function should return true if the payment order is valid + // and false if it is not. + + // For this template, only the receiver is validated. + return _validPaymentReceiver(order_.recipient); + } + + //-------------------------------------------------------------------------- + // Internal + + /// @dev Internal function to set the new payout amount multiplier. + /// @param newPayoutAmountMultiplier_ Payout amount multiplier to be set in + // the state. Cannot be zero. + function _setPayoutAmountMultiplier(uint newPayoutAmountMultiplier_) + internal + { + if (newPayoutAmountMultiplier_ == 0) { + revert Module__PP_Template_InvalidAmount(); + } + emit NewPayoutAmountMultiplierSet( + _payoutAmountMultiplier, newPayoutAmountMultiplier_ + ); + _payoutAmountMultiplier = newPayoutAmountMultiplier_; + } + + /// @dev Validate whether the address is a valid payment receiver. + /// @param receiver_ Address to validate. + /// @return validPaymentReceiver_ True if address is valid. + function _validPaymentReceiver(address receiver_) + internal + view + returns (bool) + { + return !( + receiver_ == address(0) || receiver_ == _msgSender() + || receiver_ == address(this) + || receiver_ == address(orchestrator()) + || receiver_ == address(orchestrator().fundingManager().token()) + ); + } + + /// @dev Internal function to check whether the client is valid. + /// @param client_ Address to validate. + function _ensureValidClient(address client_) internal view { + if (_msgSender() != client_) { + revert Module__PP_Template__ClientNotValid(); + } + } + + //-------------------------------------------------------------------------- + // Internal override +} diff --git a/src/templates/tests/unit/FM_Template_v1.t.sol b/src/templates/tests/unit/FM_Template_v1.t.sol new file mode 100644 index 000000000..3806448c3 --- /dev/null +++ b/src/templates/tests/unit/FM_Template_v1.t.sol @@ -0,0 +1,179 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.0; + +// Internal +import { + ModuleTest, + IModule_v1, + IOrchestrator_v1 +} from "test/modules/ModuleTest.sol"; +import {OZErrors} from "test/utils/errors/OZErrors.sol"; +import {IFundingManager_v1} from "@fm/IFundingManager_v1.sol"; + +// External +import {Clones} from "@oz/proxy/Clones.sol"; + +// Tests and Mocks +import {FM_Template_v1_Exposed} from + "src/templates/tests/unit/FM_Template_v1_Exposed.sol"; +import {ERC20Mock} from "test/utils/mocks/ERC20Mock.sol"; +import { + IERC20PaymentClientBase_v1, + ERC20PaymentClientBaseV1Mock +} from "test/utils/mocks/modules/paymentClient/ERC20PaymentClientBaseV1Mock.sol"; + +// System under Test (SuT) +import {IFM_Template_v1} from "src/templates/modules/IFM_Template_v1.sol"; + +/** + * @title Inverter Template Funding Manager Tests + * + * @notice Basic template funding manager used to showcase the unit testing + * setup + * + * @dev Not all functions are tested in this template. Placeholders of the + * functions that are not tested are added into the contract. This test + * showcases the following: + * - Inherit from the ModuleTest contract to enable interaction with + * the Inverter workflow. + * - Showcases the setup of the workflow, uses in test unit tests. + * - Pre-defined layout for all setup and functions to be tested. + * - Shows the use of Gherkin for documenting the testing. VS Code + * extension used for formatting is recommended. + * - Shows the use of the modifierInPlace pattern to test the modifier + * placement. + * + * @author Inverter Network + */ +contract FM_Template_v1_Test is ModuleTest { + // ========================================================================= + // Constants + + // ========================================================================= + // State + FM_Template_v1_Exposed fundingManager; + + // Mocks + ERC20Mock orchestratorToken; + ERC20PaymentClientBaseV1Mock paymentClient; + + // ========================================================================= + // Setup + function setUp() public { + // This function is used to setup the unit test + // Deploy the SuT + address impl = address(new FM_Template_v1_Exposed()); + fundingManager = FM_Template_v1_Exposed(Clones.clone(impl)); + + orchestratorToken = new ERC20Mock("Orchestrator Token", "OTK"); + + // Setup the module to test + _setUpOrchestrator(fundingManager); + + // General setup for other contracts in the workflow + _authorizer.setIsAuthorized(address(this), true); + + // Initialize the funding manager with metadata and config data + fundingManager.init( + _orchestrator, _METADATA, abi.encode(address(orchestratorToken)) + ); + + // Setup other modules needed in the unit tests. + // In this case a payment client is needed to test the FM_Template_v1. + paymentClient = new ERC20PaymentClientBaseV1Mock(); + _addLogicModuleToOrchestrator(address(paymentClient)); + } + + // ========================================================================= + // Test: Initialization + + // Test if the orchestrator is correctly set up after initialization + function testInit() public override(ModuleTest) { + assertEq(address(fundingManager.orchestrator()), address(_orchestrator)); + } + + // Test the reinit function + function testReinitFails() public override(ModuleTest) { + vm.expectRevert(OZErrors.Initializable__InvalidInitialization); + fundingManager.init( + _orchestrator, _METADATA, abi.encode(address(orchestratorToken)) + ); + } + + // Test the interface support + function testSupportsInterface() public { + assertTrue( + fundingManager.supportsInterface( + type(IFundingManager_v1).interfaceId + ) + ); + assertTrue( + fundingManager.supportsInterface(type(IFM_Template_v1).interfaceId) + ); + } + + // ========================================================================= + // Test: External (public & external) functions + + // Test external deposit function + + // Test external transferOrchestratorToken function + + // Test external getDepositedAmount function + + // Test external token function + + // ========================================================================= + // Test: Internal (tested through exposed_ functions) + + /* test internal _validateOrchestratorTokenTransfer() + ├── Given zero address receipent + │ └── When the function is called with zero address receipent + │ └── Then the function should revert with Module__FM_Template__ReceiverNotValid error + ├── Given zero amount + │ └── When the function is called with zero amount + │ └── Then the function should revert with Module__FM_Template__AmountNotValid error + └── Given valid receipent and amount but not enough balance + └── When the function is called with valid receipent and amount but not enough balance + └── Then the function should revert with Module__FM_Template_InvalidAmount error + */ + function testInternalValidateOrchestratorTokenTransfer_FailsZeroAddressReceipent( + ) public { + vm.expectRevert( + IFM_Template_v1.Module__FM_Template__ReceiverNotValid.selector + ); + fundingManager.exposed_validateOrchestratorTokenTransfer( + address(0), 1e18 + ); + } + + function testInternalValidateOrchestratorTokenTransfer_FailsZeroAmount() + public + { + vm.expectRevert( + IFM_Template_v1.Module__FM_Template_InvalidAmount.selector + ); + fundingManager.exposed_validateOrchestratorTokenTransfer( + address(this), 0 + ); + } + + function testInternalValidateOrchestratorTokenTransfer_FailsNotEnoughBalance( + ) public { + orchestratorToken.mint(address(1), 1 ether); + + vm.startPrank(address(1)); + { + orchestratorToken.approve(address(fundingManager), 1 ether); + fundingManager.deposit(1 ether); + } + vm.stopPrank(); + + vm.expectRevert( + IFM_Template_v1.Module__FM_Template_InvalidAmount.selector + ); + fundingManager.exposed_validateOrchestratorTokenTransfer( + address(1), 10 ether + ); + } +} diff --git a/src/templates/tests/unit/FM_Template_v1_Exposed.sol b/src/templates/tests/unit/FM_Template_v1_Exposed.sol new file mode 100644 index 000000000..19f588316 --- /dev/null +++ b/src/templates/tests/unit/FM_Template_v1_Exposed.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity ^0.8.0; + +import {FM_Template_v1} from "src/templates/modules/FM_Template_v1.sol"; + +// Access Mock of the FM_Template_v1 contract for Testing. +contract FM_Template_v1_Exposed is FM_Template_v1 { + // Use the `exposed_` prefix for functions to expose internal functions for testing purposes only. + + function exposed_validateOrchestratorTokenTransfer( + address to_, + uint amount_ + ) external view { + return _validateOrchestratorTokenTransfer(to_, amount_); + } +} diff --git a/src/templates/tests/unit/PP_Template_v1.t.sol b/src/templates/tests/unit/PP_Template_v1.t.sol new file mode 100644 index 000000000..a1ddfd6df --- /dev/null +++ b/src/templates/tests/unit/PP_Template_v1.t.sol @@ -0,0 +1,213 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.0; + +// Internal +import { + ModuleTest, + IModule_v1, + IOrchestrator_v1 +} from "test/modules/ModuleTest.sol"; +import {OZErrors} from "test/utils/errors/OZErrors.sol"; + +// External +import {Clones} from "@oz/proxy/Clones.sol"; + +// Tests and Mocks +import {PP_Template_v1_Exposed} from + "src/templates/tests/unit/PP_Template_v1_Exposed.sol"; +import { + IERC20PaymentClientBase_v1, + ERC20PaymentClientBaseV1Mock, + ERC20Mock +} from "test/utils/mocks/modules/paymentClient/ERC20PaymentClientBaseV1Mock.sol"; + +// System under Test (SuT) +import { + IPP_Template_v1, + IPaymentProcessor_v1 +} from "src/templates/modules/PP_Template_v1.sol"; + +/** + * @title Inverter Template Payment Processor Tests + * + * @notice Basic template payment processor used to showcase the unit testing + * setup + * + * @dev Not all functions are tested in this template. Placeholders of the + * functions that are not tested are added into the contract. This test + * showcases the following: + * - Inherit from the ModuleTest contract to enable interaction with + * the Inverter workflow. + * - Showcases the setup of the workflow, uses in test unit tests. + * - Pre-defined layout for all setup and functions to be tested. + * - Shows the use of Gherkin for documenting the testing. VS Code + * extension used for formatting is recommended. + * - Shows the use of the modifierInPlace pattern to test the modifier + * placement. + * + * @author Inverter Network + */ +contract PP_Template_v1_Test is ModuleTest { + //-------------------------------------------------------------------------- + // Constants + uint internal constant _payoutAmountMultiplier = 2; + + //-------------------------------------------------------------------------- + // State + + // System under test (SuT) + PP_Template_v1_Exposed paymentProcessor; + // Mocks + ERC20PaymentClientBaseV1Mock paymentClient; + + //-------------------------------------------------------------------------- + // Setup + function setUp() public { + // This function is used to setup the unit test + // Deploy the SuT + address impl = address(new PP_Template_v1_Exposed()); + paymentProcessor = PP_Template_v1_Exposed(Clones.clone(impl)); + + // Setup the module to test + _setUpOrchestrator(paymentProcessor); + + // General setup for other contracts in the workflow + _authorizer.setIsAuthorized(address(this), true); + + // Initiate the PP with the medata and config data + paymentProcessor.init( + _orchestrator, _METADATA, abi.encode(_payoutAmountMultiplier) + ); + + // Setup other modules needed in the unit tests. + // In this case a payment client is needed to test the PP_Template_v1. + impl = address(new ERC20PaymentClientBaseV1Mock()); + paymentClient = ERC20PaymentClientBaseV1Mock(Clones.clone(impl)); + // Adding the payment client is done through a timelock mechanism + _orchestrator.initiateAddModuleWithTimelock(address(paymentClient)); + vm.warp(block.timestamp + _orchestrator.MODULE_UPDATE_TIMELOCK()); + _orchestrator.executeAddModule(address(paymentClient)); + // Init payment client + paymentClient.init(_orchestrator, _METADATA, bytes("")); + paymentClient.setIsAuthorized(address(paymentProcessor), true); + paymentClient.setToken(_token); + } + + //-------------------------------------------------------------------------- + // Test: Initialization + + // Test if the orchestrator is correctly set + function testInit() public override(ModuleTest) { + assertEq( + address(paymentProcessor.orchestrator()), address(_orchestrator) + ); + } + + // Test the interface support + function testSupportsInterface() public { + assertTrue( + paymentProcessor.supportsInterface( + type(IPaymentProcessor_v1).interfaceId + ) + ); + assertTrue( + paymentProcessor.supportsInterface( + type(IPP_Template_v1).interfaceId + ) + ); + } + + // Test the reinit function + function testReinitFails() public override(ModuleTest) { + vm.expectRevert(OZErrors.Initializable__InvalidInitialization); + paymentProcessor.init( + _orchestrator, _METADATA, abi.encode(_payoutAmountMultiplier) + ); + } + + //-------------------------------------------------------------------------- + // Test: Modifiers + + /* Test validClient modifier in place (extensive testing done through internal modifier functions) + └── Given the modifier is in place + └── When the function processPayment() is called + └── Then it should revert + */ + function testProcessPayments_modifierInPlace() public { + ERC20PaymentClientBaseV1Mock nonRegisteredClient = + new ERC20PaymentClientBaseV1Mock(); + + vm.expectRevert( + IPP_Template_v1.Module__PP_Template__ClientNotValid.selector + ); + paymentProcessor.processPayments(nonRegisteredClient); + } + + //-------------------------------------------------------------------------- + // Test: External (public & external) + + // Test external processPayments() function + + // Test external cancelRunningPayments() function + + // Test external unclaimable() function + + // Test external claimPreviouslyUnclaimable() function + + // Test external validPaymentOrder() function + + //-------------------------------------------------------------------------- + // Test: Internal (tested through exposed_functions) + + /* test internal _setPayoutAmountMultiplier() + ├── Given the newPayoutAmount == 0 + │ └── When the function _setPayoutAmountMultiplier() is called + │ └── Then it should revert + └── Given the newPayoutAmount != 0 + └── When the function _setPayoutAmountMultiplier() is called + └── Then it should emit the event + └── And it should set the state correctly + */ + + function testInternalSetPayoutAmountMultiplier_FailsGivenZero() public { + vm.expectRevert( + IPP_Template_v1.Module__PP_Template_InvalidAmount.selector + ); + paymentProcessor.exposed_setPayoutAmountMultiplier(0); + } + + function testInternalSetPayoutAmountMultiplier_FailsGivenZeroAfter( + uint newPayoutAmountMultiplier_ + ) public { + // Set up assumption + vm.assume(newPayoutAmountMultiplier_ > 0); + + // Check initial state + assertEq( + paymentProcessor.getPayoutAmountMultiplier(), + _payoutAmountMultiplier + ); + + // Test internal function through mock exposed function + vm.expectEmit(true, true, true, true); + emit IPP_Template_v1.NewPayoutAmountMultiplierSet( + _payoutAmountMultiplier, newPayoutAmountMultiplier_ + ); + paymentProcessor.exposed_setPayoutAmountMultiplier( + newPayoutAmountMultiplier_ + ); + + // Test final state + assertEq( + paymentProcessor.getPayoutAmountMultiplier(), + newPayoutAmountMultiplier_ + ); + } + + // Test the internal _validPaymentReceiver() function + + // Test the internal _validClientModifier() function + + //-------------------------------------------------------------------------- + // Helper Functions +} diff --git a/src/templates/tests/unit/PP_Template_v1_Exposed.sol b/src/templates/tests/unit/PP_Template_v1_Exposed.sol new file mode 100644 index 000000000..fe3b748ad --- /dev/null +++ b/src/templates/tests/unit/PP_Template_v1_Exposed.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity ^0.8.0; + +// Internal +import {PP_Template_v1} from "src/templates/modules/PP_Template_v1.sol"; + +// Access Mock of the PP_Template_v1 contract for Testing. +contract PP_Template_v1_Exposed is PP_Template_v1 { + // Use the `exposed_` prefix for functions to expose internal contract for + // testing. + + function exposed_setPayoutAmountMultiplier(uint newPayoutAmountMultiplier_) + external + { + _setPayoutAmountMultiplier(newPayoutAmountMultiplier_); + } + + function exposed_validPaymentReceiver(address receiver_) + external + view + returns (bool validPaymentReceiver_) + { + validPaymentReceiver_ = _validPaymentReceiver(receiver_); + } + + function exposed_ensureValidClient(address client_) external view { + _ensureValidClient(client_); + } +} From 167f624d9765c7d6139b1e4330ddd7c658b513fe Mon Sep 17 00:00:00 2001 From: Zuhaib Mohammed Date: Thu, 28 Nov 2024 04:46:12 -0800 Subject: [PATCH 54/93] emit PaymentProcessed via Interface --- ...chainBase_v1.sol => CrossChainBase_v1.sol} | 0 .../PP_Connext_Crosschain_v1_Test.t.sol | 19 +++++-------------- 2 files changed, 5 insertions(+), 14 deletions(-) rename src/modules/paymentProcessor/abstracts/{CrosschainBase_v1.sol => CrossChainBase_v1.sol} (100%) diff --git a/src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol b/src/modules/paymentProcessor/abstracts/CrossChainBase_v1.sol similarity index 100% rename from src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol rename to src/modules/paymentProcessor/abstracts/CrossChainBase_v1.sol diff --git a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol index 02331995f..9543a8f88 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol @@ -18,6 +18,8 @@ import {IERC20PaymentClientBase_v1} from "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; import {ICrossChainBase_v1} from "src/modules/paymentProcessor/interfaces/ICrosschainBase_v1.sol"; +import {IPaymentProcessor_v1} from + "src/modules/paymentProcessor/IPaymentProcessor_v1.sol"; // Tests and Mocks import {Mock_EverclearPayment} from @@ -46,17 +48,6 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { uint public chainId; - // Add this event definition at the contract level - event PaymentOrderProcessed( - address indexed paymentClient, - address indexed recipient, - address indexed paymentToken, - uint amount, - uint start, - uint cliff, - uint end - ); - // Add these as contract state variables address public mockConnextBridge; address public mockEverClearSpoke; @@ -181,7 +172,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { // Expect the event vm.expectEmit(true, true, true, true); - emit PaymentOrderProcessed( + emit IPaymentProcessor_v1.PaymentOrderProcessed( address(paymentClient), testRecipient, address(token), @@ -236,7 +227,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { // Expect events for each payment for (uint i = 0; i < numRecipients; i++) { vm.expectEmit(true, true, true, true); - emit PaymentOrderProcessed( + emit IPaymentProcessor_v1.PaymentOrderProcessed( address(paymentClient), setupRecipients[i], address(token), @@ -485,7 +476,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { // Expectations vm.expectEmit(true, true, true, true); - emit PaymentOrderProcessed( + emit IPaymentProcessor_v1.PaymentOrderProcessed( address(paymentClient), testRecipient, address(token), From 66f5208e11c61f8713d61e4d0476c028b602d28c Mon Sep 17 00:00:00 2001 From: Zuhaib Mohammed Date: Thu, 28 Nov 2024 05:54:44 -0800 Subject: [PATCH 55/93] chore: use exposed payment processor --- .../PP_Connext_Crosschain_v1_Exposed.sol | 25 +++++++++++++++++++ .../PP_Connext_Crosschain_v1_Test.t.sol | 9 ++++--- 2 files changed, 30 insertions(+), 4 deletions(-) create mode 100644 test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Exposed.sol diff --git a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Exposed.sol b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Exposed.sol new file mode 100644 index 000000000..6c43e5eeb --- /dev/null +++ b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Exposed.sol @@ -0,0 +1,25 @@ +pragma solidity ^0.8.20; + +import {PP_Connext_Crosschain_v1} from + "src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol"; +import {IERC20PaymentClientBase_v1} from + "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; +import {IERC20} from "@oz/token/ERC20/IERC20.sol"; + +contract PP_Connext_Crosschain_v1_Exposed is PP_Connext_Crosschain_v1 { + // Expose internal _executeBridgeTransfer function + function exposed_executeBridgeTransfer( + IERC20PaymentClientBase_v1.PaymentOrder memory order, + bytes memory executionData + ) external returns (bytes memory) { + return _executeBridgeTransfer(order, executionData); + } + + // Expose internal xcall function + function exposed_xcall( + IERC20PaymentClientBase_v1.PaymentOrder memory order, + bytes memory executionData + ) external returns (bytes32) { + return xcall(order, executionData); + } +} diff --git a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol index 9543a8f88..e2818adb8 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol @@ -20,7 +20,8 @@ import {ICrossChainBase_v1} from "src/modules/paymentProcessor/interfaces/ICrosschainBase_v1.sol"; import {IPaymentProcessor_v1} from "src/modules/paymentProcessor/IPaymentProcessor_v1.sol"; - +import {PP_Connext_Crosschain_v1_Exposed} from + "test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Exposed.sol"; // Tests and Mocks import {Mock_EverclearPayment} from "test/modules/paymentProcessor/bridging/abstracts/mocks/Mock_EverclearPayment.sol"; @@ -40,7 +41,7 @@ import {IWETH} from "src/modules/paymentProcessor/interfaces/IWETH.sol"; import "forge-std/console2.sol"; contract PP_Connext_Crosschain_v1_Test is ModuleTest { - PP_Connext_Crosschain_v1 public paymentProcessor; + PP_Connext_Crosschain_v1_Exposed public paymentProcessor; Mock_EverclearPayment public everclearPaymentMock; ERC20Mock public token; ERC20PaymentClientBaseV1Mock paymentClient; @@ -77,8 +78,8 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { mockWeth = address(weth); // Deploy payment processor via clone - address impl = address(new PP_Connext_Crosschain_v1()); - paymentProcessor = PP_Connext_Crosschain_v1(Clones.clone(impl)); + address impl = address(new PP_Connext_Crosschain_v1_Exposed()); + paymentProcessor = PP_Connext_Crosschain_v1_Exposed(Clones.clone(impl)); _setUpOrchestrator(paymentProcessor); _authorizer.setIsAuthorized(address(this), true); From bdb3c655015d76e4ea16a860c63f34b38ebf3de9 Mon Sep 17 00:00:00 2001 From: Leandro Faria Date: Mon, 9 Dec 2024 16:33:55 -0500 Subject: [PATCH 56/93] update gherkin for tests --- LICENSE | 2 +- src/templates/tests/unit/FM_Template_v1.t.sol | 2 +- .../abstracts/CrossChainBase_v1.t.sol | 50 +++++++-------- .../PP_Connext_Crosschain_v1_Test.t.sol | 64 +++++++------------ 4 files changed, 46 insertions(+), 72 deletions(-) diff --git a/LICENSE b/LICENSE index 8e9122e4d..1aa90cc3d 100644 --- a/LICENSE +++ b/LICENSE @@ -148,7 +148,7 @@ of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. - Each version is given a distinguishing version number. If the + Each version is distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and diff --git a/src/templates/tests/unit/FM_Template_v1.t.sol b/src/templates/tests/unit/FM_Template_v1.t.sol index 3806448c3..30fc599db 100644 --- a/src/templates/tests/unit/FM_Template_v1.t.sol +++ b/src/templates/tests/unit/FM_Template_v1.t.sol @@ -64,7 +64,7 @@ contract FM_Template_v1_Test is ModuleTest { // Deploy the SuT address impl = address(new FM_Template_v1_Exposed()); fundingManager = FM_Template_v1_Exposed(Clones.clone(impl)); - + orchestratorToken = new ERC20Mock("Orchestrator Token", "OTK"); // Setup the module to test diff --git a/test/modules/paymentProcessor/bridging/abstracts/CrossChainBase_v1.t.sol b/test/modules/paymentProcessor/bridging/abstracts/CrossChainBase_v1.t.sol index 50a51f6cb..f62d5922e 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/CrossChainBase_v1.t.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/CrossChainBase_v1.t.sol @@ -66,47 +66,40 @@ contract CrossChainBase_v1_Test is ModuleTest { paymentClient.setIsAuthorized(address(crossChainBase), true); paymentClient.setToken(_token); } - /* Test CrossChainBase functionality - ├── Given the contract is initialized - │ └── When checking interface support - │ └── Then it should support ICrossChainBase_v1 - │ └── Then it should not support random interfaces - ├── Given the contract is already initialized - │ └── When trying to reinitialize - │ └── Then it should revert - └── Given a valid payment order - └── When executeBridgeTransfer is called - └── Then it should return empty bytes - */ - //-------------------------------------------------------------------------- //Test: Initialization - /* Given the contract is initialized - When checking the initialization - Then it should have the correct orchestrator address */ + /* + └── Given the contract is not initialized + └── When initializing the contract + └── Then it should set the correct orchestrator address */ + function testInit() public override(ModuleTest) { assertEq(address(crossChainBase.orchestrator()), address(_orchestrator)); } //-------------------------------------------------------------------------- //Test: Interface Support - /* Given the contract is initialized - When checking interface support - Then it should support ICrossChainBase_v1 - And it should not support random interfaces */ + /* + └── Given the contract is initialized + └── When checking for ICrossChainBase_v1 interface support + └── Then it should return true + └── When checking for an unknown interface + └── Then it should return false */ function testSupportsInterface() public { // Test for ICrossChainBase_v1 interface support bytes4 interfaceId = type(ICrossChainBase_v1).interfaceId; assertTrue(crossChainBase.supportsInterface(interfaceId)); + } - // Test for random interface ID (should return false) + function testSupportsInterface_revertsGivenUnknownInterface() public { bytes4 randomInterfaceId = bytes4(keccak256("random()")); assertFalse(crossChainBase.supportsInterface(randomInterfaceId)); } - /* Given the contract is already initialized - When trying to reinitialize - Then it should revert */ + /* + └── Given the contract is already initialized + └── When trying to reinitialize + └── Then it should revert with Initializable__InvalidInitialization */ function testReinitFails() public override(ModuleTest) { vm.expectRevert(OZErrors.Initializable__InvalidInitialization); crossChainBase.init(_orchestrator, _METADATA, abi.encode(1)); @@ -114,10 +107,11 @@ contract CrossChainBase_v1_Test is ModuleTest { //-------------------------------------------------------------------------- //Test: executeBridgeTransfer - /* Given a valid payment order - When executeBridgeTransfer is called - Then it should return empty bytes */ - function testExecuteBridgeTransfer() public { + /* + └── Given an empty payment order is created + └── When executeBridgeTransfer is called + └── Then it should return empty bytes */ + function testExecuteBridgeTransfer_worksGivenEmptyPaymentOrder() public { address[] memory setupRecipients = new address[](1); setupRecipients[0] = address(1); uint[] memory setupAmounts = new uint[](1); diff --git a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol index e2818adb8..3dc53282c 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol @@ -148,15 +148,14 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { paymentProcessor.init(_orchestrator, _METADATA, abi.encode(1)); } - /* Test single payment processing - ├── Given a valid payment order - │ ├── When processing through Connext bridge - │ │ ├── Then it should emit PaymentProcessed event - │ │ ├── Then it should transfer tokens correctly - │ │ └── Then it should create Connext intent - │ │ - │ └── When checking bridge data - │ └── Then it should contain valid Connext intent ID + /* Test single payment processin + └── Given a valid payment order + └── When processing through Connext bridge + └── Then should emit PaymentProcessed event + ├── And should transfer tokens correctly + ├── And should create Connext intent + ├── When checking bridge data + └── Then should contain valid Connext intent ID */ function testFuzz_ProcessPayments_singlePayment( address testRecipient, @@ -188,16 +187,17 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { } /* Test multiple payment processing - ├── Given multiple valid payment orders - │ ├── When processing through Connext bridge - │ │ ├── Then it should emit PaymentProcessed events for each payment - │ │ ├── Then it should batch transfer tokens correctly - │ │ └── Then it should create multiple Connext intents - │ │ - │ └── When checking bridge data - │ └── Then it should contain valid Connext intent IDs + └── Given multiple valid payment orders + └── When processing cross-chain payments + └── Then it should emit PaymentProcessed events for each payment + ├── And it should batch transfer tokens correctly + └── And it should create multiple cross-chain intents + + . + └── When checking bridge data + └── Then it should contain valid intent IDs */ - function testFuzz_ProcessPayments_multiplePayment( + function testProcessMultiplePayments_worksGivenMultipleValidPaymentOrders( uint8 numRecipients, address testRecipient, uint96 baseAmount @@ -241,12 +241,14 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { // Process payments paymentProcessor.processPayments(client, executionData); + + //should be checking in the mock for valid bridge data } /* Test empty payment processing └── When processing with no payment orders ├── Then it should complete successfully - └── Then bridge data should remain empty + └── And the bridge data should remain empty */ function test_ProcessPayments_noPayments() public { // Process payments and verify _bridgeData mapping is not updated @@ -281,34 +283,12 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { paymentProcessor.processPayments(client, invalidExecutionData); } - /* Test bridge data reversion - ├── Given invalid execution data - │ └── When attempting to process payment - │ ├── Then it should revert - │ └── Then no bridge data should be stored - */ - function testFuzz_returnsCorrectBridgeDataRevert( - address testRecipient, - uint testAmount - ) public { - vm.assume(testRecipient != address(0)); - vm.assume(testAmount > 0 && testAmount < MINTED_SUPPLY); // Keeping within our minted balance - _setupSinglePayment(testRecipient, testAmount); - - IERC20PaymentClientBase_v1 client = - IERC20PaymentClientBase_v1(address(paymentClient)); - - // Process payments - vm.expectRevert(); - paymentProcessor.processPayments(client, invalidExecutionData); - } - /* Test empty execution data ├── Given empty execution data bytes │ └── When attempting to process payment │ └── Then it should revert with InvalidExecutionData */ - function testFuzz_ProcessPayments_emptyExecutionData( + function testProcessPayments_revertsGivenEmptyExecutionData( address testRecipient, uint testAmount ) public { From ee1e448c94b5b13475b15e71dc0530bf8b7481a2 Mon Sep 17 00:00:00 2001 From: Leandro Faria Date: Mon, 9 Dec 2024 17:17:31 -0500 Subject: [PATCH 57/93] add assertions to payment processor test --- .../PP_Connext_Crosschain_v1.sol | 10 ++- .../abstracts/CrossChainBase_v1.t.sol | 5 +- .../PP_Connext_Crosschain_v1_Exposed.sol | 4 +- .../PP_Connext_Crosschain_v1_Test.t.sol | 78 ++++++++++--------- 4 files changed, 54 insertions(+), 43 deletions(-) diff --git a/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol b/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol index 13910bc19..6e50c4175 100644 --- a/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol +++ b/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol @@ -39,6 +39,10 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { IEverclearSpoke public everClearSpoke; IWETH public weth; + /// @dev Tracks all stream details for all payment orders of a paymentReceiver for a specific paymentClient. + /// paymentClient => paymentReceiver => intentId. + mapping(address => mapping(address => bytes32)) public intentId; + /// @notice Initializes the payment processor module /// @param orchestrator_ The address of the orchestrator contract /// @param metadata Module metadata @@ -65,7 +69,7 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { bytes memory executionData ) internal override returns (bytes memory) { //@notice call the connextBridgeLogic to execute the bridge transfer - bytes32 intentId = xcall(order, executionData); + bytes32 intentId = createCrossChainIntent(order, executionData); if (intentId == bytes32(0)) { revert Module__PP_Crosschain__MessageDeliveryFailed( 8453, 8453, executionData @@ -100,7 +104,7 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { orders[i].end ); _paymentId++; - + intentId[address(client)][orders[i].recipient] = bytes32(bridgeData); // Inform the client about the processed amount client.amountPaid(orders[i].paymentToken, orders[i].amount); } @@ -110,7 +114,7 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { /// @param order The payment order to be processed /// @param executionData Encoded data containing maxFee and TTL for the transfer /// @return intentId The unique identifier for the cross-chain transfer - function xcall( + function createCrossChainIntent( IERC20PaymentClientBase_v1.PaymentOrder memory order, bytes memory executionData ) internal returns (bytes32) { diff --git a/test/modules/paymentProcessor/bridging/abstracts/CrossChainBase_v1.t.sol b/test/modules/paymentProcessor/bridging/abstracts/CrossChainBase_v1.t.sol index f62d5922e..1e641fd5f 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/CrossChainBase_v1.t.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/CrossChainBase_v1.t.sol @@ -12,9 +12,6 @@ import {ICrossChainBase_v1} from "src/modules/paymentProcessor/interfaces/ICrosschainBase_v1.sol"; import {CrossChainBase_v1} from "src/modules/paymentProcessor/abstracts/CrossChainBase_v1.sol"; -//External Dependencies -import {Clones} from "@oz/proxy/Clones.sol"; - import { IERC20PaymentClientBase_v1, ERC20PaymentClientBaseV1Mock, @@ -26,7 +23,9 @@ import {IPaymentProcessor_v1} from "src/orchestrator/interfaces/IOrchestrator_v1.sol"; import {ICrossChainBase_v1} from "src/modules/paymentProcessor/interfaces/ICrosschainBase_v1.sol"; +//External Dependencies import {OZErrors} from "test/utils/errors/OZErrors.sol"; +import {Clones} from "@oz/proxy/Clones.sol"; contract CrossChainBase_v1_Test is ModuleTest { //-------------------------------------------------------------------------- diff --git a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Exposed.sol b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Exposed.sol index 6c43e5eeb..5c71414fe 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Exposed.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Exposed.sol @@ -16,10 +16,10 @@ contract PP_Connext_Crosschain_v1_Exposed is PP_Connext_Crosschain_v1 { } // Expose internal xcall function - function exposed_xcall( + function exposed_createCrossChainIntent( IERC20PaymentClientBase_v1.PaymentOrder memory order, bytes memory executionData ) external returns (bytes32) { - return xcall(order, executionData); + return createCrossChainIntent(order, executionData); } } diff --git a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol index 3dc53282c..226ddb63c 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol @@ -27,8 +27,7 @@ import {Mock_EverclearPayment} from "test/modules/paymentProcessor/bridging/abstracts/mocks/Mock_EverclearPayment.sol"; import { IERC20PaymentClientBase_v1, - ERC20PaymentClientBaseV1Mock, - ERC20Mock + ERC20PaymentClientBaseV1Mock } from "test/utils/mocks/modules/paymentClient/ERC20PaymentClientBaseV1Mock.sol"; import { ModuleTest, @@ -43,7 +42,6 @@ import "forge-std/console2.sol"; contract PP_Connext_Crosschain_v1_Test is ModuleTest { PP_Connext_Crosschain_v1_Exposed public paymentProcessor; Mock_EverclearPayment public everclearPaymentMock; - ERC20Mock public token; ERC20PaymentClientBaseV1Mock paymentClient; IWETH public weth; @@ -69,9 +67,6 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { executionData = abi.encode(maxFee, ttl); invalidExecutionData = abi.encode(address(0)); - // Deploy test token - token = new ERC20Mock("Test Token", "TEST"); - // Deploy mock contracts and set addresses everclearPaymentMock = new Mock_EverclearPayment(); mockEverClearSpoke = address(everclearPaymentMock); @@ -98,7 +93,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { // Configure payment client paymentClient.init(_orchestrator, _METADATA, bytes("")); paymentClient.setIsAuthorized(address(paymentProcessor), true); - paymentClient.setToken(token); + paymentClient.setToken(_token); _setupInitialBalances(); } @@ -148,14 +143,13 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { paymentProcessor.init(_orchestrator, _METADATA, abi.encode(1)); } - /* Test single payment processin + /* Test single payment processing └── Given a valid payment order └── When processing through Connext bridge └── Then should emit PaymentProcessed event - ├── And should transfer tokens correctly ├── And should create Connext intent - ├── When checking bridge data - └── Then should contain valid Connext intent ID + └── When checking bridge data + └── Then a valid intent ID should be stored */ function testFuzz_ProcessPayments_singlePayment( address testRecipient, @@ -175,7 +169,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { emit IPaymentProcessor_v1.PaymentOrderProcessed( address(paymentClient), testRecipient, - address(token), + address(_token), testAmount, block.timestamp, 0, @@ -184,6 +178,12 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { // Process payments paymentProcessor.processPayments(client, executionData); + bytes32 intentId = + paymentProcessor.intentId(address(paymentClient), testRecipient); + assertEq( + uint(everclearPaymentMock.status(intentId)), + uint(Mock_EverclearPayment.IntentStatus.ADDED) + ); } /* Test multiple payment processing @@ -231,7 +231,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { emit IPaymentProcessor_v1.PaymentOrderProcessed( address(paymentClient), setupRecipients[i], - address(token), + address(_token), setupAmounts[i], block.timestamp, 0, @@ -241,8 +241,16 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { // Process payments paymentProcessor.processPayments(client, executionData); - //should be checking in the mock for valid bridge data + for (uint i = 0; i < numRecipients; i++) { + bytes32 intentId = paymentProcessor.intentId( + address(paymentClient), setupRecipients[i] + ); + assertEq( + uint(everclearPaymentMock.status(intentId)), + uint(Mock_EverclearPayment.IntentStatus.ADDED) + ); + } } /* Test empty payment processing @@ -357,9 +365,9 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { /* Test bridge data storage ├── Given a valid payment order │ └── When processing payment - │ ├── Then bridge data should not be empty - │ ├── Then intent ID should be stored correctly - │ └── Then intent status should be ADDED in Everclear spoke + │ └── Then bridge data should not be empty + │ └── And intent ID should be stored correctly + └── And intent status should be ADDED in Everclear spoke */ function testFuzz_returnsCorrectBridgeData( address testRecipient, @@ -402,9 +410,9 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { } /* Test insufficient balance - ├── Given payment amount exceeds available balance - │ └── When attempting to process payment - │ └── Then it should revert with ERC20InsufficientBalance + Given payment amount exceeds available balance + When attempting to process payment + Then it should revert with ERC20InsufficientBalance */ function testFuzz_ProcessPayments_InsufficientBalance( address testRecipient, @@ -421,7 +429,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { abi.encodeWithSelector( IERC20Errors.ERC20InsufficientBalance.selector, address(this), - token.balanceOf(address(paymentProcessor)), + _token.balanceOf(address(paymentProcessor)), testAmount ) ); @@ -429,11 +437,11 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { } /* Test edge case amounts - ├── Given payment processor has exactly required amount - │ └── When processing payment - │ ├── Then it should process successfully - │ ├── Then it should emit PaymentProcessed event - │ └── Then it should handle exact balance correctly + └── Given payment processor has exactly required amount + └── When processing payment + └── Then it should process successfully + └── And should emit PaymentProcessed event + └── And should handle exact balance correctly */ function testFuzz_ProcessPayments_EdgeCaseAmounts( address testRecipient, @@ -444,14 +452,14 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { vm.assume(testRecipient != address(0)); // Setup - Clear existing balance - uint currentBalance = token.balanceOf(address(paymentProcessor)); + uint currentBalance = _token.balanceOf(address(paymentProcessor)); if (currentBalance > 0) { vm.prank(address(paymentProcessor)); - token.transfer(address(1), currentBalance); + _token.transfer(address(1), currentBalance); } // Setup - Mint exact amount needed - token.mint(address(paymentProcessor), testAmount); + _token.mint(address(paymentProcessor), testAmount); _setupSinglePayment(testRecipient, testAmount); @@ -460,7 +468,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { emit IPaymentProcessor_v1.PaymentOrderProcessed( address(paymentClient), testRecipient, - address(token), + address(_token), testAmount, block.timestamp, 0, @@ -505,7 +513,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { for (uint i = 0; i < orderCount; i++) { orders[i] = IERC20PaymentClientBase_v1.PaymentOrder({ recipient: recipients[i], - paymentToken: address(token), + paymentToken: address(_token), amount: amounts[i], start: block.timestamp, cliff: 0, @@ -517,11 +525,11 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { function _setupInitialBalances() internal { // Setup token approvals and initial balances - token.mint(address(this), MINTED_SUPPLY); - token.approve(address(paymentProcessor), type(uint).max); + _token.mint(address(this), MINTED_SUPPLY); + _token.approve(address(paymentProcessor), type(uint).max); - token.mint(address(paymentProcessor), MINTED_SUPPLY); // Mint tokens to processor + _token.mint(address(paymentProcessor), MINTED_SUPPLY); // Mint _tokens to processor vm.prank(address(paymentProcessor)); - token.approve(address(paymentProcessor), type(uint).max); // Processor approves bridge logic + _token.approve(address(paymentProcessor), type(uint).max); // Processor approves bridge logic } } From 918b836ccdbfde03136eac94942a4ca3f7014375 Mon Sep 17 00:00:00 2001 From: Leandro Faria Date: Mon, 9 Dec 2024 18:15:43 -0500 Subject: [PATCH 58/93] update gherkin and remove vs code settings --- .vscode/settings.json | 14 +--- .../PP_Connext_Crosschain_v1_Test.t.sol | 71 ++++++++----------- 2 files changed, 30 insertions(+), 55 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 5c9e0f9e8..c89d041c6 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,17 +7,5 @@ "search.exclude": { "lib": true }, - "editor.formatOnSave": true, - "wake.compiler.solc.remappings": [ - "@oz-up/=lib/openzeppelin-contracts-upgradeable/contracts/", - "@oz/=lib/openzeppelin-contracts/contracts/", - "@df/=lib/deterministic-factory/src/", - "@uma/=lib/uma-protocol/packages/core/contracts/", - "@ex/=src/external/", - "@fm/=src/modules/fundingManager/", - "@pp/=src/modules/paymentProcessor/", - "@lm/=src/modules/logicModule/", - "@lm_pc/=src/modules/logicModule/paymentClient/", - "@aut/=src/modules/authorizer/" - ] + "editor.formatOnSave": true } \ No newline at end of file diff --git a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol index 226ddb63c..aa6cfa18d 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol @@ -99,9 +99,6 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { } /* Test initialization - └── When the contract is initialized - ├── Then it should set the orchestrator correctly - └── Then it should set up Connext configuration properly */ function testInit() public override(ModuleTest) { assertEq( @@ -110,12 +107,6 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { } /* Test interface support - ├── When checking IModule_v1 interface - │ └── Then it should return true - ├── When checking ICrossChainBase_v1 interface - │ └── Then it should return true - └── When checking random interface - └── Then it should return false */ function testSupportsInterface() public { // Test for IModule_v1 interface @@ -134,9 +125,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { assertFalse(paymentProcessor.supportsInterface(0xffffffff)); } - /* Test reinitialization protection - └── When trying to reinitialize an already initialized contract - └── Then it should revert with InvalidInitialization + /* Test reinitialization */ function testReinitFails() public override(ModuleTest) { vm.expectRevert(OZErrors.Initializable__InvalidInitialization); @@ -144,14 +133,12 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { } /* Test single payment processing - └── Given a valid payment order - └── When processing through Connext bridge - └── Then should emit PaymentProcessed event - ├── And should create Connext intent - └── When checking bridge data - └── Then a valid intent ID should be stored + └── Given single valid payment order + └── When processing cross-chain payments + └── Then it should emit PaymentProcessed events for payment + └── And it should create cross-chain intent */ - function testFuzz_ProcessPayments_singlePayment( + function testProcessPayments_worksGivenSingleValidPaymentOrder( address testRecipient, uint testAmount ) public { @@ -188,16 +175,12 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { /* Test multiple payment processing └── Given multiple valid payment orders - └── When processing cross-chain payments - └── Then it should emit PaymentProcessed events for each payment - ├── And it should batch transfer tokens correctly - └── And it should create multiple cross-chain intents - - . - └── When checking bridge data - └── Then it should contain valid intent IDs + └── When processing cross-chain payments + └── Then it should emit PaymentProcessed events for each payment + ├── And it should create multiple cross-chain intents + └── And it should contain valid intent IDs */ - function testProcessMultiplePayments_worksGivenMultipleValidPaymentOrders( + function testProcessPayments_worksGivenMultipleValidPaymentOrders( uint8 numRecipients, address testRecipient, uint96 baseAmount @@ -258,7 +241,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { ├── Then it should complete successfully └── And the bridge data should remain empty */ - function test_ProcessPayments_noPayments() public { + function testProcessPayments_worksGivenNoPaymentOrders() public { // Process payments and verify _bridgeData mapping is not updated paymentProcessor.processPayments( IERC20PaymentClientBase_v1(address(paymentClient)), executionData @@ -267,13 +250,17 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { keccak256(paymentProcessor.getBridgeData(0)) == keccak256(bytes("")), "Bridge data should be empty" ); + assertEq( + paymentProcessor.intentId(address(paymentClient), address(0)), + bytes32(0) + ); } /* Test invalid execution data └── When processing with invalid Connext parameters └── Then it should revert */ - function testFuzz_ProcessPayments_invalidExecutionData( + function testProcessPayments_revertsGivenInvalidExecutionData( address testRecipient, uint testAmount ) public { @@ -323,7 +310,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { │ └── When attempting to process payment │ └── Then it should revert with InvalidRecipient */ - function testFuzz_ProcessPayments_invalidRecipient(uint testAmount) + function testProcessPayments_revertsGivenInvalidRecipient(uint testAmount) public { vm.assume(testAmount > 0 && testAmount < MINTED_SUPPLY); // Keeping within our minted balance @@ -345,9 +332,9 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { │ └── When attempting to process payment │ └── Then it should revert with InvalidAmount */ - function testFuzz_ProcessPayments_invalidAmount(address testRecipient) - public - { + function testProcessPayments_revertsGivenInvalidAmount( + address testRecipient + ) public { vm.assume(testRecipient != address(0)); _setupSinglePayment(testRecipient, 0); @@ -369,7 +356,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { │ └── And intent ID should be stored correctly └── And intent status should be ADDED in Everclear spoke */ - function testFuzz_returnsCorrectBridgeData( + function testProcessPayments_worksGivenCorrectBridgeData( address testRecipient, uint testAmount ) public { @@ -395,10 +382,10 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { } /* Test empty bridge data - └── When checking bridge data with no processed payments + └── When checking bridge data with no added payments └── Then it should return empty bytes */ - function test_returnsEmptyBridgeData() public { + function testProcessPayments_worksGivenEmptyBridgeData() public { IERC20PaymentClientBase_v1 client = IERC20PaymentClientBase_v1(address(paymentClient)); // Process payments and verify _bridgeData mapping is updated @@ -410,11 +397,11 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { } /* Test insufficient balance - Given payment amount exceeds available balance - When attempting to process payment - Then it should revert with ERC20InsufficientBalance + └── Given payment amount exceeds available balance + └── When attempting to process payment + └── Then it should revert with ERC20InsufficientBalance */ - function testFuzz_ProcessPayments_InsufficientBalance( + function testProcessPayments_revertsGivenInsufficientBalance( address testRecipient, uint testAmount ) public { @@ -443,7 +430,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { └── And should emit PaymentProcessed event └── And should handle exact balance correctly */ - function testFuzz_ProcessPayments_EdgeCaseAmounts( + function testProcessPayments_worksGivenEdgeCaseAmounts( address testRecipient, uint96 testAmount ) public { From 3c98fa7648d8140d8ef850ced56a37ac05b68fdf Mon Sep 17 00:00:00 2001 From: Leandro Faria Date: Tue, 10 Dec 2024 23:29:33 -0500 Subject: [PATCH 59/93] feat: add cancel payments --- .../PP_Connext_Crosschain_v1.sol | 139 ++++++++++++++++-- .../PP_Connext_Crosschain_v1_Test.t.sol | 26 ++-- 2 files changed, 138 insertions(+), 27 deletions(-) diff --git a/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol b/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol index 6e50c4175..7be6b334c 100644 --- a/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol +++ b/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol @@ -43,6 +43,11 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { /// paymentClient => paymentReceiver => intentId. mapping(address => mapping(address => bytes32)) public intentId; + /// @dev Tracks failed transfers that can be retried + /// paymentClient => recipient => intentId => amount + mapping(address => mapping(address => mapping(bytes32 => uint))) public + failedTransfers; + /// @notice Initializes the payment processor module /// @param orchestrator_ The address of the orchestrator contract /// @param metadata Module metadata @@ -60,6 +65,18 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { weth = IWETH(weth_); } + /// @notice Retrieves the bridge data for a specific payment ID + /// @param paymentId The unique identifier of the payment + /// @return The bridge data associated with the payment (encoded intentId) + function getBridgeData(uint paymentId) + public + view + override + returns (bytes memory) + { + return _bridgeData[paymentId]; + } + /// @notice Execute the cross-chain bridge transfer /// @dev Override this function to implement specific bridge logic /// @param order The payment order containing all necessary transfer details @@ -68,14 +85,20 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { IERC20PaymentClientBase_v1.PaymentOrder memory order, bytes memory executionData ) internal override returns (bytes memory) { - //@notice call the connextBridgeLogic to execute the bridge transfer - bytes32 intentId = createCrossChainIntent(order, executionData); - if (intentId == bytes32(0)) { + bytes32 _intentId = createCrossChainIntent(order, executionData); + if (_intentId == bytes32(0)) { + // Track failed transfer + failedTransfers[msg.sender][order.recipient][_intentId] = + order.amount; + emit TransferFailed( + msg.sender, order.recipient, _intentId, order.amount + ); + revert Module__PP_Crosschain__MessageDeliveryFailed( 8453, 8453, executionData ); } - return abi.encode(intentId); + return abi.encode(_intentId); } /// @notice Processes multiple payment orders through the bridge @@ -170,15 +193,103 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { return intentId; } - /// @notice Retrieves the bridge data for a specific payment ID - /// @param paymentId The unique identifier of the payment - /// @return The bridge data associated with the payment (encoded intentId) - function getBridgeData(uint paymentId) - public - view - override - returns (bytes memory) - { - return _bridgeData[paymentId]; + /// @notice Allows retrying a failed cross-chain transfer + /// @param client The payment client address + /// @param recipient The recipient address + /// @param failedIntentId The intentId of the failed transfer + /// @param executionData New execution data containing maxFee and TTL + function retryFailedTransfer( + address client, + address recipient, + bytes32 failedIntentId, + bytes memory executionData + ) external { + uint amount = failedTransfers[client][recipient][failedIntentId]; + if (amount == 0) { + ///@audit - change this to something + revert Module__PP_Crosschain__MessageDeliveryFailed( + 8453, 8453, executionData + ); + } + + // Create new transfer attempt + bytes32 newIntentId = createCrossChainIntent( + IERC20PaymentClientBase_v1.PaymentOrder({ + recipient: recipient, + paymentToken: address(weth), // Note: You'll need to store the original token + amount: amount, + start: 0, // Immediate transfer + cliff: 0, + end: 0 + }), + executionData + ); + + // Clear the failed transfer record + delete failedTransfers[client][recipient][failedIntentId]; + + emit FailedTransferRetried( + client, recipient, failedIntentId, newIntentId + ); + } + + /// @notice Cancels a pending transfer and returns funds to the client + /// @param client The payment client address + /// @param recipient The recipient address + /// @param pendingIntentId The intentId to cancel + function cancelTransfer( + address client, + address recipient, + bytes32 pendingIntentId, + IERC20PaymentClientBase_v1.PaymentOrder memory order + ) external { + //@audit - change this to something + bytes memory randomBytes = new bytes(32); + //require the cancel is coming from the creator + if (msg.sender != order.recipient) { + revert Module__PP_Crosschain__MessageDeliveryFailed( + 8453, 8453, randomBytes + ); + } + + // Verify the transfer exists + if (intentId[client][recipient] != pendingIntentId) { + revert Module__PP_Crosschain__MessageDeliveryFailed( + 8453, 8453, randomBytes + ); + } + + delete intentId[client][recipient]; + + uint amount = order.amount; + + (bool success,) = order.recipient.call{value: amount}(""); + if (!success) { + revert Module__PP_Crosschain__MessageDeliveryFailed( + 8453, 8453, randomBytes + ); + } + + emit TransferCancelled(client, recipient, pendingIntentId); } + + event TransferFailed( + address indexed client, + address indexed recipient, + bytes32 indexed intentId, + uint amount + ); + + event FailedTransferRetried( + address indexed client, + address indexed recipient, + bytes32 indexed oldIntentId, + bytes32 newIntentId + ); + + event TransferCancelled( + address indexed client, + address indexed recipient, + bytes32 indexed intentId + ); } diff --git a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol index aa6cfa18d..bde116365 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol @@ -138,7 +138,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { └── Then it should emit PaymentProcessed events for payment └── And it should create cross-chain intent */ - function testProcessPayments_worksGivenSingleValidPaymentOrder( + function testPublicProcessPayments_succeedsGivenSingleValidPaymentOrder( address testRecipient, uint testAmount ) public { @@ -180,7 +180,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { ├── And it should create multiple cross-chain intents └── And it should contain valid intent IDs */ - function testProcessPayments_worksGivenMultipleValidPaymentOrders( + function testPublicProcessPayments_succeedsGivenMultipleValidPaymentOrders( uint8 numRecipients, address testRecipient, uint96 baseAmount @@ -241,7 +241,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { ├── Then it should complete successfully └── And the bridge data should remain empty */ - function testProcessPayments_worksGivenNoPaymentOrders() public { + function testPublicProcessPayments_succeedsGivenNoPaymentOrders() public { // Process payments and verify _bridgeData mapping is not updated paymentProcessor.processPayments( IERC20PaymentClientBase_v1(address(paymentClient)), executionData @@ -260,7 +260,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { └── When processing with invalid Connext parameters └── Then it should revert */ - function testProcessPayments_revertsGivenInvalidExecutionData( + function testPublicProcessPayments_revertsGivenInvalidExecutionData( address testRecipient, uint testAmount ) public { @@ -283,7 +283,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { │ └── When attempting to process payment │ └── Then it should revert with InvalidExecutionData */ - function testProcessPayments_revertsGivenEmptyExecutionData( + function testPublicProcessPayments_revertsGivenEmptyExecutionData( address testRecipient, uint testAmount ) public { @@ -310,9 +310,9 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { │ └── When attempting to process payment │ └── Then it should revert with InvalidRecipient */ - function testProcessPayments_revertsGivenInvalidRecipient(uint testAmount) - public - { + function testPublicProcessPayments_revertsGivenInvalidRecipient( + uint testAmount + ) public { vm.assume(testAmount > 0 && testAmount < MINTED_SUPPLY); // Keeping within our minted balance _setupSinglePayment(address(0), testAmount); @@ -332,7 +332,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { │ └── When attempting to process payment │ └── Then it should revert with InvalidAmount */ - function testProcessPayments_revertsGivenInvalidAmount( + function testPublicProcessPayments_revertsGivenInvalidAmount( address testRecipient ) public { vm.assume(testRecipient != address(0)); @@ -356,7 +356,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { │ └── And intent ID should be stored correctly └── And intent status should be ADDED in Everclear spoke */ - function testProcessPayments_worksGivenCorrectBridgeData( + function testPublicProcessPayments_worksGivenCorrectBridgeData( address testRecipient, uint testAmount ) public { @@ -385,7 +385,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { └── When checking bridge data with no added payments └── Then it should return empty bytes */ - function testProcessPayments_worksGivenEmptyBridgeData() public { + function testPublicProcessPayments_worksGivenEmptyBridgeData() public { IERC20PaymentClientBase_v1 client = IERC20PaymentClientBase_v1(address(paymentClient)); // Process payments and verify _bridgeData mapping is updated @@ -401,7 +401,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { └── When attempting to process payment └── Then it should revert with ERC20InsufficientBalance */ - function testProcessPayments_revertsGivenInsufficientBalance( + function testPublicProcessPayments_revertsGivenInsufficientBalance( address testRecipient, uint testAmount ) public { @@ -430,7 +430,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { └── And should emit PaymentProcessed event └── And should handle exact balance correctly */ - function testProcessPayments_worksGivenEdgeCaseAmounts( + function testPublicProcessPayments_worksGivenEdgeCaseAmounts( address testRecipient, uint96 testAmount ) public { From 855f6f2084e56882487d5d52d9d16da002e1bfe5 Mon Sep 17 00:00:00 2001 From: Leandro Faria Date: Thu, 12 Dec 2024 12:21:56 -0500 Subject: [PATCH 60/93] feat:add cancel payment & test --- .../PP_Connext_Crosschain_v1.sol | 174 ++++++++---------- .../interfaces/IEverclear.sol | 2 +- .../interfaces/IPP_Crosschain_v1.sol | 58 +++++- .../paymentProcessor/interfaces/IWETH.sol | 1 + .../PP_Connext_Crosschain_v1_Exposed.sol | 9 + .../PP_Connext_Crosschain_v1_Test.t.sol | 168 ++++++++++++++++- .../abstracts/mocks/Mock_EverclearPayment.sol | 18 +- 7 files changed, 317 insertions(+), 113 deletions(-) diff --git a/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol b/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol index 7be6b334c..0ef513c3e 100644 --- a/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol +++ b/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol @@ -1,5 +1,6 @@ pragma solidity ^0.8.20; +import {console} from "forge-std/console.sol"; import {CrossChainBase_v1} from "src/modules/paymentProcessor/abstracts/CrossChainBase_v1.sol"; import {ICrossChainBase_v1} from @@ -39,14 +40,22 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { IEverclearSpoke public everClearSpoke; IWETH public weth; - /// @dev Tracks all stream details for all payment orders of a paymentReceiver for a specific paymentClient. + error FailedTransfer(); + + /// @dev Tracks all details for all payment orders of a paymentReceiver for a specific paymentClient. /// paymentClient => paymentReceiver => intentId. - mapping(address => mapping(address => bytes32)) public intentId; + mapping( + address paymentClient => mapping(address recipient => bytes32 intentId) + ) public intentId; /// @dev Tracks failed transfers that can be retried /// paymentClient => recipient => intentId => amount - mapping(address => mapping(address => mapping(bytes32 => uint))) public - failedTransfers; + mapping( + address paymentClient + => mapping( + address recipient => mapping(bytes32 intentId => uint amount) + ) + ) public failedTransfers; /// @notice Initializes the payment processor module /// @param orchestrator_ The address of the orchestrator contract @@ -83,20 +92,20 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { /// @return bridgeData Arbitrary data returned by the bridge implementation function _executeBridgeTransfer( IERC20PaymentClientBase_v1.PaymentOrder memory order, - bytes memory executionData - ) internal override returns (bytes memory) { + bytes memory executionData, + address client + ) internal returns (bytes memory) { bytes32 _intentId = createCrossChainIntent(order, executionData); if (_intentId == bytes32(0)) { // Track failed transfer - failedTransfers[msg.sender][order.recipient][_intentId] = - order.amount; + failedTransfers[client][order.recipient][_intentId] = order.amount; emit TransferFailed( - msg.sender, order.recipient, _intentId, order.amount + client, order.recipient, _intentId, order.amount ); - revert Module__PP_Crosschain__MessageDeliveryFailed( - 8453, 8453, executionData - ); + // revert Module__PP_Crosschain__MessageDeliveryFailed( + // 8453, 8453, executionData + // ); } return abi.encode(_intentId); } @@ -113,8 +122,9 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { (orders,,) = client.collectPaymentOrders(); for (uint i = 0; i < orders.length; i++) { - bytes memory bridgeData = - _executeBridgeTransfer(orders[i], executionData); + bytes memory bridgeData = _executeBridgeTransfer( + orders[i], executionData, address(client) + ); _bridgeData[i] = bridgeData; emit PaymentOrderProcessed( @@ -146,7 +156,6 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { ICrossChainBase_v1 .Module__CrossChainBase_InvalidExecutionData(); } - if (order.amount == 0) { revert ICrossChainBase_v1.Module__CrossChainBase__InvalidAmount(); } @@ -155,7 +164,6 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { } // Decode the execution data (uint maxFee, uint ttl) = abi.decode(executionData, (uint, uint)); - if (ttl == 0) { revert IPP_Connext_Crosschain_v1 @@ -179,7 +187,7 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { // new struct is created for us // Call newIntent on the EverClearSpoke contract - (intentId,) = everClearSpoke.newIntent( + (bytes32 intentId) = everClearSpoke.newIntent( destinations, order.recipient, order.paymentToken, @@ -189,107 +197,87 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { uint48(ttl), "" ); - return intentId; } - - /// @notice Allows retrying a failed cross-chain transfer + /// @notice Cancels a pending transfer and returns funds to the client /// @param client The payment client address /// @param recipient The recipient address - /// @param failedIntentId The intentId of the failed transfer - /// @param executionData New execution data containing maxFee and TTL - function retryFailedTransfer( + /// @param pendingIntentId The intentId to cancel + + function cancelTransfer( address client, address recipient, - bytes32 failedIntentId, - bytes memory executionData + bytes32 pendingIntentId, + IERC20PaymentClientBase_v1.PaymentOrder memory order ) external { - uint amount = failedTransfers[client][recipient][failedIntentId]; - if (amount == 0) { - ///@audit - change this to something - revert Module__PP_Crosschain__MessageDeliveryFailed( - 8453, 8453, executionData - ); + // Verify the caller is the intended recipient of the transfer + if (msg.sender != client) { + revert Module__InvalidAddress(); } - // Create new transfer attempt - bytes32 newIntentId = createCrossChainIntent( - IERC20PaymentClientBase_v1.PaymentOrder({ - recipient: recipient, - paymentToken: address(weth), // Note: You'll need to store the original token - amount: amount, - start: 0, // Immediate transfer - cliff: 0, - end: 0 - }), - executionData - ); + // Verify the transfer exists and matches the provided intentId + if (intentId[client][recipient] != pendingIntentId) { + revert Module__PP_Crosschain__InvalidIntentId(); + } - // Clear the failed transfer record - delete failedTransfers[client][recipient][failedIntentId]; + // Verify the order details by checking if there's a failed transfer record + uint failedAmount = failedTransfers[client][recipient][pendingIntentId]; - emit FailedTransferRetried( - client, recipient, failedIntentId, newIntentId - ); + if (failedAmount == 0) { + //@audit - change this to something + revert Module__CrossChainBase__InvalidAmount(); + } + + // Clean up storage + delete intentId[client][recipient]; + delete failedTransfers[client][recipient][pendingIntentId]; + + // // Update client's payment records + // IERC20PaymentClientBase_v1(client).amountPaid( + // order.paymentToken, order.amount + // ); + + // Transfer tokens to the recipient since they are cancelling their own failed transfer + if (!IERC20(order.paymentToken).transfer(recipient, order.amount)) { + revert FailedTransfer(); + } + + emit TransferCancelled(client, recipient, pendingIntentId, order.amount); } - /// @notice Cancels a pending transfer and returns funds to the client - /// @param client The payment client address - /// @param recipient The recipient address - /// @param pendingIntentId The intentId to cancel - function cancelTransfer( + function retryFailedTransfer( address client, address recipient, bytes32 pendingIntentId, - IERC20PaymentClientBase_v1.PaymentOrder memory order + IERC20PaymentClientBase_v1.PaymentOrder memory order, + bytes memory executionData ) external { - //@audit - change this to something - bytes memory randomBytes = new bytes(32); - //require the cancel is coming from the creator - if (msg.sender != order.recipient) { - revert Module__PP_Crosschain__MessageDeliveryFailed( - 8453, 8453, randomBytes - ); + //verify the caller is the intended recipient of the transfer + if (msg.sender != client) { + revert Module__InvalidAddress(); } - // Verify the transfer exists + //verify the transfer exists and matches the provided intentId if (intentId[client][recipient] != pendingIntentId) { - revert Module__PP_Crosschain__MessageDeliveryFailed( - 8453, 8453, randomBytes - ); + revert Module__PP_Crosschain__InvalidIntentId(); } - delete intentId[client][recipient]; - - uint amount = order.amount; - - (bool success,) = order.recipient.call{value: amount}(""); - if (!success) { + //verify the order details by checking if there's a failed transfer record + uint failedAmount = failedTransfers[client][recipient][pendingIntentId]; + if (failedAmount == 0) { + revert Module__CrossChainBase__InvalidAmount(); + } + //create a new intent with the updated execution data + bytes32 newIntentId = createCrossChainIntent(order, executionData); + if (newIntentId == bytes32(0)) { revert Module__PP_Crosschain__MessageDeliveryFailed( - 8453, 8453, randomBytes + 8453, 8453, executionData ); } + // delete the failed transfer record + delete failedTransfers[client][recipient][pendingIntentId]; - emit TransferCancelled(client, recipient, pendingIntentId); + // update the intentId with the new intentId + intentId[client][recipient] = newIntentId; } - - event TransferFailed( - address indexed client, - address indexed recipient, - bytes32 indexed intentId, - uint amount - ); - - event FailedTransferRetried( - address indexed client, - address indexed recipient, - bytes32 indexed oldIntentId, - bytes32 newIntentId - ); - - event TransferCancelled( - address indexed client, - address indexed recipient, - bytes32 indexed intentId - ); } diff --git a/src/modules/paymentProcessor/interfaces/IEverclear.sol b/src/modules/paymentProcessor/interfaces/IEverclear.sol index 293afc3ec..2123210c3 100644 --- a/src/modules/paymentProcessor/interfaces/IEverclear.sol +++ b/src/modules/paymentProcessor/interfaces/IEverclear.sol @@ -10,5 +10,5 @@ interface IEverclearSpoke { uint24 maxFee, uint48 ttl, bytes memory data - ) external returns (bytes32 intentId, uint amountOut); + ) external returns (bytes32 intentId); } diff --git a/src/modules/paymentProcessor/interfaces/IPP_Crosschain_v1.sol b/src/modules/paymentProcessor/interfaces/IPP_Crosschain_v1.sol index 0efaba763..3d389cd4c 100644 --- a/src/modules/paymentProcessor/interfaces/IPP_Crosschain_v1.sol +++ b/src/modules/paymentProcessor/interfaces/IPP_Crosschain_v1.sol @@ -7,25 +7,73 @@ import {IPaymentProcessor_v1} from /// @notice Interface for cross-chain payment processing functionality interface IPP_Crosschain_v1 is IPaymentProcessor_v1 { + // Events //-------------------------------------------------------------------------- + + /// @notice Emitted when a cross-chain transfer fails to complete + /// @param client The address initiating the transfer + /// @param recipient The intended recipient of the transfer + /// @param intentId The unique identifier for this transfer attempt + /// @param amount The amount that failed to transfer + event TransferFailed( + address indexed client, + address indexed recipient, + bytes32 indexed intentId, + uint amount + ); + + /// @notice Emitted when a previously failed transfer is attempted again + /// @param client The address initiating the retry + /// @param recipient The intended recipient of the transfer + /// @param oldIntentId The intent ID of the failed transfer + /// @param newIntentId The new intent ID for this retry attempt + event FailedTransferRetried( + address indexed client, + address indexed recipient, + bytes32 indexed oldIntentId, + bytes32 newIntentId + ); + + /// @notice Emitted when a transfer is cancelled by the client or system + /// @param client The address that initiated the original transfer + /// @param recipient The intended recipient of the cancelled transfer + /// @param intentId The unique identifier of the cancelled transfer + /// @param amount The amount that was intended to be transferred + event TransferCancelled( + address indexed client, + address indexed recipient, + bytes32 indexed intentId, + uint amount + ); + // Errors + //-------------------------------------------------------------------------- + + /// @notice Thrown when the provided intent ID is invalid or does not exist + error Module__PP_Crosschain__InvalidIntentId(); + + /// @notice Thrown when the payment amount is invalid (e.g., zero or exceeds + /// limits) + error Module__PP_Crosschain__InvalidAmount(); + + /// @notice Thrown when the cross-chain bridge fees exceed the maximum allowed + error Module__PP_Crosschain__InvalidBridgeFee(); /// @notice Thrown when the cross-chain message fails to be delivered /// @param sourceChain The chain ID where the message originated - /// @param destinationChain The chain ID where the message was meant to be delivered + /// @param destinationChain The chain ID where the message was meant to be + /// delivered /// @param executionData The encoded execution parameters (maxFee, ttl) error Module__PP_Crosschain__MessageDeliveryFailed( uint sourceChain, uint destinationChain, bytes executionData ); - /// @notice Thrown when attempting to process a cross-chain payment with invalid parameters + /// @notice Thrown when attempting to process a cross-chain payment with invalid + /// parameters /// @param paymentClient The address of the payment client /// @param paymentId The ID of the payment being processed /// @param destinationChain The target chain ID for the payment error Module__PP_Crosschain__InvalidPaymentParameters( address paymentClient, uint paymentId, uint destinationChain ); - - /// @notice Thrown when the cross-chain bridge fees exceed the maximum allowed - error Module__PP_Crosschain__InvalidBridgeFee(); } diff --git a/src/modules/paymentProcessor/interfaces/IWETH.sol b/src/modules/paymentProcessor/interfaces/IWETH.sol index 79b2b6256..87b293284 100644 --- a/src/modules/paymentProcessor/interfaces/IWETH.sol +++ b/src/modules/paymentProcessor/interfaces/IWETH.sol @@ -3,4 +3,5 @@ pragma solidity ^0.8.0; interface IWETH { function deposit() external payable; function withdraw(uint amount) external; + function approve(address spender, uint amount) external returns (bool); } diff --git a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Exposed.sol b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Exposed.sol index 5c71414fe..b0ac06853 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Exposed.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Exposed.sol @@ -22,4 +22,13 @@ contract PP_Connext_Crosschain_v1_Exposed is PP_Connext_Crosschain_v1 { ) external returns (bytes32) { return createCrossChainIntent(order, executionData); } + + function exposed_setFailedTransfer( + address client, + address recipient, + bytes32 intentId, + uint amount + ) external { + failedTransfers[client][recipient][intentId] = amount; + } } diff --git a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol index bde116365..d5ccc9312 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol @@ -38,11 +38,17 @@ import {OZErrors} from "test/utils/errors/OZErrors.sol"; import {IERC20Errors} from "@oz/interfaces/draft-IERC6093.sol"; import {IWETH} from "src/modules/paymentProcessor/interfaces/IWETH.sol"; import "forge-std/console2.sol"; +import {IPP_Crosschain_v1} from + "src/modules/paymentProcessor/interfaces/IPP_Crosschain_v1.sol"; + +import {IModule_v1, IOrchestrator_v1} from "src/modules/base/IModule_v1.sol"; contract PP_Connext_Crosschain_v1_Test is ModuleTest { PP_Connext_Crosschain_v1_Exposed public paymentProcessor; Mock_EverclearPayment public everclearPaymentMock; ERC20PaymentClientBaseV1Mock paymentClient; + IPP_Crosschain_v1 public crossChainBase; + IWETH public weth; uint public chainId; @@ -382,7 +388,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { } /* Test empty bridge data - └── When checking bridge data with no added payments + ── When checking bridge data with no added payments └── Then it should return empty bytes */ function testPublicProcessPayments_worksGivenEmptyBridgeData() public { @@ -468,9 +474,156 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { ); } + /* Test retry failed transfer + └── Given a failed transfer + └── When retrying with valid execution data + └── Then it should create a new intent + └── And clear the failed transfer record + └── And emit FailedTransferRetried event + */ + function testRetryFailedTransfer_succeeds(address recipient) public { + // Setup + address recipient; + uint amount = 1 ether; + + // Setup initial payment + _setupSinglePayment(recipient, amount); + + // Process payment first time (this will create an intent) + paymentProcessor.processPayments( + IERC20PaymentClientBase_v1(address(paymentClient)), executionData + ); + + // Get the intent ID that was created + bytes32 originalIntentId = + paymentProcessor.intentId(address(paymentClient), recipient); + + // Simulate a failed transfer by setting it in the failed transfers mapping + paymentProcessor.exposed_setFailedTransfer( + address(paymentClient), recipient, originalIntentId, amount + ); + + // // Now retry the failed transfer + // paymentProcessor.retryFailedTransfer( + // address(paymentClient), recipient, originalIntentId, executionData + // ); + + // Verify: + // 1. The failed transfer was cleared + assertEq( + paymentProcessor.failedTransfers( + address(paymentClient), recipient, originalIntentId + ), + 0 + ); + + // 2. A new intent was created (should be different from original) + bytes32 newIntentId = + paymentProcessor.intentId(address(paymentClient), recipient); + assertTrue(newIntentId != originalIntentId); + assertTrue(newIntentId != bytes32(0)); + } + + /* Test cancel transfer + └── Given a pending transfer + └── When cancelled by the recipient + └── Then it should clear the intent + └── And return funds to recipient + └── And emit TransferCancelled event + */ + function testCancelTransfer_succeeds() public { + // Setup + address recipient = address(0xBEEF); + + uint amount = 1 ether; + + // Setup the payment and process it + IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = + _setupSinglePayment(recipient, amount); + paymentClient.addPaymentOrder(orders[0]); + //call processPayments with maxFee = 333 + paymentProcessor.processPayments( + IERC20PaymentClientBase_v1(address(paymentClient)), + abi.encode(333, 1) + ); + console2.log("Fucker who seneeed", address(this)); + // see if failed failedTransfers updates + assertEq( + paymentProcessor.failedTransfers( + address(paymentClient), recipient, bytes32(0) + ), + orders[0].amount + ); + + uint failedIntentId = paymentProcessor.failedTransfers( + address(paymentClient), recipient, bytes32(0) + ); + console2.log("AMOUNT", failedIntentId); + assertEq(failedIntentId, orders[0].amount); + + // Cancel as recipient + vm.prank(address(paymentClient)); + paymentProcessor.cancelTransfer( + address(paymentClient), recipient, bytes32(0), orders[0] + ); + + // Verify intentId was cleared + assertEq( + paymentProcessor.intentId(address(paymentClient), recipient), + bytes32(0) + ); + } + + /* Test cancel transfer by non-recipient + └── Given a pending transfer + └── When cancelled by someone other than recipient + └── Then it should revert with InvalidAddress + */ + function testCancelTransfer_revertsForNonRecipient( + address testRecipient, + address nonRecipient, + uint testAmount + ) public { + vm.assume(testRecipient != address(0)); + vm.assume(nonRecipient != testRecipient); + vm.assume(testAmount > 0 && testAmount < MINTED_SUPPLY); + + _setupSinglePayment(testRecipient, testAmount); + + // Process payment to create intent + paymentProcessor.processPayments( + IERC20PaymentClientBase_v1(address(paymentClient)), executionData + ); + + bytes32 pendingIntentId = + paymentProcessor.intentId(address(paymentClient), testRecipient); + + // Create payment order for cancellation + IERC20PaymentClientBase_v1.PaymentOrder memory order = + IERC20PaymentClientBase_v1.PaymentOrder({ + recipient: testRecipient, + paymentToken: address(_token), + amount: testAmount, + start: block.timestamp, + cliff: 0, + end: block.timestamp + 1 days + }); + + // Prank as non-recipient + vm.prank(nonRecipient); + + vm.expectRevert(IModule_v1.Module__InvalidAddress.selector); + paymentProcessor.cancelTransfer( + address(paymentClient), testRecipient, pendingIntentId, order + ); + } + // Helper functions - function _setupSinglePayment(address _recipient, uint _amount) internal { + function _setupSinglePayment(address _recipient, uint _amount) + internal + returns (IERC20PaymentClientBase_v1.PaymentOrder[] memory) + { address[] memory setupRecipients = new address[](1); setupRecipients[0] = _recipient; uint[] memory setupAmounts = new uint[](1); @@ -478,18 +631,14 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = _createPaymentOrders(1, setupRecipients, setupAmounts); - paymentClient.addPaymentOrders(orders); + return orders; } function _createPaymentOrders( uint orderCount, address[] memory recipients, uint[] memory amounts - ) - internal - view - returns (IERC20PaymentClientBase_v1.PaymentOrder[] memory) - { + ) internal returns (IERC20PaymentClientBase_v1.PaymentOrder[] memory) { // Sanity checks for array lengths require( recipients.length == orderCount && amounts.length == orderCount, @@ -497,6 +646,8 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { ); IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = new IERC20PaymentClientBase_v1.PaymentOrder[](orderCount); + //add payment order to client + for (uint i = 0; i < orderCount; i++) { orders[i] = IERC20PaymentClientBase_v1.PaymentOrder({ recipient: recipients[i], @@ -506,6 +657,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { cliff: 0, end: block.timestamp + 1 days }); + paymentClient.addPaymentOrder(orders[i]); } return orders; } diff --git a/test/modules/paymentProcessor/bridging/abstracts/mocks/Mock_EverclearPayment.sol b/test/modules/paymentProcessor/bridging/abstracts/mocks/Mock_EverclearPayment.sol index fbf4aa0ff..6662f594c 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/mocks/Mock_EverclearPayment.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/mocks/Mock_EverclearPayment.sol @@ -1,6 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.23; +import {console} from "forge-std/console.sol"; + contract Mock_EverclearPayment { event IntentAdded(bytes32 intentId, uint queuePosition, Intent intent); @@ -39,11 +41,15 @@ contract Mock_EverclearPayment { uint24 _maxFee, uint48 _ttl, bytes calldata _data - ) external returns (bytes32 _intentId, Intent memory _intent) { + ) external returns (bytes32 _intentId) { // Increment nonce for each new intent nonce++; - - _intent = Intent({ + console.log("maxFee", _maxFee); + if (_maxFee == 333) { + return bytes32(0); + } + //if data is the word "fail" intentional return bytes32(0) + Intent memory _intent = Intent({ initiator: msg.sender, receiver: _to, inputAsset: _inputAsset, @@ -61,10 +67,10 @@ contract Mock_EverclearPayment { // Generate a unique intent ID _intentId = keccak256(abi.encode(_intent)); - // Set intent status to ADDED and emit the event + // // Set intent status to ADDED and emit the event status[_intentId] = IntentStatus.ADDED; emit IntentAdded(_intentId, nonce, _intent); - - return (_intentId, _intent); + console.log("WHYYYYY"); + return (_intentId); } } From d64d9771315f2f59cae22d8ff4ca09b2cd843dc4 Mon Sep 17 00:00:00 2001 From: Leandro Faria Date: Thu, 12 Dec 2024 12:30:32 -0500 Subject: [PATCH 61/93] feat:add retry payments & test --- .../PP_Connext_Crosschain_v1_Test.t.sol | 45 ++++++++++--------- .../abstracts/mocks/Mock_EverclearPayment.sol | 3 +- 2 files changed, 26 insertions(+), 22 deletions(-) diff --git a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol index d5ccc9312..b38f8773a 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol @@ -481,46 +481,51 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { └── And clear the failed transfer record └── And emit FailedTransferRetried event */ - function testRetryFailedTransfer_succeeds(address recipient) public { + function testRetryFailedTransfer_succeeds() public { // Setup - address recipient; + address recipient = address(0xBEEF); uint amount = 1 ether; // Setup initial payment - _setupSinglePayment(recipient, amount); + IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = + _setupSinglePayment(recipient, amount); - // Process payment first time (this will create an intent) + // First attempt with high maxFee to force failure paymentProcessor.processPayments( - IERC20PaymentClientBase_v1(address(paymentClient)), executionData + IERC20PaymentClientBase_v1(address(paymentClient)), + abi.encode(333, 1) // maxFee of 333 will cause failure ); - // Get the intent ID that was created - bytes32 originalIntentId = - paymentProcessor.intentId(address(paymentClient), recipient); - - // Simulate a failed transfer by setting it in the failed transfers mapping - paymentProcessor.exposed_setFailedTransfer( - address(paymentClient), recipient, originalIntentId, amount + // Verify failed transfer was recorded + assertEq( + paymentProcessor.failedTransfers( + address(paymentClient), recipient, bytes32(0) + ), + orders[0].amount ); - // // Now retry the failed transfer - // paymentProcessor.retryFailedTransfer( - // address(paymentClient), recipient, originalIntentId, executionData - // ); + // Now retry with proper execution data + vm.prank(address(paymentClient)); + paymentProcessor.retryFailedTransfer( + address(paymentClient), + recipient, + bytes32(0), + orders[0], + abi.encode(0, 1) // proper maxFee and ttl + ); // Verify: - // 1. The failed transfer was cleared + // 1. Failed transfer record was cleared assertEq( paymentProcessor.failedTransfers( - address(paymentClient), recipient, originalIntentId + address(paymentClient), recipient, bytes32(0) ), 0 ); - // 2. A new intent was created (should be different from original) + // 2. New intent was created (should be non-zero) bytes32 newIntentId = paymentProcessor.intentId(address(paymentClient), recipient); - assertTrue(newIntentId != originalIntentId); assertTrue(newIntentId != bytes32(0)); } diff --git a/test/modules/paymentProcessor/bridging/abstracts/mocks/Mock_EverclearPayment.sol b/test/modules/paymentProcessor/bridging/abstracts/mocks/Mock_EverclearPayment.sol index 6662f594c..1d83eca7e 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/mocks/Mock_EverclearPayment.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/mocks/Mock_EverclearPayment.sol @@ -44,7 +44,6 @@ contract Mock_EverclearPayment { ) external returns (bytes32 _intentId) { // Increment nonce for each new intent nonce++; - console.log("maxFee", _maxFee); if (_maxFee == 333) { return bytes32(0); } @@ -70,7 +69,7 @@ contract Mock_EverclearPayment { // // Set intent status to ADDED and emit the event status[_intentId] = IntentStatus.ADDED; emit IntentAdded(_intentId, nonce, _intent); - console.log("WHYYYYY"); + return (_intentId); } } From cedbb592fe14075965c18b276490ec940d8281a9 Mon Sep 17 00:00:00 2001 From: Leandro Faria Date: Thu, 12 Dec 2024 13:02:22 -0500 Subject: [PATCH 62/93] fix:retry payments test --- .../PP_Connext_Crosschain_v1.sol | 239 +++++++++--------- .../PP_Connext_Crosschain_v1_Test.t.sol | 9 +- 2 files changed, 125 insertions(+), 123 deletions(-) diff --git a/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol b/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol index 0ef513c3e..2faa74196 100644 --- a/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol +++ b/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol @@ -1,6 +1,11 @@ +// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; -import {console} from "forge-std/console.sol"; +// External Imports +import {IERC20} from "@oz/token/ERC20/IERC20.sol"; +import {Module_v1} from "src/modules/base/Module_v1.sol"; + +// Internal Imports import {CrossChainBase_v1} from "src/modules/paymentProcessor/abstracts/CrossChainBase_v1.sol"; import {ICrossChainBase_v1} from @@ -9,7 +14,6 @@ import {IPP_Connext_Crosschain_v1} from "src/modules/paymentProcessor/interfaces/IPP_Connext_Crosschain_v1.sol"; import {IERC20PaymentClientBase_v1} from "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; -import {IERC20} from "@oz/token/ERC20/IERC20.sol"; import {PP_Crosschain_v1} from "src/modules/paymentProcessor/abstracts/PP_Crosschain_v1.sol"; import {IWETH} from "src/modules/paymentProcessor/interfaces/IWETH.sol"; @@ -17,7 +21,6 @@ import {IEverclearSpoke} from "src/modules/paymentProcessor/interfaces/IEverclear.sol"; import {IOrchestrator_v1} from "src/orchestrator/interfaces/IOrchestrator_v1.sol"; -import {Module_v1} from "src/modules/base/Module_v1.sol"; /** * @title Connext Cross-chain Payment Processor @@ -35,21 +38,24 @@ import {Module_v1} from "src/modules/base/Module_v1.sol"; * @custom:security-contact security@inverter.network * In case of any concerns or findings, please refer to our Security Policy * at security.inverter.network + * + * @custom:version 1.0.0 + * @custom:standard-version 1.0.0 + * @author Inverter Network */ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { + // Storage Variables IEverclearSpoke public everClearSpoke; IWETH public weth; - error FailedTransfer(); - - /// @dev Tracks all details for all payment orders of a paymentReceiver for a specific paymentClient. - /// paymentClient => paymentReceiver => intentId. + /// @dev Tracks all details for all payment orders of a paymentReceiver for a specific paymentClient. + /// paymentClient => paymentReceiver => intentId. mapping( address paymentClient => mapping(address recipient => bytes32 intentId) ) public intentId; /// @dev Tracks failed transfers that can be retried - /// paymentClient => recipient => intentId => amount + /// paymentClient => recipient => intentId => amount mapping( address paymentClient => mapping( @@ -57,6 +63,10 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { ) ) public failedTransfers; + // Errors + error FailedTransfer(); + + // External Functions /// @notice Initializes the payment processor module /// @param orchestrator_ The address of the orchestrator contract /// @param metadata Module metadata @@ -74,42 +84,6 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { weth = IWETH(weth_); } - /// @notice Retrieves the bridge data for a specific payment ID - /// @param paymentId The unique identifier of the payment - /// @return The bridge data associated with the payment (encoded intentId) - function getBridgeData(uint paymentId) - public - view - override - returns (bytes memory) - { - return _bridgeData[paymentId]; - } - - /// @notice Execute the cross-chain bridge transfer - /// @dev Override this function to implement specific bridge logic - /// @param order The payment order containing all necessary transfer details - /// @return bridgeData Arbitrary data returned by the bridge implementation - function _executeBridgeTransfer( - IERC20PaymentClientBase_v1.PaymentOrder memory order, - bytes memory executionData, - address client - ) internal returns (bytes memory) { - bytes32 _intentId = createCrossChainIntent(order, executionData); - if (_intentId == bytes32(0)) { - // Track failed transfer - failedTransfers[client][order.recipient][_intentId] = order.amount; - emit TransferFailed( - client, order.recipient, _intentId, order.amount - ); - - // revert Module__PP_Crosschain__MessageDeliveryFailed( - // 8453, 8453, executionData - // ); - } - return abi.encode(_intentId); - } - /// @notice Processes multiple payment orders through the bridge /// @param client The payment client contract interface /// @param executionData Additional data needed for execution (encoded maxFee and TTL) @@ -143,26 +117,95 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { } } - /// @notice Executes a cross-chain transfer through the Connext bridge - /// @param order The payment order to be processed - /// @param executionData Encoded data containing maxFee and TTL for the transfer - /// @return intentId The unique identifier for the cross-chain transfer + /// @notice Cancels a pending transfer and returns funds to the client + /// @param client The payment client address + /// @param recipient The recipient address + /// @param pendingIntentId The intentId to cancel + function cancelTransfer( + address client, + address recipient, + bytes32 pendingIntentId, + IERC20PaymentClientBase_v1.PaymentOrder memory order + ) external { + _validateTransferRequest(client, recipient, pendingIntentId); + _cleanupFailedTransfer(client, recipient, pendingIntentId); + //Set approval to 0 + IERC20(order.paymentToken).approve(address(everClearSpoke), 0); + // Just do the transfer inline + if (!IERC20(order.paymentToken).transfer(recipient, order.amount)) { + revert FailedTransfer(); + } + + emit TransferCancelled(client, recipient, pendingIntentId, order.amount); + } + + function retryFailedTransfer( + address client, + address recipient, + bytes32 pendingIntentId, + IERC20PaymentClientBase_v1.PaymentOrder memory order, + bytes memory executionData + ) external { + _validateTransferRequest(client, recipient, pendingIntentId); + + bytes32 newIntentId = createCrossChainIntent(order, executionData); + if (newIntentId == bytes32(0)) { + revert Module__PP_Crosschain__MessageDeliveryFailed( + 8453, 8453, executionData + ); + } + + _cleanupFailedTransfer(client, recipient, pendingIntentId); + intentId[client][recipient] = newIntentId; + } + + // Public Functions + /// @notice Retrieves the bridge data for a specific payment ID + /// @param paymentId The unique identifier of the payment + /// @return The bridge data associated with the payment (encoded intentId) + function getBridgeData(uint paymentId) + public + view + override + returns (bytes memory) + { + return _bridgeData[paymentId]; + } + + // Internal Functions + /// @notice Execute the cross-chain bridge transfer + /// @dev Override this function to implement specific bridge logic + /// @param order The payment order containing all necessary transfer details + /// @return bridgeData Arbitrary data returned by the bridge implementation + function _executeBridgeTransfer( + IERC20PaymentClientBase_v1.PaymentOrder memory order, + bytes memory executionData, + address client + ) internal returns (bytes memory) { + bytes32 _intentId = createCrossChainIntent(order, executionData); + + if (_intentId == bytes32(0)) { + failedTransfers[client][order.recipient][_intentId] = order.amount; + intentId[client][order.recipient] = _intentId; + emit TransferFailed( + client, order.recipient, _intentId, order.amount + ); + } + + return abi.encode(_intentId); + } + function createCrossChainIntent( IERC20PaymentClientBase_v1.PaymentOrder memory order, bytes memory executionData ) internal returns (bytes32) { + _validateOrder(order); + if (executionData.length == 0) { revert ICrossChainBase_v1 .Module__CrossChainBase_InvalidExecutionData(); } - if (order.amount == 0) { - revert ICrossChainBase_v1.Module__CrossChainBase__InvalidAmount(); - } - if (order.recipient == address(0)) { - revert ICrossChainBase_v1.Module__CrossChainBase__InvalidRecipient(); - } - // Decode the execution data (uint maxFee, uint ttl) = abi.decode(executionData, (uint, uint)); if (ttl == 0) { revert @@ -170,24 +213,17 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { .Module__PP_Connext_Crosschain__InvalidTTL(); } - // Wrap ETH into WETH to send with the xcall IERC20(order.paymentToken).transferFrom( msg.sender, address(this), order.amount ); - - // This contract approves transfer to EverClearSpoke IERC20(order.paymentToken).approve( address(everClearSpoke), order.amount ); - // Create destinations array with the target chain uint32[] memory destinations = new uint32[](1); destinations[0] = 8453; - // @note -> hardcode for now -> order.destinationChainId when the - // new struct is created for us - // Call newIntent on the EverClearSpoke contract - (bytes32 intentId) = everClearSpoke.newIntent( + return everClearSpoke.newIntent( destinations, order.recipient, order.paymentToken, @@ -197,87 +233,48 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { uint48(ttl), "" ); - return intentId; } - /// @notice Cancels a pending transfer and returns funds to the client - /// @param client The payment client address - /// @param recipient The recipient address - /// @param pendingIntentId The intentId to cancel - function cancelTransfer( + /// @dev Common validation for transfer-related operations + function _validateTransferRequest( address client, address recipient, - bytes32 pendingIntentId, - IERC20PaymentClientBase_v1.PaymentOrder memory order - ) external { - // Verify the caller is the intended recipient of the transfer + bytes32 pendingIntentId + ) internal view returns (uint) { if (msg.sender != client) { revert Module__InvalidAddress(); } - // Verify the transfer exists and matches the provided intentId if (intentId[client][recipient] != pendingIntentId) { revert Module__PP_Crosschain__InvalidIntentId(); } - // Verify the order details by checking if there's a failed transfer record uint failedAmount = failedTransfers[client][recipient][pendingIntentId]; - if (failedAmount == 0) { - //@audit - change this to something revert Module__CrossChainBase__InvalidAmount(); } - // Clean up storage - delete intentId[client][recipient]; - delete failedTransfers[client][recipient][pendingIntentId]; - - // // Update client's payment records - // IERC20PaymentClientBase_v1(client).amountPaid( - // order.paymentToken, order.amount - // ); - - // Transfer tokens to the recipient since they are cancelling their own failed transfer - if (!IERC20(order.paymentToken).transfer(recipient, order.amount)) { - revert FailedTransfer(); - } - - emit TransferCancelled(client, recipient, pendingIntentId, order.amount); + return failedAmount; } - function retryFailedTransfer( + /// @dev Helper function to clean up storage after handling a failed transfer + function _cleanupFailedTransfer( address client, address recipient, - bytes32 pendingIntentId, - IERC20PaymentClientBase_v1.PaymentOrder memory order, - bytes memory executionData - ) external { - //verify the caller is the intended recipient of the transfer - if (msg.sender != client) { - revert Module__InvalidAddress(); - } - - //verify the transfer exists and matches the provided intentId - if (intentId[client][recipient] != pendingIntentId) { - revert Module__PP_Crosschain__InvalidIntentId(); - } + bytes32 pendingIntentId + ) internal { + delete intentId[client][recipient]; + delete failedTransfers[client][recipient][pendingIntentId]; + } - //verify the order details by checking if there's a failed transfer record - uint failedAmount = failedTransfers[client][recipient][pendingIntentId]; - if (failedAmount == 0) { - revert Module__CrossChainBase__InvalidAmount(); + function _validateOrder( + IERC20PaymentClientBase_v1.PaymentOrder memory order + ) internal pure { + if (order.amount == 0) { + revert ICrossChainBase_v1.Module__CrossChainBase__InvalidAmount(); } - //create a new intent with the updated execution data - bytes32 newIntentId = createCrossChainIntent(order, executionData); - if (newIntentId == bytes32(0)) { - revert Module__PP_Crosschain__MessageDeliveryFailed( - 8453, 8453, executionData - ); + if (order.recipient == address(0)) { + revert ICrossChainBase_v1.Module__CrossChainBase__InvalidRecipient(); } - // delete the failed transfer record - delete failedTransfers[client][recipient][pendingIntentId]; - - // update the intentId with the new intentId - intentId[client][recipient] = newIntentId; } } diff --git a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol index b38f8773a..b33bb2d45 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol +++ b/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol @@ -192,9 +192,13 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { uint96 baseAmount ) public { // Assumptions to keep the test manageable and within bounds - vm.assume(numRecipients > 0 && numRecipients <= type(uint8).max); // Limit array size + vm.assume(numRecipients > 0 && numRecipients <= 10); vm.assume(testRecipient != address(0)); - vm.assume(baseAmount > 0 && baseAmount <= MINTED_SUPPLY / numRecipients); // Ensure total amount won't exceed MINTED_SUPPLY + + // Just make sure baseAmount * numRecipients doesn't exceed MINTED_SUPPLY + vm.assume( + baseAmount > 0 && baseAmount <= MINTED_SUPPLY / (numRecipients * 2) + ); // Setup mock payment orders address[] memory setupRecipients = new address[](numRecipients); @@ -202,6 +206,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { for (uint i = 0; i < numRecipients; i++) { setupRecipients[i] = testRecipient; + // Simple amount calculation without any Math.min nonsense setupAmounts[i] = 1 + (uint(keccak256(abi.encode(i, baseAmount))) % baseAmount); } From f1e0e69f2a9386e7295d03156ffdc085578a2599 Mon Sep 17 00:00:00 2001 From: Leandro Faria Date: Thu, 12 Dec 2024 13:12:42 -0500 Subject: [PATCH 63/93] fix:format --- .../PP_Connext_Crosschain_v1.sol | 98 ++++++++++++------- 1 file changed, 63 insertions(+), 35 deletions(-) diff --git a/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol b/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol index 2faa74196..abe123a28 100644 --- a/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol +++ b/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol @@ -24,9 +24,7 @@ import {IOrchestrator_v1} from /** * @title Connext Cross-chain Payment Processor - * - * @notice Payment processor implementation for cross-chain payments using the Connext protocol. - * + * @notice Payment processor implementation for cross-chain payments using the Connext protocol * @dev This contract implements cross-chain payment processing via Connext and provides: * - Integration with Connext's EverClear protocol for cross-chain transfers * - WETH handling for native token wrapping @@ -34,14 +32,7 @@ import {IOrchestrator_v1} from * - Payment order processing and validation * - Bridge data storage and retrieval * - Support for Base network (chainId: 8453) - * * @custom:security-contact security@inverter.network - * In case of any concerns or findings, please refer to our Security Policy - * at security.inverter.network - * - * @custom:version 1.0.0 - * @custom:standard-version 1.0.0 - * @author Inverter Network */ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { // Storage Variables @@ -67,10 +58,12 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { error FailedTransfer(); // External Functions - /// @notice Initializes the payment processor module - /// @param orchestrator_ The address of the orchestrator contract - /// @param metadata Module metadata - /// @param configData ABI encoded configuration data containing everClearSpoke and WETH addresses + /** + * @notice Initializes the payment processor module + * @param orchestrator_ The orchestrator contract address + * @param metadata Module metadata + * @param configData ABI encoded configuration data (everClearSpoke and WETH addresses) + */ function init( IOrchestrator_v1 orchestrator_, Metadata memory metadata, @@ -84,14 +77,15 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { weth = IWETH(weth_); } - /// @notice Processes multiple payment orders through the bridge - /// @param client The payment client contract interface - /// @param executionData Additional data needed for execution (encoded maxFee and TTL) + /** + * @notice Processes multiple payment orders through the bridge + * @param client The payment client contract interface + * @param executionData Additional data needed for execution (encoded maxFee and TTL) + */ function processPayments( IERC20PaymentClientBase_v1 client, bytes memory executionData ) external { - // Collect orders from the client IERC20PaymentClientBase_v1.PaymentOrder[] memory orders; (orders,,) = client.collectPaymentOrders(); @@ -112,15 +106,17 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { ); _paymentId++; intentId[address(client)][orders[i].recipient] = bytes32(bridgeData); - // Inform the client about the processed amount client.amountPaid(orders[i].paymentToken, orders[i].amount); } } - /// @notice Cancels a pending transfer and returns funds to the client - /// @param client The payment client address - /// @param recipient The recipient address - /// @param pendingIntentId The intentId to cancel + /** + * @notice Cancels a pending transfer and returns funds to the client + * @param client The payment client address + * @param recipient The recipient address + * @param pendingIntentId The intentId to cancel + * @param order The payment order details + */ function cancelTransfer( address client, address recipient, @@ -129,9 +125,7 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { ) external { _validateTransferRequest(client, recipient, pendingIntentId); _cleanupFailedTransfer(client, recipient, pendingIntentId); - //Set approval to 0 - IERC20(order.paymentToken).approve(address(everClearSpoke), 0); - // Just do the transfer inline + if (!IERC20(order.paymentToken).transfer(recipient, order.amount)) { revert FailedTransfer(); } @@ -139,6 +133,14 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { emit TransferCancelled(client, recipient, pendingIntentId, order.amount); } + /** + * @notice Retries a previously failed transfer + * @param client The payment client address + * @param recipient The recipient address + * @param pendingIntentId The failed intent ID + * @param order The payment order details + * @param executionData New execution data for retry + */ function retryFailedTransfer( address client, address recipient, @@ -160,9 +162,11 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { } // Public Functions - /// @notice Retrieves the bridge data for a specific payment ID - /// @param paymentId The unique identifier of the payment - /// @return The bridge data associated with the payment (encoded intentId) + /** + * @notice Retrieves the bridge data for a specific payment ID + * @param paymentId The unique identifier of the payment + * @return The bridge data associated with the payment + */ function getBridgeData(uint paymentId) public view @@ -173,10 +177,13 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { } // Internal Functions - /// @notice Execute the cross-chain bridge transfer - /// @dev Override this function to implement specific bridge logic - /// @param order The payment order containing all necessary transfer details - /// @return bridgeData Arbitrary data returned by the bridge implementation + /** + * @dev Execute the cross-chain bridge transfer + * @param order The payment order containing transfer details + * @param executionData Additional execution parameters + * @param client The client address + * @return bridgeData Data returned by the bridge implementation + */ function _executeBridgeTransfer( IERC20PaymentClientBase_v1.PaymentOrder memory order, bytes memory executionData, @@ -195,6 +202,12 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { return abi.encode(_intentId); } + /** + * @dev Creates a new cross-chain intent for payment transfer + * @param order The payment order details + * @param executionData Additional execution parameters + * @return The ID of the created intent + */ function createCrossChainIntent( IERC20PaymentClientBase_v1.PaymentOrder memory order, bytes memory executionData @@ -235,7 +248,13 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { ); } - /// @dev Common validation for transfer-related operations + /** + * @dev Validates a transfer request + * @param client The payment client address + * @param recipient The recipient address + * @param pendingIntentId The intent ID to validate + * @return The amount of the failed transfer + */ function _validateTransferRequest( address client, address recipient, @@ -257,7 +276,12 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { return failedAmount; } - /// @dev Helper function to clean up storage after handling a failed transfer + /** + * @dev Cleans up storage after handling a failed transfer + * @param client The payment client address + * @param recipient The recipient address + * @param pendingIntentId The intent ID to clean up + */ function _cleanupFailedTransfer( address client, address recipient, @@ -267,6 +291,10 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { delete failedTransfers[client][recipient][pendingIntentId]; } + /** + * @dev Validates a payment order + * @param order The payment order to validate + */ function _validateOrder( IERC20PaymentClientBase_v1.PaymentOrder memory order ) internal pure { From a15150f01946d3ebc5dffada7f0d9c5dc9e26b6c Mon Sep 17 00:00:00 2001 From: Leandro Faria Date: Thu, 12 Dec 2024 13:18:27 -0500 Subject: [PATCH 64/93] fix:fmt and standard updates --- .../abstracts/CrossChainBase_v1.sol | 53 +++++++++---------- 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/src/modules/paymentProcessor/abstracts/CrossChainBase_v1.sol b/src/modules/paymentProcessor/abstracts/CrossChainBase_v1.sol index a954223f0..c4b0723a3 100644 --- a/src/modules/paymentProcessor/abstracts/CrossChainBase_v1.sol +++ b/src/modules/paymentProcessor/abstracts/CrossChainBase_v1.sol @@ -9,28 +9,36 @@ import {IERC20PaymentClientBase_v1} from import {Module_v1} from "src/modules/base/Module_v1.sol"; import {ERC165Upgradeable, Module_v1} from "src/modules/base/Module_v1.sol"; import {ICrossChainBase_v1} from "../interfaces/ICrosschainBase_v1.sol"; + /** * @title Cross-chain Base Contract - * - * @notice Abstract base contract providing core cross-chain functionality for payment processors. - * + * @notice Abstract base contract providing core cross-chain functionality for payment + * processors. * @dev This contract implements fundamental cross-chain operations and provides: * - Bridge data storage and retrieval functionality * - Abstract interface for bridge transfer execution * - Integration with the Module_v1 base contract * - Implementation of ICrossChainBase_v1 interface * - ERC165 interface support for cross-chain functionality - * * @custom:security-contact security@inverter.network - * In case of any concerns or findings, please refer to our Security Policy - * at security.inverter.network or email us directly! - * + * In case of any concerns or findings, please refer to our + * Security Policy at security.inverter.network or email us + * directly! * @author Inverter Network */ - abstract contract CrossChainBase_v1 is ICrossChainBase_v1, Module_v1 { + // Storage Variables mapping(uint => bytes) internal _bridgeData; + // External Functions + /// @notice Process payments for a given payment client + /// @param client The payment client to process payments for + function processPayments(IERC20PaymentClientBase_v1 client) + external + virtual + {} + + // Public Functions /// @inheritdoc ERC165Upgradeable function supportsInterface(bytes4 interfaceId_) public @@ -43,25 +51,6 @@ abstract contract CrossChainBase_v1 is ICrossChainBase_v1, Module_v1 { || super.supportsInterface(interfaceId_); } - //-------------------------------------------------------------------------- - // Virtual Functions - - /// @notice Execute the cross-chain bridge transfer - /// @dev Override this function to implement specific bridge logic - /// @param order The payment order containing all necessary transfer details - /// @return bridgeData Arbitrary data returned by the bridge implementation - function _executeBridgeTransfer( - IERC20PaymentClientBase_v1.PaymentOrder memory order, - bytes memory executionData - ) internal virtual returns (bytes memory) {} - - /// @notice Process payments for a given payment client - /// @param client The payment client to process payments for - function processPayments(IERC20PaymentClientBase_v1 client) - external - virtual - {} - /// @notice Get the bridge data for a given payment ID /// @param paymentId The ID of the payment to get the bridge data for /// @return The bridge data for the given payment ID @@ -71,4 +60,14 @@ abstract contract CrossChainBase_v1 is ICrossChainBase_v1, Module_v1 { virtual returns (bytes memory) {} + + // Internal Functions + /// @notice Execute the cross-chain bridge transfer + /// @dev Override this function to implement specific bridge logic + /// @param order The payment order containing all necessary transfer details + /// @return bridgeData Arbitrary data returned by the bridge implementation + function _executeBridgeTransfer( + IERC20PaymentClientBase_v1.PaymentOrder memory order, + bytes memory executionData + ) internal virtual returns (bytes memory) {} } From c3fbdf85267dfbaf6de04c7b8b4647105960a890 Mon Sep 17 00:00:00 2001 From: Leandro Faria Date: Thu, 12 Dec 2024 21:42:58 -0500 Subject: [PATCH 65/93] chore:delete bridging folder --- .../PP_Connext_Crosschain_v1.sol | 6 +- .../abstracts/CrossChainBase_v1.sol | 3 +- .../abstracts/CrossChainBase_v1.t.sol | 0 .../abstracts/CrossChainBase_v1_Exposed.sol | 0 .../abstracts/CrosschainBase_v1.t.sol | 175 +++++++++++++----- .../abstracts/CrosschainBase_v1_Exposed.sol | 19 +- .../abstracts/FM_Template_v1.t.sol | 0 .../abstracts/FM_Template_v1_Exposed.sol | 0 .../abstracts/Interfaces/IEverClearSpoke.sol | 0 .../PP_Connext_Crosschain_v1_Exposed.sol | 2 +- .../PP_Connext_Crosschain_v1_Test.t.sol | 6 +- .../abstracts/mocks/Mock_EverclearPayment.sol | 0 12 files changed, 139 insertions(+), 72 deletions(-) rename test/modules/paymentProcessor/{bridging => }/abstracts/CrossChainBase_v1.t.sol (100%) rename test/modules/paymentProcessor/{bridging => }/abstracts/CrossChainBase_v1_Exposed.sol (100%) rename test/modules/paymentProcessor/{bridging => }/abstracts/FM_Template_v1.t.sol (100%) rename test/modules/paymentProcessor/{bridging => }/abstracts/FM_Template_v1_Exposed.sol (100%) rename test/modules/paymentProcessor/{bridging => }/abstracts/Interfaces/IEverClearSpoke.sol (100%) rename test/modules/paymentProcessor/{bridging => }/abstracts/PP_Connext_Crosschain_v1_Exposed.sol (94%) rename test/modules/paymentProcessor/{bridging => }/abstracts/PP_Connext_Crosschain_v1_Test.t.sol (98%) rename test/modules/paymentProcessor/{bridging => }/abstracts/mocks/Mock_EverclearPayment.sol (100%) diff --git a/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol b/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol index abe123a28..82e8ad56c 100644 --- a/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol +++ b/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol @@ -150,7 +150,7 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { ) external { _validateTransferRequest(client, recipient, pendingIntentId); - bytes32 newIntentId = createCrossChainIntent(order, executionData); + bytes32 newIntentId = _createCrossChainIntent(order, executionData); if (newIntentId == bytes32(0)) { revert Module__PP_Crosschain__MessageDeliveryFailed( 8453, 8453, executionData @@ -189,7 +189,7 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { bytes memory executionData, address client ) internal returns (bytes memory) { - bytes32 _intentId = createCrossChainIntent(order, executionData); + bytes32 _intentId = _createCrossChainIntent(order, executionData); if (_intentId == bytes32(0)) { failedTransfers[client][order.recipient][_intentId] = order.amount; @@ -208,7 +208,7 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { * @param executionData Additional execution parameters * @return The ID of the created intent */ - function createCrossChainIntent( + function _createCrossChainIntent( IERC20PaymentClientBase_v1.PaymentOrder memory order, bytes memory executionData ) internal returns (bytes32) { diff --git a/src/modules/paymentProcessor/abstracts/CrossChainBase_v1.sol b/src/modules/paymentProcessor/abstracts/CrossChainBase_v1.sol index c4b0723a3..5ab6392ad 100644 --- a/src/modules/paymentProcessor/abstracts/CrossChainBase_v1.sol +++ b/src/modules/paymentProcessor/abstracts/CrossChainBase_v1.sol @@ -14,12 +14,11 @@ import {ICrossChainBase_v1} from "../interfaces/ICrosschainBase_v1.sol"; * @title Cross-chain Base Contract * @notice Abstract base contract providing core cross-chain functionality for payment * processors. - * @dev This contract implements fundamental cross-chain operations and provides: + * @dev This contract exposes fundamental cross-chain operations and provides: * - Bridge data storage and retrieval functionality * - Abstract interface for bridge transfer execution * - Integration with the Module_v1 base contract * - Implementation of ICrossChainBase_v1 interface - * - ERC165 interface support for cross-chain functionality * @custom:security-contact security@inverter.network * In case of any concerns or findings, please refer to our * Security Policy at security.inverter.network or email us diff --git a/test/modules/paymentProcessor/bridging/abstracts/CrossChainBase_v1.t.sol b/test/modules/paymentProcessor/abstracts/CrossChainBase_v1.t.sol similarity index 100% rename from test/modules/paymentProcessor/bridging/abstracts/CrossChainBase_v1.t.sol rename to test/modules/paymentProcessor/abstracts/CrossChainBase_v1.t.sol diff --git a/test/modules/paymentProcessor/bridging/abstracts/CrossChainBase_v1_Exposed.sol b/test/modules/paymentProcessor/abstracts/CrossChainBase_v1_Exposed.sol similarity index 100% rename from test/modules/paymentProcessor/bridging/abstracts/CrossChainBase_v1_Exposed.sol rename to test/modules/paymentProcessor/abstracts/CrossChainBase_v1_Exposed.sol diff --git a/test/modules/paymentProcessor/abstracts/CrosschainBase_v1.t.sol b/test/modules/paymentProcessor/abstracts/CrosschainBase_v1.t.sol index 550e0119f..1e641fd5f 100644 --- a/test/modules/paymentProcessor/abstracts/CrosschainBase_v1.t.sol +++ b/test/modules/paymentProcessor/abstracts/CrosschainBase_v1.t.sol @@ -1,7 +1,7 @@ //SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.0; -//Internal Dependencies +// Internal import { ModuleTest, IModule_v1, @@ -10,77 +10,152 @@ import { import {OZErrors} from "test/utils/errors/OZErrors.sol"; import {ICrossChainBase_v1} from "src/modules/paymentProcessor/interfaces/ICrosschainBase_v1.sol"; -import {CrosschainBase_v1} from - "src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol"; -//External Dependencies -import {Clones} from "@oz/proxy/Clones.sol"; - -//Tests and Mocks -// import cr +import {CrossChainBase_v1} from + "src/modules/paymentProcessor/abstracts/CrossChainBase_v1.sol"; import { IERC20PaymentClientBase_v1, ERC20PaymentClientBaseV1Mock, ERC20Mock } from "test/utils/mocks/modules/paymentClient/ERC20PaymentClientBaseV1Mock.sol"; -//import exposed -import {CrosschainBase_v1_Exposed} from "./CrosschainBase_v1_Exposed.sol"; - -//System under test (SuT) -// import { -// IPP_CrossChain_v1, -// PP_CrossChain_v1, -// IPaymentProcessor_v1 -// } from "src/templates/modules/PP_Template_v1.sol"; +import {CrossChainBase_v1_Exposed} from "./CrossChainBase_v1_Exposed.sol"; + import {IPaymentProcessor_v1} from "src/orchestrator/interfaces/IOrchestrator_v1.sol"; import {ICrossChainBase_v1} from "src/modules/paymentProcessor/interfaces/ICrosschainBase_v1.sol"; +//External Dependencies +import {OZErrors} from "test/utils/errors/OZErrors.sol"; +import {Clones} from "@oz/proxy/Clones.sol"; -/** - * @title Inverter Template Payment Processor - * - * @notice Basic template payment processor used to showcase the unit testing setup - * - * @dev Not all functions are tested in this template. Placeholders of the functions that are not tested are added - * into the contract. This test showcases the following: - * - Inherit from the ModuleTest contract to enable interaction with the Inverter workflow. - * - Showcases the setup of the workflow, uses in test unit tests. - * - Pre-defined layout for all setup and functions to be tested. - * - Shows the use of Gherkin for documenting the testing. VS Code extension used for formatting is recommended. - * - Shows the use of the modifierInPlace pattern to test the modifier placement. - * - * @custom:security-contact security@inverter.network - * In case of any concerns or findings, please refer to our Security Policy - * at security.inverter.network or email us directly! - * - * @author Inverter Network - */ -contract CrosschainBase_v1_Test is ModuleTest { +contract CrossChainBase_v1_Test is ModuleTest { //-------------------------------------------------------------------------- //Constants //-------------------------------------------------------------------------- - //State - //Mocks ERC20PaymentClientBaseV1Mock paymentClient; - - //System under test (SuT) - CrosschainBase_v1 public paymentProcessor; - //PP_CrossChain_v1_Exposed public paymentProcessor; + CrossChainBase_v1_Exposed public crossChainBase; //-------------------------------------------------------------------------- //Setup - function setUp() public {} + function setUp() public { + //This function is used to setup the unit test + //Deploy the SuT + address impl = address(new CrossChainBase_v1_Exposed(block.chainid)); + crossChainBase = CrossChainBase_v1_Exposed(Clones.clone(impl)); + + //Setup the module to test + _setUpOrchestrator(crossChainBase); + + //General setup for other contracts in the workflow + _authorizer.setIsAuthorized(address(this), true); + //Initiate the PP with the medata and config data + crossChainBase.init(_orchestrator, _METADATA, abi.encode(1)); + + //Setup other modules needed in the unit tests. + //In this case a payment client is needed to test the PP_Template_v1. + impl = address(new ERC20PaymentClientBaseV1Mock()); + paymentClient = ERC20PaymentClientBaseV1Mock(Clones.clone(impl)); + //Adding the payment client is done through a timelock mechanism + _orchestrator.initiateAddModuleWithTimelock(address(paymentClient)); + vm.warp(block.timestamp + _orchestrator.MODULE_UPDATE_TIMELOCK()); + _orchestrator.executeAddModule(address(paymentClient)); + //Init payment client + paymentClient.init(_orchestrator, _METADATA, bytes("")); + paymentClient.setIsAuthorized(address(crossChainBase), true); + paymentClient.setToken(_token); + } //-------------------------------------------------------------------------- //Test: Initialization + /* + └── Given the contract is not initialized + └── When initializing the contract + └── Then it should set the correct orchestrator address */ + + function testInit() public override(ModuleTest) { + assertEq(address(crossChainBase.orchestrator()), address(_orchestrator)); + } + + //-------------------------------------------------------------------------- + //Test: Interface Support + /* + └── Given the contract is initialized + └── When checking for ICrossChainBase_v1 interface support + └── Then it should return true + └── When checking for an unknown interface + └── Then it should return false */ + function testSupportsInterface() public { + // Test for ICrossChainBase_v1 interface support + bytes4 interfaceId = type(ICrossChainBase_v1).interfaceId; + assertTrue(crossChainBase.supportsInterface(interfaceId)); + } + + function testSupportsInterface_revertsGivenUnknownInterface() public { + bytes4 randomInterfaceId = bytes4(keccak256("random()")); + assertFalse(crossChainBase.supportsInterface(randomInterfaceId)); + } - //Test if the orchestrator is correctly set - function testInit() public override(ModuleTest) {} + /* + └── Given the contract is already initialized + └── When trying to reinitialize + └── Then it should revert with Initializable__InvalidInitialization */ + function testReinitFails() public override(ModuleTest) { + vm.expectRevert(OZErrors.Initializable__InvalidInitialization); + crossChainBase.init(_orchestrator, _METADATA, abi.encode(1)); + } + //-------------------------------------------------------------------------- + //Test: executeBridgeTransfer + + /* + └── Given an empty payment order is created + └── When executeBridgeTransfer is called + └── Then it should return empty bytes */ + function testExecuteBridgeTransfer_worksGivenEmptyPaymentOrder() public { + address[] memory setupRecipients = new address[](1); + setupRecipients[0] = address(1); + uint[] memory setupAmounts = new uint[](1); + setupAmounts[0] = 100 ether; + + IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = + _createPaymentOrders(1, setupRecipients, setupAmounts); + paymentClient.addPaymentOrders(orders); - //Test the interface support - function testSupportsInterface() public {} + bytes memory executionData = abi.encode(0, 0); //maxFee and ttl setup + + bytes memory result = crossChainBase.exposed_executeBridgeTransfer( + orders[0], executionData + ); + assertEq(result, bytes("")); + } + //-------------------------------------------------------------------------- + //Helper Functions - //Test the reinit function - function testReinitFails() public override(ModuleTest) {} + function _createPaymentOrders( + uint orderCount, + address[] memory recipients, + uint[] memory amounts + ) + internal + view + returns (IERC20PaymentClientBase_v1.PaymentOrder[] memory) + { + // Sanity checks for array lengths + require( + recipients.length == orderCount && amounts.length == orderCount, + "Array lengths must match orderCount" + ); + IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = + new IERC20PaymentClientBase_v1.PaymentOrder[](orderCount); + for (uint i = 0; i < orderCount; i++) { + orders[i] = IERC20PaymentClientBase_v1.PaymentOrder({ + recipient: recipients[i], + paymentToken: address(0xabcd), + amount: amounts[i], + start: block.timestamp, + cliff: 0, + end: block.timestamp + 1 days + }); + } + return orders; + } } diff --git a/test/modules/paymentProcessor/abstracts/CrosschainBase_v1_Exposed.sol b/test/modules/paymentProcessor/abstracts/CrosschainBase_v1_Exposed.sol index 1a3e49f5f..8d78f964f 100644 --- a/test/modules/paymentProcessor/abstracts/CrosschainBase_v1_Exposed.sol +++ b/test/modules/paymentProcessor/abstracts/CrosschainBase_v1_Exposed.sol @@ -1,27 +1,20 @@ // SPDX-License-Identifier: LGPL-3.0-only -pragma solidity 0.8.23; // Internal Dependencies //import {PP_CrossChain_v1} from "src/templates/modules/PP_Template_v1.sol"; -import {CrosschainBase_v1} from - "src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol"; +import {CrossChainBase_v1} from + "src/modules/paymentProcessor/abstracts/CrossChainBase_v1.sol"; import {IERC20PaymentClientBase_v1} from "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; -contract CrosschainBase_v1_Exposed is CrosschainBase_v1 { - /// @notice Implementation of the bridge transfer logic using EverClear - ///// @inheritdoc CrosschainBase_v1 +contract CrossChainBase_v1_Exposed is CrossChainBase_v1 { + constructor(uint chainId_) CrossChainBase_v1() {} + ///// @inheritdoc CrossChainBase_v1 + function exposed_executeBridgeTransfer( IERC20PaymentClientBase_v1.PaymentOrder memory order, bytes memory executionData ) external payable returns (bytes memory) { return _executeBridgeTransfer(order, executionData); } - - function _executeBridgeTransfer( - IERC20PaymentClientBase_v1.PaymentOrder memory order, - bytes memory executionData - ) internal override(CrosschainBase_v1) returns (bytes memory) { - // Add default return value here for testing - } } diff --git a/test/modules/paymentProcessor/bridging/abstracts/FM_Template_v1.t.sol b/test/modules/paymentProcessor/abstracts/FM_Template_v1.t.sol similarity index 100% rename from test/modules/paymentProcessor/bridging/abstracts/FM_Template_v1.t.sol rename to test/modules/paymentProcessor/abstracts/FM_Template_v1.t.sol diff --git a/test/modules/paymentProcessor/bridging/abstracts/FM_Template_v1_Exposed.sol b/test/modules/paymentProcessor/abstracts/FM_Template_v1_Exposed.sol similarity index 100% rename from test/modules/paymentProcessor/bridging/abstracts/FM_Template_v1_Exposed.sol rename to test/modules/paymentProcessor/abstracts/FM_Template_v1_Exposed.sol diff --git a/test/modules/paymentProcessor/bridging/abstracts/Interfaces/IEverClearSpoke.sol b/test/modules/paymentProcessor/abstracts/Interfaces/IEverClearSpoke.sol similarity index 100% rename from test/modules/paymentProcessor/bridging/abstracts/Interfaces/IEverClearSpoke.sol rename to test/modules/paymentProcessor/abstracts/Interfaces/IEverClearSpoke.sol diff --git a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Exposed.sol b/test/modules/paymentProcessor/abstracts/PP_Connext_Crosschain_v1_Exposed.sol similarity index 94% rename from test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Exposed.sol rename to test/modules/paymentProcessor/abstracts/PP_Connext_Crosschain_v1_Exposed.sol index b0ac06853..f18befe37 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Exposed.sol +++ b/test/modules/paymentProcessor/abstracts/PP_Connext_Crosschain_v1_Exposed.sol @@ -20,7 +20,7 @@ contract PP_Connext_Crosschain_v1_Exposed is PP_Connext_Crosschain_v1 { IERC20PaymentClientBase_v1.PaymentOrder memory order, bytes memory executionData ) external returns (bytes32) { - return createCrossChainIntent(order, executionData); + return _createCrossChainIntent(order, executionData); } function exposed_setFailedTransfer( diff --git a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol b/test/modules/paymentProcessor/abstracts/PP_Connext_Crosschain_v1_Test.t.sol similarity index 98% rename from test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol rename to test/modules/paymentProcessor/abstracts/PP_Connext_Crosschain_v1_Test.t.sol index b33bb2d45..af7b8c010 100644 --- a/test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Test.t.sol +++ b/test/modules/paymentProcessor/abstracts/PP_Connext_Crosschain_v1_Test.t.sol @@ -13,7 +13,7 @@ import {PP_Connext_Crosschain_v1} from import {CrossChainBase_v1} from "src/modules/paymentProcessor/abstracts/CrossChainBase_v1.sol"; import {CrossChainBase_v1_Exposed} from - "test/modules/paymentProcessor/bridging/abstracts/CrossChainBase_v1_Exposed.sol"; + "test/modules/paymentProcessor/abstracts/CrossChainBase_v1_Exposed.sol"; import {IERC20PaymentClientBase_v1} from "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; import {ICrossChainBase_v1} from @@ -21,10 +21,10 @@ import {ICrossChainBase_v1} from import {IPaymentProcessor_v1} from "src/modules/paymentProcessor/IPaymentProcessor_v1.sol"; import {PP_Connext_Crosschain_v1_Exposed} from - "test/modules/paymentProcessor/bridging/abstracts/PP_Connext_Crosschain_v1_Exposed.sol"; + "test/modules/paymentProcessor/abstracts/PP_Connext_Crosschain_v1_Exposed.sol"; // Tests and Mocks import {Mock_EverclearPayment} from - "test/modules/paymentProcessor/bridging/abstracts/mocks/Mock_EverclearPayment.sol"; + "test/modules/paymentProcessor/abstracts/mocks/Mock_EverclearPayment.sol"; import { IERC20PaymentClientBase_v1, ERC20PaymentClientBaseV1Mock diff --git a/test/modules/paymentProcessor/bridging/abstracts/mocks/Mock_EverclearPayment.sol b/test/modules/paymentProcessor/abstracts/mocks/Mock_EverclearPayment.sol similarity index 100% rename from test/modules/paymentProcessor/bridging/abstracts/mocks/Mock_EverclearPayment.sol rename to test/modules/paymentProcessor/abstracts/mocks/Mock_EverclearPayment.sol From 197a65abbf794280303c9a0742aa4ac56d6e2271 Mon Sep 17 00:00:00 2001 From: Leandro Faria Date: Mon, 16 Dec 2024 18:19:06 -0600 Subject: [PATCH 66/93] fix:rebase onto feature --- test/modules/paymentProcessor/PP_Connext_Bridge.t.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/modules/paymentProcessor/PP_Connext_Bridge.t.sol b/test/modules/paymentProcessor/PP_Connext_Bridge.t.sol index 64b560ee4..fe6d4615a 100644 --- a/test/modules/paymentProcessor/PP_Connext_Bridge.t.sol +++ b/test/modules/paymentProcessor/PP_Connext_Bridge.t.sol @@ -10,8 +10,8 @@ import { import {OZErrors} from "test/utils/errors/OZErrors.sol"; import {ICrossChainBase_v1} from "src/modules/paymentProcessor/interfaces/ICrosschainBase_v1.sol"; -import {CrosschainBase_v1} from - "src/modules/paymentProcessor/abstracts/CrosschainBase_v1.sol"; +import {CrossChainBase_v1} from + "src/modules/paymentProcessor/abstracts/CrossChainBase_v1.sol"; //External Dependencies import {Clones} from "@oz/proxy/Clones.sol"; From 90615c27b6beca361d505ef3888d09109e198575 Mon Sep 17 00:00:00 2001 From: Leandro Faria Date: Tue, 17 Dec 2024 10:24:50 -0600 Subject: [PATCH 67/93] chore:remove redundant files --- .../abstracts/FM_Template_v1.sol | 196 ------------- .../abstracts/IFM_Template_v1.sol | 72 ----- .../abstracts/IPP_Template_v1.sol | 103 ------- .../abstracts/PP_Template_v1.sol | 265 ------------------ 4 files changed, 636 deletions(-) delete mode 100644 src/modules/paymentProcessor/abstracts/FM_Template_v1.sol delete mode 100644 src/modules/paymentProcessor/abstracts/IFM_Template_v1.sol delete mode 100644 src/modules/paymentProcessor/abstracts/IPP_Template_v1.sol delete mode 100644 src/modules/paymentProcessor/abstracts/PP_Template_v1.sol diff --git a/src/modules/paymentProcessor/abstracts/FM_Template_v1.sol b/src/modules/paymentProcessor/abstracts/FM_Template_v1.sol deleted file mode 100644 index 437e7c7d3..000000000 --- a/src/modules/paymentProcessor/abstracts/FM_Template_v1.sol +++ /dev/null @@ -1,196 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity 0.8.23; - -// Internal -import {IOrchestrator_v1} from - "src/orchestrator/interfaces/IOrchestrator_v1.sol"; -import {ERC165Upgradeable, Module_v1} from "src/modules/base/Module_v1.sol"; -import {IFM_Template_v1} from "./IFM_Template_v1.sol"; -import {IFundingManager_v1} from "@fm/IFundingManager_v1.sol"; - -// External -import {IERC20} from "@oz/token/ERC20/IERC20.sol"; -import {SafeERC20} from "@oz/token/ERC20/utils/SafeERC20.sol"; - -/** - * @title Inverter Template Funding Manager - * - * @notice Basic template funding manager used as base for developing new - * funding managers. - * - * @dev This contract is used to showcase a basic setup for a funding - * manager. The contract showcases the following: - * - Inherit from the Module_v1 contract to enable interaction with - * the Inverter workflow. - * - Use of the IFundingManager_v1 interface to facilitate - * interaction as a Funding Manager. - * - Implement custom interface which has all the public facing - * functions, errors, events and structs. - * - Pre-defined layout for all contract functions, modifiers, state - * variables etc. - * - Use of the ERC165Upgradeable contract to check for interface - * support. - * - * @custom:security-contact security@inverter.network - * In case of any concerns or findings, please refer - * to our Security Policy at security.inverter.network - * or email us directly! - * - * @custom:version 1.0.0 - * - * @author Inverter Network - */ -contract FM_Template_v1 is IFM_Template_v1, Module_v1 { - // ========================================================================= - // Libraries - - using SafeERC20 for IERC20; - - // ========================================================================= - // ERC165 - - /// @inheritdoc ERC165Upgradeable - function supportsInterface(bytes4 interfaceId_) - public - view - virtual - override(Module_v1) - returns (bool) - { - return interfaceId_ == type(IFM_Template_v1).interfaceId - || interfaceId_ == type(IFundingManager_v1).interfaceId - || super.supportsInterface(interfaceId_); - } - - // ========================================================================= - // Constants - - // Add constants here - - // ========================================================================= - // State - - /// @notice Mapping of user addresses to their deposited token amounts. - mapping(address user => uint amount) internal _depositedAmounts; - - /// @notice The orchestrator token. - IERC20 internal _orchestratorToken; - - /// @notice Storage gap for future upgrades. - uint[50] private __gap; - - // ========================================================================= - // Modifiers - - // Add modifiers here - - // ========================================================================= - // Constructor & Init - - /// @inheritdoc Module_v1 - function init( - IOrchestrator_v1 orchestrator_, - Metadata memory metadata_, - bytes memory configData_ - ) external override(Module_v1) initializer { - __Module_init(orchestrator_, metadata_); - - // Decode module specific init data through use of configData bytes. - // This value is an example value used to showcase the setters/getters - // and internal functions/state formatting style. - (address orchestratorTokenAddress) = abi.decode(configData_, (address)); - - // Set init state. - _orchestratorToken = IERC20(orchestratorTokenAddress); - } - - // ========================================================================= - // Public - Getters - - /// @inheritdoc IFM_Template_v1 - function getDepositedAmount(address user_) - external - view - virtual - returns (uint amount_) - { - amount_ = _depositedAmounts[user_]; - } - - /// @inheritdoc IFundingManager_v1 - function token() - external - view - override - returns (IERC20 orchestratorToken_) - { - orchestratorToken_ = _orchestratorToken; - } - - // ========================================================================= - // Public - Mutating - - /// @inheritdoc IFM_Template_v1 - function deposit(uint amount_) external virtual { - // Validate parameters. - if (amount_ == 0) { - revert Module__FM_Template_InvalidAmount(); - } - - // Update state. - _depositedAmounts[_msgSender()] += amount_; - - // Transfer tokens. - _orchestratorToken.safeTransferFrom( - _msgSender(), address(this), amount_ - ); - - // Emit event. - emit Deposited(_msgSender(), amount_); - } - - /// @inheritdoc IFundingManager_v1 - /// @dev Only the payment client can call this function. - function transferOrchestratorToken(address to, uint amount) - external - virtual - override - onlyPaymentClient - { - // Validate parameters. - _validateOrchestratorTokenTransfer(to, amount); - - // Transfer tokens. - _orchestratorToken.safeTransfer(to, amount); - - // Emit event. - emit TransferOrchestratorToken(to, amount); - } - - // ========================================================================= - // Internal - - /// @notice Validates the transfer of orchestrator token. - /// @param to_ Address to transfer to. - /// @param amount_ Amount to transfer. - function _validateOrchestratorTokenTransfer(address to_, uint amount_) - internal - view - virtual - { - if (to_ == address(0)) { - revert Module__FM_Template__ReceiverNotValid(); - } - - if (amount_ == 0) { - revert Module__FM_Template_InvalidAmount(); - } - - if (_depositedAmounts[_msgSender()] < amount_) { - revert Module__FM_Template_InvalidAmount(); - } - } - - // ========================================================================= - // Overridden Internal Functions -} diff --git a/src/modules/paymentProcessor/abstracts/IFM_Template_v1.sol b/src/modules/paymentProcessor/abstracts/IFM_Template_v1.sol deleted file mode 100644 index f644c53df..000000000 --- a/src/modules/paymentProcessor/abstracts/IFM_Template_v1.sol +++ /dev/null @@ -1,72 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity ^0.8.0; - -import {IFundingManager_v1} from "@fm/IFundingManager_v1.sol"; - -/** - * @title Inverter Template Funding Manager - * - * @notice Basic template for funding manager inteface used as base for developing new - * funding managers. - * - * @dev This contract is used to showcase a basic setup for a funding - * manager. The contract showcases the following: - * - Inherit from the Module_v1 contract to enable interaction with - * the Inverter workflow. - * - Use of the IFundingManager_v1 interface to facilitate - * interaction as a Funding Manager. - * - Implement custom interface which has all the public facing - * functions, errors, events and structs. - * - Pre-defined layout for all contract functions, modifiers, state - * variables etc. - * - Use of the ERC165Upgradeable contract to check for interface - * support. - * - * @custom:security-contact security@inverter.network - * In case of any concerns or findings, please refer - * to our Security Policy at security.inverter.network - * or email us directly! - * - * @custom:version 1.0.0 - * - * @author Inverter Network - */ -interface IFM_Template_v1 is IFundingManager_v1 { - // ========================================================================= - // Structs - - // ========================================================================= - // Events - - /// @notice Emit when the token amount has been deposited. - /// @param sender_ The address of the depositor. - /// @param amount_ The amount of tokens deposited. - event Deposited(address indexed sender_, uint amount_); - - // ========================================================================= - // Errors - - /// @notice Amount can not be zero. - error Module__FM_Template_InvalidAmount(); - - /// @notice Token receiver is not valid. - error Module__FM_Template__ReceiverNotValid(); - - // ========================================================================= - // Public - Getters - - /// @notice Returns the deposited balance of a specific address. - /// @param user_ The address of the user. - /// @return amount_ Deposited amount of the user. - function getDepositedAmount(address user_) - external - view - returns (uint amount_); - - // ========================================================================= - // Public - Mutating - - /// @notice Deposits tokens to the funding manager. - /// @param amount_ The amount of tokens to deposit. - function deposit(uint amount_) external; -} diff --git a/src/modules/paymentProcessor/abstracts/IPP_Template_v1.sol b/src/modules/paymentProcessor/abstracts/IPP_Template_v1.sol deleted file mode 100644 index a8ca0d9ec..000000000 --- a/src/modules/paymentProcessor/abstracts/IPP_Template_v1.sol +++ /dev/null @@ -1,103 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity ^0.8.0; - -// Internal -import {IPaymentProcessor_v1} from "@pp/IPaymentProcessor_v1.sol"; - -/** - * @title Inverter Template Payment Processor - * - * @notice Basic template payment processor used as base for developing new - * payment processors. - * - * @dev This contract is used to showcase a basic setup for a payment - * processor. The contract showcases the following: - * - Inherit from the Module_v1 contract to enable interaction with - * the Inverter workflow. - * - Use of the IPaymentProcessor_v1 interface to facilitate - * interaction with a payment client. - * - Implement custom interface which has all the public facing - * functions, errors, events and structs. - * - Pre-defined layout for all contract functions, modifiers, state - * variables etc. - * - Use of the ERC165Upgradeable contract to check for interface - * support. - * - * @custom:security-contact security@inverter.network - * In case of any concerns or findings, please refer - * to our Security Policy at security.inverter.network - * or email us directly! - * - * @custom:version 1.0.0 - * - * @author Inverter Network - */ -interface IPP_Template_v1 is IPaymentProcessor_v1 { - //-------------------------------------------------------------------------- - // Structs - - /// @notice Struct to hold cross-chain message data - struct CrossChainMessage { - uint messageId; - address sourceChain; - address targetChain; - bytes payload; - bool executed; - } - - //-------------------------------------------------------------------------- - // Events - - /// @notice Emit when new payout amount has been set. - /// @param oldPayoutAmount_ Old payout amount. - /// @param newPayoutAmount_ Newly set payout amount. - event NewPayoutAmountMultiplierSet( - uint indexed oldPayoutAmount_, uint indexed newPayoutAmount_ - ); - - /// @notice Emitted when a cross-chain message is sent - /// @param messageId Unique identifier for the message - /// @param targetChain Address of the target chain - event CrossChainMessageSent( - uint indexed messageId, address indexed targetChain - ); - - /// @notice Emitted when a cross-chain message is received - /// @param messageId Unique identifier for the message - /// @param sourceChain Address of the source chain - event CrossChainMessageReceived( - uint indexed messageId, address indexed sourceChain - ); - - //-------------------------------------------------------------------------- - // Errors - - /// @notice Amount can not be zero. - error Module__PP_Template_InvalidAmount(); - - /// @notice Client is not valid. - error Module__PP_Template__ClientNotValid(); - - error Module__PP_CrossChain__NotValidClient(); - - error Module__PP_CrossChain__InvalidAmount(); - - /// @notice Message has already been executed - error Module__PP_CrossChain_MessageAlreadyExecuted(); - /// @notice Invalid chain ID provided - error Module__PP_CrossChain_InvalidChainId(); - /// @notice Message verification failed - error Module__PP_CrossChain_MessageVerificationFailed(); - - //-------------------------------------------------------------------------- - // Public (Getter) - - /// @notice Returns the payout amount for each payment order. - /// @return payoutAmountMultiplier_ The payout amount multiplier. - function getPayoutAmountMultiplier() - external - returns (uint payoutAmountMultiplier_); - - //-------------------------------------------------------------------------- - // Public (Mutating) -} diff --git a/src/modules/paymentProcessor/abstracts/PP_Template_v1.sol b/src/modules/paymentProcessor/abstracts/PP_Template_v1.sol deleted file mode 100644 index e1be6e87b..000000000 --- a/src/modules/paymentProcessor/abstracts/PP_Template_v1.sol +++ /dev/null @@ -1,265 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity 0.8.23; - -// Internal -import {IOrchestrator_v1} from - "src/orchestrator/interfaces/IOrchestrator_v1.sol"; -import {IPaymentProcessor_v1} from "@pp/IPaymentProcessor_v1.sol"; -import {IERC20PaymentClientBase_v1} from - "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; -import {IPP_Template_v1} from "./IPP_Template_v1.sol"; -import {ERC165Upgradeable, Module_v1} from "src/modules/base/Module_v1.sol"; - -// External -import {IERC20} from "@oz/token/ERC20/IERC20.sol"; - -/** - * @title Inverter Template Payment Processor - * - * @notice Basic template payment processor used as base for developing new - * payment processors. - * - * @dev This contract is used to showcase a basic setup for a payment - * processor. The contract showcases the following: - * - Inherit from the Module_v1 contract to enable interaction with - * the Inverter workflow. - * - Use of the IPaymentProcessor_v1 interface to facilitate - * interaction with a payment client. - * - Implement custom interface which has all the public facing - * functions, errors, events and structs. - * - Pre-defined layout for all contract functions, modifiers, state - * variables etc. - * - Use of the ERC165Upgradeable contract to check for interface - * support. - * - * @custom:security-contact security@inverter.network - * In case of any concerns or findings, please refer - * to our Security Policy at security.inverter.network - * or email us directly! - * - * @custom:version 1.0.0 - * - * @author Inverter Network - */ -contract PP_Template_v1 is IPP_Template_v1, Module_v1 { - //-------------------------------------------------------------------------- - // Libraries - - // Add library usage here - - //-------------------------------------------------------------------------- - // ERC165 - - /// @inheritdoc ERC165Upgradeable - function supportsInterface(bytes4 interfaceId_) - public - view - virtual - override(Module_v1) - returns (bool) - { - return interfaceId_ == type(IPP_Template_v1).interfaceId - || interfaceId_ == type(IPaymentProcessor_v1).interfaceId - || super.supportsInterface(interfaceId_); - } - - //-------------------------------------------------------------------------- - // Constants - - // Add constants here - - //-------------------------------------------------------------------------- - // State - - /// @dev Payout amount multiplier. - uint internal _payoutAmountMultiplier; - - /// @dev Payment ID of the last processed payment order. - uint internal _paymentId; - - //-------------------------------------------------------------------------- - // Modifiers - - /// @dev Checks that the client is calling for itself. - modifier clientIsValid(address client_) { - // Modifier logic moved to internal function for contract size reduction. - _ensureValidClient(client_); - _; - } - - //-------------------------------------------------------------------------- - // Constructor & Init - - /// @inheritdoc Module_v1 - function init( - IOrchestrator_v1 orchestrator_, - Metadata memory metadata_, - bytes memory configData_ - ) external override(Module_v1) initializer { - __Module_init(orchestrator_, metadata_); - - // Decode module specific init data through use of configData bytes. - // This value is an example value used to showcase the setters/getters - // and internal functions/state formating style. - (uint payoutAmountMultiplier_) = abi.decode(configData_, (uint)); - - // Set init state. - _setPayoutAmountMultiplier(payoutAmountMultiplier_); - } - - //-------------------------------------------------------------------------- - // Public (Getters) - - /// @inheritdoc IPP_Template_v1 - function getPayoutAmountMultiplier() - external - view - returns (uint payoutAmount_) - { - return _payoutAmountMultiplier; - } - - //-------------------------------------------------------------------------- - // Public (Mutating) - - /// @inheritdoc IPaymentProcessor_v1 - function processPayments(IERC20PaymentClientBase_v1 client_) - external - clientIsValid(address(client_)) - { - // The IERC20PaymentClientBase_v1 client should be used to access - // created payment orders in the Logic Module (LM) implementing the - // interface. The interface should be referenced to see the different - // functionalities provided by the ERC20PaymentClientBase_v1. - - // Collect orders from the client - IERC20PaymentClientBase_v1.PaymentOrder[] memory orders; - (orders,,) = client_.collectPaymentOrders(); - - // Custom logic to proces the payment orders should be implemented - // below. This template implements a straight forward token transfer - // using the first order of the payment order array for simplicity. - - // Get payment order details - address recipient_ = orders[0].recipient; - address token_ = orders[0].paymentToken; - uint amount_ = orders[0].amount * _payoutAmountMultiplier; - _paymentId = _paymentId + 1; - - // Emit event of the IPaymentProcessor_v1. This is used by Inverter's - // Indexer. - emit PaymentOrderProcessed( - address(client_), recipient_, token_, amount_, 0, 0, 0 - ); - - // Transfer tokens from {IERC20PaymentClientBase_v1} to order - // recipients. - // Please note: When processing multiple payment orders and then - // letting the call revert as in this example might not be the best - // solution. Ways to handle this by implementing the `unclaimable` - // function can be found in the other Payment Processor (PP) - // implementations. - IERC20(token_).transferFrom(address(client_), recipient_, amount_); - - // Inform the client about the amount that was released, to keep - // the accounting correct. - client_.amountPaid(token_, amount_); - - // Emit event of the IPaymentProcessor_v1. This is used by Inverter's - // Indexer. - emit TokensReleased(recipient_, token_, amount_); - } - - /// @inheritdoc IPaymentProcessor_v1 - function cancelRunningPayments(IERC20PaymentClientBase_v1 client_) - external - view - clientIsValid(address(client_)) - { - // This function is used to implement custom logic to cancel running - // payments. If the nature of processing payments is one of direct - // processing then this function can be left empty, return nothing. - return; - } - - /// @inheritdoc IPaymentProcessor_v1 - function unclaimable( - address, /*client_*/ - address, /*token_*/ - address /*paymentReceiver_*/ - ) external pure returns (uint amount_) { - // This function is used to check if there are unclaimable tokens for a - // specific client, token and payment receiver. As this template only - // executes one payment order at a time, this function is not utilzed - // and can return 0. - return 0; - } - - /// @inheritdoc IPaymentProcessor_v1 - function claimPreviouslyUnclaimable( - address, /*client_*/ - address, /*token_*/ - address /*receiver_*/ - ) external virtual { - return; - } - - /// @inheritdoc IPaymentProcessor_v1 - function validPaymentOrder( - IERC20PaymentClientBase_v1.PaymentOrder memory order_ - ) external view returns (bool) { - // This function is used to validate the payment order created on the - // client side (LM_PC) with the input required by the Payment Processor - // (PP). The function should return true if the payment order is valid - // and false if it is not. - - // For this template, only the receiver is validated. - return _validPaymentReceiver(order_.recipient); - } - - //-------------------------------------------------------------------------- - // Internal - - /// @dev Internal function to set the new payout amount multiplier. - /// @param newPayoutAmountMultiplier_ Payout amount multiplier to be set in - // the state. Cannot be zero. - function _setPayoutAmountMultiplier(uint newPayoutAmountMultiplier_) - internal - { - if (newPayoutAmountMultiplier_ == 0) { - revert Module__PP_Template_InvalidAmount(); - } - emit NewPayoutAmountMultiplierSet( - _payoutAmountMultiplier, newPayoutAmountMultiplier_ - ); - _payoutAmountMultiplier = newPayoutAmountMultiplier_; - } - - /// @dev Validate whether the address is a valid payment receiver. - /// @param receiver_ Address to validate. - /// @return validPaymentReceiver_ True if address is valid. - function _validPaymentReceiver(address receiver_) - internal - view - virtual - returns (bool) - { - return !( - receiver_ == address(0) || receiver_ == _msgSender() - || receiver_ == address(this) - || receiver_ == address(orchestrator()) - || receiver_ == address(orchestrator().fundingManager().token()) - ); - } - - /// @dev Internal function to check whether the client is valid. - /// @param client_ Address to validate. - function _ensureValidClient(address client_) internal view { - if (_msgSender() != client_) { - revert Module__PP_Template__ClientNotValid(); - } - } - - //-------------------------------------------------------------------------- - // Internal override -} From 8f34e00dea797a4adb418be123325c6f7665ffd7 Mon Sep 17 00:00:00 2001 From: Leandro Faria Date: Tue, 17 Dec 2024 11:40:07 -0600 Subject: [PATCH 68/93] fix:add standard to test file --- .../abstracts/CrossChainBase_v1.sol | 4 +- .../abstracts/PP_Crosschain_v1.sol | 66 +++++++++++-------- .../PP_Connext_Crosschain_v1_Test.t.sol | 60 ++++++++++------- 3 files changed, 79 insertions(+), 51 deletions(-) diff --git a/src/modules/paymentProcessor/abstracts/CrossChainBase_v1.sol b/src/modules/paymentProcessor/abstracts/CrossChainBase_v1.sol index 5ab6392ad..de3957681 100644 --- a/src/modules/paymentProcessor/abstracts/CrossChainBase_v1.sol +++ b/src/modules/paymentProcessor/abstracts/CrossChainBase_v1.sol @@ -23,7 +23,9 @@ import {ICrossChainBase_v1} from "../interfaces/ICrosschainBase_v1.sol"; * In case of any concerns or findings, please refer to our * Security Policy at security.inverter.network or email us * directly! - * @author Inverter Network + * @custom:version 1.0.0 + * @custom:standard-version 1.0.0 + * @author 33Audits */ abstract contract CrossChainBase_v1 is ICrossChainBase_v1, Module_v1 { // Storage Variables diff --git a/src/modules/paymentProcessor/abstracts/PP_Crosschain_v1.sol b/src/modules/paymentProcessor/abstracts/PP_Crosschain_v1.sol index 3a9f45206..03823091e 100644 --- a/src/modules/paymentProcessor/abstracts/PP_Crosschain_v1.sol +++ b/src/modules/paymentProcessor/abstracts/PP_Crosschain_v1.sol @@ -39,19 +39,32 @@ import {IPP_Crosschain_v1} from "../interfaces/IPP_Crosschain_v1.sol"; * at security.inverter.network or email us directly! * * @author Inverter Network + * @custom:version 1.0.0 + * @custom:standard-version 1.0.0 */ abstract contract PP_Crosschain_v1 is CrossChainBase_v1, IPP_Crosschain_v1 { - /// @inheritdoc ERC165Upgradeable - function supportsInterface(bytes4 interfaceId_) - public - view - virtual - override(CrossChainBase_v1) - returns (bool) - { - return interfaceId_ == type(IPP_Crosschain_v1).interfaceId - || super.supportsInterface(interfaceId_); - } + //-------------------------------------------------------------------------- + // Events + event PaymentProcessed(uint indexed paymentId, address indexed client); + event PaymentCancelled(uint indexed paymentId, address indexed client); + event UnclaimablePaymentClaimed( + address indexed client, + address indexed token, + address indexed receiver, + uint amount + ); + + //-------------------------------------------------------------------------- + // Constants + uint public constant VERSION = 1; + + //-------------------------------------------------------------------------- + // Storage Variables + bytes public executionData; + uint public _paymentId; + + /// @dev Gap for possible future upgrades. + uint[50] private __gap; //-------------------------------------------------------------------------- // Modifiers @@ -73,18 +86,19 @@ abstract contract PP_Crosschain_v1 is CrossChainBase_v1, IPP_Crosschain_v1 { } //-------------------------------------------------------------------------- - // Storage + // External/Public Functions - bytes public executionData; - - /// @dev Gap for possible future upgrades. - uint[50] private __gap; - - //@dev paymentId - uint public _paymentId; - - //-------------------------------------------------------------------------- - // Virtual Functions + /// @inheritdoc ERC165Upgradeable + function supportsInterface(bytes4 interfaceId_) + public + view + virtual + override(CrossChainBase_v1) + returns (bool) + { + return interfaceId_ == type(IPP_Crosschain_v1).interfaceId + || super.supportsInterface(interfaceId_); + } /// @notice Process payments for a given payment client /// @param client The payment client to process payments for @@ -134,6 +148,9 @@ abstract contract PP_Crosschain_v1 is CrossChainBase_v1, IPP_Crosschain_v1 { && _validPaymentToken(order.paymentToken); } + //-------------------------------------------------------------------------- + // Internal Functions + /// @dev Validate address input. /// @param addr Address to validate. /// @return True if address is valid. @@ -162,8 +179,6 @@ abstract contract PP_Crosschain_v1 is CrossChainBase_v1, IPP_Crosschain_v1 { pure returns (bool) { - // _start + _cliff should be less or equal to _end - // this already implies that _start is not greater than _end return _start + _cliff <= _end; } @@ -171,9 +186,6 @@ abstract contract PP_Crosschain_v1 is CrossChainBase_v1, IPP_Crosschain_v1 { /// @param _token Address of the token to validate. /// @return True if address is valid. function _validPaymentToken(address _token) internal returns (bool) { - // Only a basic sanity check that the address supports the balanceOf() function. The corresponding - // module should ensure it's sending an ERC20. - (bool success, bytes memory data) = _token.call( abi.encodeWithSelector( IERC20(_token).balanceOf.selector, address(this) diff --git a/test/modules/paymentProcessor/abstracts/PP_Connext_Crosschain_v1_Test.t.sol b/test/modules/paymentProcessor/abstracts/PP_Connext_Crosschain_v1_Test.t.sol index af7b8c010..df0acb6aa 100644 --- a/test/modules/paymentProcessor/abstracts/PP_Connext_Crosschain_v1_Test.t.sol +++ b/test/modules/paymentProcessor/abstracts/PP_Connext_Crosschain_v1_Test.t.sol @@ -2,68 +2,72 @@ pragma solidity ^0.8.20; +//-------------------------------------------------------------------------- +// Imports + // External Dependencies import {Test} from "forge-std/Test.sol"; import {Clones} from "@oz/proxy/Clones.sol"; +import {IERC20Errors} from "@oz/interfaces/draft-IERC6093.sol"; +import {IWETH} from "src/modules/paymentProcessor/interfaces/IWETH.sol"; +import "forge-std/console2.sol"; // Internal Dependencies import {PP_Connext_Crosschain_v1} from "src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol"; - import {CrossChainBase_v1} from "src/modules/paymentProcessor/abstracts/CrossChainBase_v1.sol"; -import {CrossChainBase_v1_Exposed} from - "test/modules/paymentProcessor/abstracts/CrossChainBase_v1_Exposed.sol"; -import {IERC20PaymentClientBase_v1} from - "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; import {ICrossChainBase_v1} from "src/modules/paymentProcessor/interfaces/ICrosschainBase_v1.sol"; import {IPaymentProcessor_v1} from "src/modules/paymentProcessor/IPaymentProcessor_v1.sol"; +import {IPP_Crosschain_v1} from + "src/modules/paymentProcessor/interfaces/IPP_Crosschain_v1.sol"; +import {IModule_v1, IOrchestrator_v1} from "src/modules/base/IModule_v1.sol"; +import {IERC20PaymentClientBase_v1} from + "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; + +// Tests and Mocks +import {CrossChainBase_v1_Exposed} from + "test/modules/paymentProcessor/abstracts/CrossChainBase_v1_Exposed.sol"; import {PP_Connext_Crosschain_v1_Exposed} from "test/modules/paymentProcessor/abstracts/PP_Connext_Crosschain_v1_Exposed.sol"; -// Tests and Mocks import {Mock_EverclearPayment} from "test/modules/paymentProcessor/abstracts/mocks/Mock_EverclearPayment.sol"; import { IERC20PaymentClientBase_v1, ERC20PaymentClientBaseV1Mock } from "test/utils/mocks/modules/paymentClient/ERC20PaymentClientBaseV1Mock.sol"; -import { - ModuleTest, - IModule_v1, - IOrchestrator_v1 -} from "test/modules/ModuleTest.sol"; +import {ModuleTest} from "test/modules/ModuleTest.sol"; import {OZErrors} from "test/utils/errors/OZErrors.sol"; -import {IERC20Errors} from "@oz/interfaces/draft-IERC6093.sol"; -import {IWETH} from "src/modules/paymentProcessor/interfaces/IWETH.sol"; -import "forge-std/console2.sol"; -import {IPP_Crosschain_v1} from - "src/modules/paymentProcessor/interfaces/IPP_Crosschain_v1.sol"; - -import {IModule_v1, IOrchestrator_v1} from "src/modules/base/IModule_v1.sol"; contract PP_Connext_Crosschain_v1_Test is ModuleTest { + //-------------------------------------------------------------------------- + // Constants + uint constant MINTED_SUPPLY = 1000 ether; + + //-------------------------------------------------------------------------- + // Test Storage PP_Connext_Crosschain_v1_Exposed public paymentProcessor; Mock_EverclearPayment public everclearPaymentMock; ERC20PaymentClientBaseV1Mock paymentClient; IPP_Crosschain_v1 public crossChainBase; - IWETH public weth; - uint public chainId; - // Add these as contract state variables + // Bridge-related storage address public mockConnextBridge; address public mockEverClearSpoke; address public mockWeth; + // Execution data storage uint maxFee = 0; uint ttl = 1; bytes executionData; bytes invalidExecutionData; - uint constant MINTED_SUPPLY = 1000 ether; + //-------------------------------------------------------------------------- + // Setup Function function setUp() public { // Set chain ID for test environment @@ -104,6 +108,9 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { _setupInitialBalances(); } + //-------------------------------------------------------------------------- + // Initialization Tests + /* Test initialization */ function testInit() public override(ModuleTest) { @@ -138,6 +145,9 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { paymentProcessor.init(_orchestrator, _METADATA, abi.encode(1)); } + //-------------------------------------------------------------------------- + // Payment Processing Tests + /* Test single payment processing └── Given single valid payment order └── When processing cross-chain payments @@ -267,6 +277,9 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { ); } + //-------------------------------------------------------------------------- + // Error Case Tests + /* Test invalid execution data └── When processing with invalid Connext parameters └── Then it should revert @@ -628,7 +641,8 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { ); } - // Helper functions + //-------------------------------------------------------------------------- + // Helper Functions function _setupSinglePayment(address _recipient, uint _amount) internal From 18aa1f4c0fb2500a51648bb0b5f0a26c347cc319 Mon Sep 17 00:00:00 2001 From: Leandro Faria Date: Tue, 17 Dec 2024 12:01:33 -0600 Subject: [PATCH 69/93] chore:remove redundant files --- .../abstracts/FM_Template_v1.sol | 196 +++++++++++++ .../abstracts/IFM_Template_v1.sol | 72 +++++ .../abstracts/IPP_Template_v1.sol | 103 +++++++ .../abstracts/PP_Template_v1.sol | 265 ++++++++++++++++++ .../paymentProcessor/PP_Connext_Bridge.t.sol | 73 ----- .../PP_Connext_Crosschain_v1_Test.t.sol | 6 +- .../abstracts/CrosschainBase_v1.t.sol | 3 +- .../abstracts/CrosschainBase_v1_Exposed.sol | 20 -- .../abstracts/FM_Template_v1.t.sol | 179 ------------ .../abstracts/FM_Template_v1_Exposed.sol | 16 -- .../abstracts/Interfaces/IEverClearSpoke.sol | 116 -------- .../abstracts/mocks/Mock_EverclearPayment.sol | 75 ----- .../interfaces/IEverClearSpoke.sol | 116 -------- .../external}/CrossChainBase_v1_Exposed.sol | 0 .../mocks/external/Mock_EverclearPayment.sol | 15 +- .../PP_Connext_Crosschain_v1_Exposed.sol | 0 16 files changed, 651 insertions(+), 604 deletions(-) create mode 100644 src/modules/paymentProcessor/abstracts/FM_Template_v1.sol create mode 100644 src/modules/paymentProcessor/abstracts/IFM_Template_v1.sol create mode 100644 src/modules/paymentProcessor/abstracts/IPP_Template_v1.sol create mode 100644 src/modules/paymentProcessor/abstracts/PP_Template_v1.sol delete mode 100644 test/modules/paymentProcessor/PP_Connext_Bridge.t.sol rename test/modules/paymentProcessor/{abstracts => }/PP_Connext_Crosschain_v1_Test.t.sol (99%) delete mode 100644 test/modules/paymentProcessor/abstracts/CrosschainBase_v1_Exposed.sol delete mode 100644 test/modules/paymentProcessor/abstracts/FM_Template_v1.t.sol delete mode 100644 test/modules/paymentProcessor/abstracts/FM_Template_v1_Exposed.sol delete mode 100644 test/modules/paymentProcessor/abstracts/Interfaces/IEverClearSpoke.sol delete mode 100644 test/modules/paymentProcessor/abstracts/mocks/Mock_EverclearPayment.sol delete mode 100644 test/modules/paymentProcessor/interfaces/IEverClearSpoke.sol rename test/{modules/paymentProcessor/abstracts => utils/mocks/external}/CrossChainBase_v1_Exposed.sol (100%) rename test/{modules/paymentProcessor/abstracts => utils/mocks/external}/PP_Connext_Crosschain_v1_Exposed.sol (100%) diff --git a/src/modules/paymentProcessor/abstracts/FM_Template_v1.sol b/src/modules/paymentProcessor/abstracts/FM_Template_v1.sol new file mode 100644 index 000000000..437e7c7d3 --- /dev/null +++ b/src/modules/paymentProcessor/abstracts/FM_Template_v1.sol @@ -0,0 +1,196 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity 0.8.23; + +// Internal +import {IOrchestrator_v1} from + "src/orchestrator/interfaces/IOrchestrator_v1.sol"; +import {ERC165Upgradeable, Module_v1} from "src/modules/base/Module_v1.sol"; +import {IFM_Template_v1} from "./IFM_Template_v1.sol"; +import {IFundingManager_v1} from "@fm/IFundingManager_v1.sol"; + +// External +import {IERC20} from "@oz/token/ERC20/IERC20.sol"; +import {SafeERC20} from "@oz/token/ERC20/utils/SafeERC20.sol"; + +/** + * @title Inverter Template Funding Manager + * + * @notice Basic template funding manager used as base for developing new + * funding managers. + * + * @dev This contract is used to showcase a basic setup for a funding + * manager. The contract showcases the following: + * - Inherit from the Module_v1 contract to enable interaction with + * the Inverter workflow. + * - Use of the IFundingManager_v1 interface to facilitate + * interaction as a Funding Manager. + * - Implement custom interface which has all the public facing + * functions, errors, events and structs. + * - Pre-defined layout for all contract functions, modifiers, state + * variables etc. + * - Use of the ERC165Upgradeable contract to check for interface + * support. + * + * @custom:security-contact security@inverter.network + * In case of any concerns or findings, please refer + * to our Security Policy at security.inverter.network + * or email us directly! + * + * @custom:version 1.0.0 + * + * @author Inverter Network + */ +contract FM_Template_v1 is IFM_Template_v1, Module_v1 { + // ========================================================================= + // Libraries + + using SafeERC20 for IERC20; + + // ========================================================================= + // ERC165 + + /// @inheritdoc ERC165Upgradeable + function supportsInterface(bytes4 interfaceId_) + public + view + virtual + override(Module_v1) + returns (bool) + { + return interfaceId_ == type(IFM_Template_v1).interfaceId + || interfaceId_ == type(IFundingManager_v1).interfaceId + || super.supportsInterface(interfaceId_); + } + + // ========================================================================= + // Constants + + // Add constants here + + // ========================================================================= + // State + + /// @notice Mapping of user addresses to their deposited token amounts. + mapping(address user => uint amount) internal _depositedAmounts; + + /// @notice The orchestrator token. + IERC20 internal _orchestratorToken; + + /// @notice Storage gap for future upgrades. + uint[50] private __gap; + + // ========================================================================= + // Modifiers + + // Add modifiers here + + // ========================================================================= + // Constructor & Init + + /// @inheritdoc Module_v1 + function init( + IOrchestrator_v1 orchestrator_, + Metadata memory metadata_, + bytes memory configData_ + ) external override(Module_v1) initializer { + __Module_init(orchestrator_, metadata_); + + // Decode module specific init data through use of configData bytes. + // This value is an example value used to showcase the setters/getters + // and internal functions/state formatting style. + (address orchestratorTokenAddress) = abi.decode(configData_, (address)); + + // Set init state. + _orchestratorToken = IERC20(orchestratorTokenAddress); + } + + // ========================================================================= + // Public - Getters + + /// @inheritdoc IFM_Template_v1 + function getDepositedAmount(address user_) + external + view + virtual + returns (uint amount_) + { + amount_ = _depositedAmounts[user_]; + } + + /// @inheritdoc IFundingManager_v1 + function token() + external + view + override + returns (IERC20 orchestratorToken_) + { + orchestratorToken_ = _orchestratorToken; + } + + // ========================================================================= + // Public - Mutating + + /// @inheritdoc IFM_Template_v1 + function deposit(uint amount_) external virtual { + // Validate parameters. + if (amount_ == 0) { + revert Module__FM_Template_InvalidAmount(); + } + + // Update state. + _depositedAmounts[_msgSender()] += amount_; + + // Transfer tokens. + _orchestratorToken.safeTransferFrom( + _msgSender(), address(this), amount_ + ); + + // Emit event. + emit Deposited(_msgSender(), amount_); + } + + /// @inheritdoc IFundingManager_v1 + /// @dev Only the payment client can call this function. + function transferOrchestratorToken(address to, uint amount) + external + virtual + override + onlyPaymentClient + { + // Validate parameters. + _validateOrchestratorTokenTransfer(to, amount); + + // Transfer tokens. + _orchestratorToken.safeTransfer(to, amount); + + // Emit event. + emit TransferOrchestratorToken(to, amount); + } + + // ========================================================================= + // Internal + + /// @notice Validates the transfer of orchestrator token. + /// @param to_ Address to transfer to. + /// @param amount_ Amount to transfer. + function _validateOrchestratorTokenTransfer(address to_, uint amount_) + internal + view + virtual + { + if (to_ == address(0)) { + revert Module__FM_Template__ReceiverNotValid(); + } + + if (amount_ == 0) { + revert Module__FM_Template_InvalidAmount(); + } + + if (_depositedAmounts[_msgSender()] < amount_) { + revert Module__FM_Template_InvalidAmount(); + } + } + + // ========================================================================= + // Overridden Internal Functions +} diff --git a/src/modules/paymentProcessor/abstracts/IFM_Template_v1.sol b/src/modules/paymentProcessor/abstracts/IFM_Template_v1.sol new file mode 100644 index 000000000..f644c53df --- /dev/null +++ b/src/modules/paymentProcessor/abstracts/IFM_Template_v1.sol @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity ^0.8.0; + +import {IFundingManager_v1} from "@fm/IFundingManager_v1.sol"; + +/** + * @title Inverter Template Funding Manager + * + * @notice Basic template for funding manager inteface used as base for developing new + * funding managers. + * + * @dev This contract is used to showcase a basic setup for a funding + * manager. The contract showcases the following: + * - Inherit from the Module_v1 contract to enable interaction with + * the Inverter workflow. + * - Use of the IFundingManager_v1 interface to facilitate + * interaction as a Funding Manager. + * - Implement custom interface which has all the public facing + * functions, errors, events and structs. + * - Pre-defined layout for all contract functions, modifiers, state + * variables etc. + * - Use of the ERC165Upgradeable contract to check for interface + * support. + * + * @custom:security-contact security@inverter.network + * In case of any concerns or findings, please refer + * to our Security Policy at security.inverter.network + * or email us directly! + * + * @custom:version 1.0.0 + * + * @author Inverter Network + */ +interface IFM_Template_v1 is IFundingManager_v1 { + // ========================================================================= + // Structs + + // ========================================================================= + // Events + + /// @notice Emit when the token amount has been deposited. + /// @param sender_ The address of the depositor. + /// @param amount_ The amount of tokens deposited. + event Deposited(address indexed sender_, uint amount_); + + // ========================================================================= + // Errors + + /// @notice Amount can not be zero. + error Module__FM_Template_InvalidAmount(); + + /// @notice Token receiver is not valid. + error Module__FM_Template__ReceiverNotValid(); + + // ========================================================================= + // Public - Getters + + /// @notice Returns the deposited balance of a specific address. + /// @param user_ The address of the user. + /// @return amount_ Deposited amount of the user. + function getDepositedAmount(address user_) + external + view + returns (uint amount_); + + // ========================================================================= + // Public - Mutating + + /// @notice Deposits tokens to the funding manager. + /// @param amount_ The amount of tokens to deposit. + function deposit(uint amount_) external; +} diff --git a/src/modules/paymentProcessor/abstracts/IPP_Template_v1.sol b/src/modules/paymentProcessor/abstracts/IPP_Template_v1.sol new file mode 100644 index 000000000..a8ca0d9ec --- /dev/null +++ b/src/modules/paymentProcessor/abstracts/IPP_Template_v1.sol @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity ^0.8.0; + +// Internal +import {IPaymentProcessor_v1} from "@pp/IPaymentProcessor_v1.sol"; + +/** + * @title Inverter Template Payment Processor + * + * @notice Basic template payment processor used as base for developing new + * payment processors. + * + * @dev This contract is used to showcase a basic setup for a payment + * processor. The contract showcases the following: + * - Inherit from the Module_v1 contract to enable interaction with + * the Inverter workflow. + * - Use of the IPaymentProcessor_v1 interface to facilitate + * interaction with a payment client. + * - Implement custom interface which has all the public facing + * functions, errors, events and structs. + * - Pre-defined layout for all contract functions, modifiers, state + * variables etc. + * - Use of the ERC165Upgradeable contract to check for interface + * support. + * + * @custom:security-contact security@inverter.network + * In case of any concerns or findings, please refer + * to our Security Policy at security.inverter.network + * or email us directly! + * + * @custom:version 1.0.0 + * + * @author Inverter Network + */ +interface IPP_Template_v1 is IPaymentProcessor_v1 { + //-------------------------------------------------------------------------- + // Structs + + /// @notice Struct to hold cross-chain message data + struct CrossChainMessage { + uint messageId; + address sourceChain; + address targetChain; + bytes payload; + bool executed; + } + + //-------------------------------------------------------------------------- + // Events + + /// @notice Emit when new payout amount has been set. + /// @param oldPayoutAmount_ Old payout amount. + /// @param newPayoutAmount_ Newly set payout amount. + event NewPayoutAmountMultiplierSet( + uint indexed oldPayoutAmount_, uint indexed newPayoutAmount_ + ); + + /// @notice Emitted when a cross-chain message is sent + /// @param messageId Unique identifier for the message + /// @param targetChain Address of the target chain + event CrossChainMessageSent( + uint indexed messageId, address indexed targetChain + ); + + /// @notice Emitted when a cross-chain message is received + /// @param messageId Unique identifier for the message + /// @param sourceChain Address of the source chain + event CrossChainMessageReceived( + uint indexed messageId, address indexed sourceChain + ); + + //-------------------------------------------------------------------------- + // Errors + + /// @notice Amount can not be zero. + error Module__PP_Template_InvalidAmount(); + + /// @notice Client is not valid. + error Module__PP_Template__ClientNotValid(); + + error Module__PP_CrossChain__NotValidClient(); + + error Module__PP_CrossChain__InvalidAmount(); + + /// @notice Message has already been executed + error Module__PP_CrossChain_MessageAlreadyExecuted(); + /// @notice Invalid chain ID provided + error Module__PP_CrossChain_InvalidChainId(); + /// @notice Message verification failed + error Module__PP_CrossChain_MessageVerificationFailed(); + + //-------------------------------------------------------------------------- + // Public (Getter) + + /// @notice Returns the payout amount for each payment order. + /// @return payoutAmountMultiplier_ The payout amount multiplier. + function getPayoutAmountMultiplier() + external + returns (uint payoutAmountMultiplier_); + + //-------------------------------------------------------------------------- + // Public (Mutating) +} diff --git a/src/modules/paymentProcessor/abstracts/PP_Template_v1.sol b/src/modules/paymentProcessor/abstracts/PP_Template_v1.sol new file mode 100644 index 000000000..e1be6e87b --- /dev/null +++ b/src/modules/paymentProcessor/abstracts/PP_Template_v1.sol @@ -0,0 +1,265 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity 0.8.23; + +// Internal +import {IOrchestrator_v1} from + "src/orchestrator/interfaces/IOrchestrator_v1.sol"; +import {IPaymentProcessor_v1} from "@pp/IPaymentProcessor_v1.sol"; +import {IERC20PaymentClientBase_v1} from + "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; +import {IPP_Template_v1} from "./IPP_Template_v1.sol"; +import {ERC165Upgradeable, Module_v1} from "src/modules/base/Module_v1.sol"; + +// External +import {IERC20} from "@oz/token/ERC20/IERC20.sol"; + +/** + * @title Inverter Template Payment Processor + * + * @notice Basic template payment processor used as base for developing new + * payment processors. + * + * @dev This contract is used to showcase a basic setup for a payment + * processor. The contract showcases the following: + * - Inherit from the Module_v1 contract to enable interaction with + * the Inverter workflow. + * - Use of the IPaymentProcessor_v1 interface to facilitate + * interaction with a payment client. + * - Implement custom interface which has all the public facing + * functions, errors, events and structs. + * - Pre-defined layout for all contract functions, modifiers, state + * variables etc. + * - Use of the ERC165Upgradeable contract to check for interface + * support. + * + * @custom:security-contact security@inverter.network + * In case of any concerns or findings, please refer + * to our Security Policy at security.inverter.network + * or email us directly! + * + * @custom:version 1.0.0 + * + * @author Inverter Network + */ +contract PP_Template_v1 is IPP_Template_v1, Module_v1 { + //-------------------------------------------------------------------------- + // Libraries + + // Add library usage here + + //-------------------------------------------------------------------------- + // ERC165 + + /// @inheritdoc ERC165Upgradeable + function supportsInterface(bytes4 interfaceId_) + public + view + virtual + override(Module_v1) + returns (bool) + { + return interfaceId_ == type(IPP_Template_v1).interfaceId + || interfaceId_ == type(IPaymentProcessor_v1).interfaceId + || super.supportsInterface(interfaceId_); + } + + //-------------------------------------------------------------------------- + // Constants + + // Add constants here + + //-------------------------------------------------------------------------- + // State + + /// @dev Payout amount multiplier. + uint internal _payoutAmountMultiplier; + + /// @dev Payment ID of the last processed payment order. + uint internal _paymentId; + + //-------------------------------------------------------------------------- + // Modifiers + + /// @dev Checks that the client is calling for itself. + modifier clientIsValid(address client_) { + // Modifier logic moved to internal function for contract size reduction. + _ensureValidClient(client_); + _; + } + + //-------------------------------------------------------------------------- + // Constructor & Init + + /// @inheritdoc Module_v1 + function init( + IOrchestrator_v1 orchestrator_, + Metadata memory metadata_, + bytes memory configData_ + ) external override(Module_v1) initializer { + __Module_init(orchestrator_, metadata_); + + // Decode module specific init data through use of configData bytes. + // This value is an example value used to showcase the setters/getters + // and internal functions/state formating style. + (uint payoutAmountMultiplier_) = abi.decode(configData_, (uint)); + + // Set init state. + _setPayoutAmountMultiplier(payoutAmountMultiplier_); + } + + //-------------------------------------------------------------------------- + // Public (Getters) + + /// @inheritdoc IPP_Template_v1 + function getPayoutAmountMultiplier() + external + view + returns (uint payoutAmount_) + { + return _payoutAmountMultiplier; + } + + //-------------------------------------------------------------------------- + // Public (Mutating) + + /// @inheritdoc IPaymentProcessor_v1 + function processPayments(IERC20PaymentClientBase_v1 client_) + external + clientIsValid(address(client_)) + { + // The IERC20PaymentClientBase_v1 client should be used to access + // created payment orders in the Logic Module (LM) implementing the + // interface. The interface should be referenced to see the different + // functionalities provided by the ERC20PaymentClientBase_v1. + + // Collect orders from the client + IERC20PaymentClientBase_v1.PaymentOrder[] memory orders; + (orders,,) = client_.collectPaymentOrders(); + + // Custom logic to proces the payment orders should be implemented + // below. This template implements a straight forward token transfer + // using the first order of the payment order array for simplicity. + + // Get payment order details + address recipient_ = orders[0].recipient; + address token_ = orders[0].paymentToken; + uint amount_ = orders[0].amount * _payoutAmountMultiplier; + _paymentId = _paymentId + 1; + + // Emit event of the IPaymentProcessor_v1. This is used by Inverter's + // Indexer. + emit PaymentOrderProcessed( + address(client_), recipient_, token_, amount_, 0, 0, 0 + ); + + // Transfer tokens from {IERC20PaymentClientBase_v1} to order + // recipients. + // Please note: When processing multiple payment orders and then + // letting the call revert as in this example might not be the best + // solution. Ways to handle this by implementing the `unclaimable` + // function can be found in the other Payment Processor (PP) + // implementations. + IERC20(token_).transferFrom(address(client_), recipient_, amount_); + + // Inform the client about the amount that was released, to keep + // the accounting correct. + client_.amountPaid(token_, amount_); + + // Emit event of the IPaymentProcessor_v1. This is used by Inverter's + // Indexer. + emit TokensReleased(recipient_, token_, amount_); + } + + /// @inheritdoc IPaymentProcessor_v1 + function cancelRunningPayments(IERC20PaymentClientBase_v1 client_) + external + view + clientIsValid(address(client_)) + { + // This function is used to implement custom logic to cancel running + // payments. If the nature of processing payments is one of direct + // processing then this function can be left empty, return nothing. + return; + } + + /// @inheritdoc IPaymentProcessor_v1 + function unclaimable( + address, /*client_*/ + address, /*token_*/ + address /*paymentReceiver_*/ + ) external pure returns (uint amount_) { + // This function is used to check if there are unclaimable tokens for a + // specific client, token and payment receiver. As this template only + // executes one payment order at a time, this function is not utilzed + // and can return 0. + return 0; + } + + /// @inheritdoc IPaymentProcessor_v1 + function claimPreviouslyUnclaimable( + address, /*client_*/ + address, /*token_*/ + address /*receiver_*/ + ) external virtual { + return; + } + + /// @inheritdoc IPaymentProcessor_v1 + function validPaymentOrder( + IERC20PaymentClientBase_v1.PaymentOrder memory order_ + ) external view returns (bool) { + // This function is used to validate the payment order created on the + // client side (LM_PC) with the input required by the Payment Processor + // (PP). The function should return true if the payment order is valid + // and false if it is not. + + // For this template, only the receiver is validated. + return _validPaymentReceiver(order_.recipient); + } + + //-------------------------------------------------------------------------- + // Internal + + /// @dev Internal function to set the new payout amount multiplier. + /// @param newPayoutAmountMultiplier_ Payout amount multiplier to be set in + // the state. Cannot be zero. + function _setPayoutAmountMultiplier(uint newPayoutAmountMultiplier_) + internal + { + if (newPayoutAmountMultiplier_ == 0) { + revert Module__PP_Template_InvalidAmount(); + } + emit NewPayoutAmountMultiplierSet( + _payoutAmountMultiplier, newPayoutAmountMultiplier_ + ); + _payoutAmountMultiplier = newPayoutAmountMultiplier_; + } + + /// @dev Validate whether the address is a valid payment receiver. + /// @param receiver_ Address to validate. + /// @return validPaymentReceiver_ True if address is valid. + function _validPaymentReceiver(address receiver_) + internal + view + virtual + returns (bool) + { + return !( + receiver_ == address(0) || receiver_ == _msgSender() + || receiver_ == address(this) + || receiver_ == address(orchestrator()) + || receiver_ == address(orchestrator().fundingManager().token()) + ); + } + + /// @dev Internal function to check whether the client is valid. + /// @param client_ Address to validate. + function _ensureValidClient(address client_) internal view { + if (_msgSender() != client_) { + revert Module__PP_Template__ClientNotValid(); + } + } + + //-------------------------------------------------------------------------- + // Internal override +} diff --git a/test/modules/paymentProcessor/PP_Connext_Bridge.t.sol b/test/modules/paymentProcessor/PP_Connext_Bridge.t.sol deleted file mode 100644 index fe6d4615a..000000000 --- a/test/modules/paymentProcessor/PP_Connext_Bridge.t.sol +++ /dev/null @@ -1,73 +0,0 @@ -//SPDX-License-Identifier: LGPL-3.0-only -pragma solidity ^0.8.0; - -//Internal Dependencies -import { - ModuleTest, - IModule_v1, - IOrchestrator_v1 -} from "test/modules/ModuleTest.sol"; -import {OZErrors} from "test/utils/errors/OZErrors.sol"; -import {ICrossChainBase_v1} from - "src/modules/paymentProcessor/interfaces/ICrosschainBase_v1.sol"; -import {CrossChainBase_v1} from - "src/modules/paymentProcessor/abstracts/CrossChainBase_v1.sol"; -//External Dependencies -import {Clones} from "@oz/proxy/Clones.sol"; - -//Tests and Mocks -// import cr -import { - IERC20PaymentClientBase_v1, - ERC20PaymentClientBaseV1Mock, - ERC20Mock -} from "test/utils/mocks/modules/paymentClient/ERC20PaymentClientBaseV1Mock.sol"; - -import {ICrossChainBase_v1} from - "src/modules/paymentProcessor/interfaces/ICrosschainBase_v1.sol"; -/** - * @title Inverter Template Payment Processor - * - * @notice Basic template payment processor used to showcase the unit testing setup - * - * @dev Not all functions are tested in this template. Placeholders of the functions that are not tested are added - * into the contract. This test showcases the following: - * - Inherit from the ModuleTest contract to enable interaction with the Inverter workflow. - * - Showcases the setup of the workflow, uses in test unit tests. - * - Pre-defined layout for all setup and functions to be tested. - * - Shows the use of Gherkin for documenting the testing. VS Code extension used for formatting is recommended. - * - Shows the use of the modifierInPlace pattern to test the modifier placement. - * - * @custom:security-contact security@inverter.network - * In case of any concerns or findings, please refer to our Security Policy - * at security.inverter.network or email us directly! - * - * @author Inverter Network - */ - -contract PP_Connext_Bridge_Test is ModuleTest { - //-------------------------------------------------------------------------- - //Constants - - //-------------------------------------------------------------------------- - //State - - //-------------------------------------------------------------------------- - //Mocks - - //-------------------------------------------------------------------------- - //Setup - function setUp() public {} - - //-------------------------------------------------------------------------- - //Test: Initialization - - //Test if the orchestrator is correctly set - function testInit() public override(ModuleTest) {} - - //Test the interface support - function testSupportsInterface() public {} - - //Test the reinit function - function testReinitFails() public override(ModuleTest) {} -} diff --git a/test/modules/paymentProcessor/abstracts/PP_Connext_Crosschain_v1_Test.t.sol b/test/modules/paymentProcessor/PP_Connext_Crosschain_v1_Test.t.sol similarity index 99% rename from test/modules/paymentProcessor/abstracts/PP_Connext_Crosschain_v1_Test.t.sol rename to test/modules/paymentProcessor/PP_Connext_Crosschain_v1_Test.t.sol index df0acb6aa..3dcd1f75d 100644 --- a/test/modules/paymentProcessor/abstracts/PP_Connext_Crosschain_v1_Test.t.sol +++ b/test/modules/paymentProcessor/PP_Connext_Crosschain_v1_Test.t.sol @@ -29,11 +29,11 @@ import {IERC20PaymentClientBase_v1} from // Tests and Mocks import {CrossChainBase_v1_Exposed} from - "test/modules/paymentProcessor/abstracts/CrossChainBase_v1_Exposed.sol"; + "test/utils/mocks/external/CrossChainBase_v1_Exposed.sol"; import {PP_Connext_Crosschain_v1_Exposed} from - "test/modules/paymentProcessor/abstracts/PP_Connext_Crosschain_v1_Exposed.sol"; + "test/utils/mocks/external/PP_Connext_Crosschain_v1_Exposed.sol"; import {Mock_EverclearPayment} from - "test/modules/paymentProcessor/abstracts/mocks/Mock_EverclearPayment.sol"; + "test/utils/mocks/external/Mock_EverclearPayment.sol"; import { IERC20PaymentClientBase_v1, ERC20PaymentClientBaseV1Mock diff --git a/test/modules/paymentProcessor/abstracts/CrosschainBase_v1.t.sol b/test/modules/paymentProcessor/abstracts/CrosschainBase_v1.t.sol index 1e641fd5f..4a0c0c958 100644 --- a/test/modules/paymentProcessor/abstracts/CrosschainBase_v1.t.sol +++ b/test/modules/paymentProcessor/abstracts/CrosschainBase_v1.t.sol @@ -17,7 +17,8 @@ import { ERC20PaymentClientBaseV1Mock, ERC20Mock } from "test/utils/mocks/modules/paymentClient/ERC20PaymentClientBaseV1Mock.sol"; -import {CrossChainBase_v1_Exposed} from "./CrossChainBase_v1_Exposed.sol"; +import {CrossChainBase_v1_Exposed} from + "test/utils/mocks/external/CrossChainBase_v1_Exposed.sol"; import {IPaymentProcessor_v1} from "src/orchestrator/interfaces/IOrchestrator_v1.sol"; diff --git a/test/modules/paymentProcessor/abstracts/CrosschainBase_v1_Exposed.sol b/test/modules/paymentProcessor/abstracts/CrosschainBase_v1_Exposed.sol deleted file mode 100644 index 8d78f964f..000000000 --- a/test/modules/paymentProcessor/abstracts/CrosschainBase_v1_Exposed.sol +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only - -// Internal Dependencies -//import {PP_CrossChain_v1} from "src/templates/modules/PP_Template_v1.sol"; -import {CrossChainBase_v1} from - "src/modules/paymentProcessor/abstracts/CrossChainBase_v1.sol"; -import {IERC20PaymentClientBase_v1} from - "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; - -contract CrossChainBase_v1_Exposed is CrossChainBase_v1 { - constructor(uint chainId_) CrossChainBase_v1() {} - ///// @inheritdoc CrossChainBase_v1 - - function exposed_executeBridgeTransfer( - IERC20PaymentClientBase_v1.PaymentOrder memory order, - bytes memory executionData - ) external payable returns (bytes memory) { - return _executeBridgeTransfer(order, executionData); - } -} diff --git a/test/modules/paymentProcessor/abstracts/FM_Template_v1.t.sol b/test/modules/paymentProcessor/abstracts/FM_Template_v1.t.sol deleted file mode 100644 index 3806448c3..000000000 --- a/test/modules/paymentProcessor/abstracts/FM_Template_v1.t.sol +++ /dev/null @@ -1,179 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.0; - -// Internal -import { - ModuleTest, - IModule_v1, - IOrchestrator_v1 -} from "test/modules/ModuleTest.sol"; -import {OZErrors} from "test/utils/errors/OZErrors.sol"; -import {IFundingManager_v1} from "@fm/IFundingManager_v1.sol"; - -// External -import {Clones} from "@oz/proxy/Clones.sol"; - -// Tests and Mocks -import {FM_Template_v1_Exposed} from - "src/templates/tests/unit/FM_Template_v1_Exposed.sol"; -import {ERC20Mock} from "test/utils/mocks/ERC20Mock.sol"; -import { - IERC20PaymentClientBase_v1, - ERC20PaymentClientBaseV1Mock -} from "test/utils/mocks/modules/paymentClient/ERC20PaymentClientBaseV1Mock.sol"; - -// System under Test (SuT) -import {IFM_Template_v1} from "src/templates/modules/IFM_Template_v1.sol"; - -/** - * @title Inverter Template Funding Manager Tests - * - * @notice Basic template funding manager used to showcase the unit testing - * setup - * - * @dev Not all functions are tested in this template. Placeholders of the - * functions that are not tested are added into the contract. This test - * showcases the following: - * - Inherit from the ModuleTest contract to enable interaction with - * the Inverter workflow. - * - Showcases the setup of the workflow, uses in test unit tests. - * - Pre-defined layout for all setup and functions to be tested. - * - Shows the use of Gherkin for documenting the testing. VS Code - * extension used for formatting is recommended. - * - Shows the use of the modifierInPlace pattern to test the modifier - * placement. - * - * @author Inverter Network - */ -contract FM_Template_v1_Test is ModuleTest { - // ========================================================================= - // Constants - - // ========================================================================= - // State - FM_Template_v1_Exposed fundingManager; - - // Mocks - ERC20Mock orchestratorToken; - ERC20PaymentClientBaseV1Mock paymentClient; - - // ========================================================================= - // Setup - function setUp() public { - // This function is used to setup the unit test - // Deploy the SuT - address impl = address(new FM_Template_v1_Exposed()); - fundingManager = FM_Template_v1_Exposed(Clones.clone(impl)); - - orchestratorToken = new ERC20Mock("Orchestrator Token", "OTK"); - - // Setup the module to test - _setUpOrchestrator(fundingManager); - - // General setup for other contracts in the workflow - _authorizer.setIsAuthorized(address(this), true); - - // Initialize the funding manager with metadata and config data - fundingManager.init( - _orchestrator, _METADATA, abi.encode(address(orchestratorToken)) - ); - - // Setup other modules needed in the unit tests. - // In this case a payment client is needed to test the FM_Template_v1. - paymentClient = new ERC20PaymentClientBaseV1Mock(); - _addLogicModuleToOrchestrator(address(paymentClient)); - } - - // ========================================================================= - // Test: Initialization - - // Test if the orchestrator is correctly set up after initialization - function testInit() public override(ModuleTest) { - assertEq(address(fundingManager.orchestrator()), address(_orchestrator)); - } - - // Test the reinit function - function testReinitFails() public override(ModuleTest) { - vm.expectRevert(OZErrors.Initializable__InvalidInitialization); - fundingManager.init( - _orchestrator, _METADATA, abi.encode(address(orchestratorToken)) - ); - } - - // Test the interface support - function testSupportsInterface() public { - assertTrue( - fundingManager.supportsInterface( - type(IFundingManager_v1).interfaceId - ) - ); - assertTrue( - fundingManager.supportsInterface(type(IFM_Template_v1).interfaceId) - ); - } - - // ========================================================================= - // Test: External (public & external) functions - - // Test external deposit function - - // Test external transferOrchestratorToken function - - // Test external getDepositedAmount function - - // Test external token function - - // ========================================================================= - // Test: Internal (tested through exposed_ functions) - - /* test internal _validateOrchestratorTokenTransfer() - ├── Given zero address receipent - │ └── When the function is called with zero address receipent - │ └── Then the function should revert with Module__FM_Template__ReceiverNotValid error - ├── Given zero amount - │ └── When the function is called with zero amount - │ └── Then the function should revert with Module__FM_Template__AmountNotValid error - └── Given valid receipent and amount but not enough balance - └── When the function is called with valid receipent and amount but not enough balance - └── Then the function should revert with Module__FM_Template_InvalidAmount error - */ - function testInternalValidateOrchestratorTokenTransfer_FailsZeroAddressReceipent( - ) public { - vm.expectRevert( - IFM_Template_v1.Module__FM_Template__ReceiverNotValid.selector - ); - fundingManager.exposed_validateOrchestratorTokenTransfer( - address(0), 1e18 - ); - } - - function testInternalValidateOrchestratorTokenTransfer_FailsZeroAmount() - public - { - vm.expectRevert( - IFM_Template_v1.Module__FM_Template_InvalidAmount.selector - ); - fundingManager.exposed_validateOrchestratorTokenTransfer( - address(this), 0 - ); - } - - function testInternalValidateOrchestratorTokenTransfer_FailsNotEnoughBalance( - ) public { - orchestratorToken.mint(address(1), 1 ether); - - vm.startPrank(address(1)); - { - orchestratorToken.approve(address(fundingManager), 1 ether); - fundingManager.deposit(1 ether); - } - vm.stopPrank(); - - vm.expectRevert( - IFM_Template_v1.Module__FM_Template_InvalidAmount.selector - ); - fundingManager.exposed_validateOrchestratorTokenTransfer( - address(1), 10 ether - ); - } -} diff --git a/test/modules/paymentProcessor/abstracts/FM_Template_v1_Exposed.sol b/test/modules/paymentProcessor/abstracts/FM_Template_v1_Exposed.sol deleted file mode 100644 index 19f588316..000000000 --- a/test/modules/paymentProcessor/abstracts/FM_Template_v1_Exposed.sol +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity ^0.8.0; - -import {FM_Template_v1} from "src/templates/modules/FM_Template_v1.sol"; - -// Access Mock of the FM_Template_v1 contract for Testing. -contract FM_Template_v1_Exposed is FM_Template_v1 { - // Use the `exposed_` prefix for functions to expose internal functions for testing purposes only. - - function exposed_validateOrchestratorTokenTransfer( - address to_, - uint amount_ - ) external view { - return _validateOrchestratorTokenTransfer(to_, amount_); - } -} diff --git a/test/modules/paymentProcessor/abstracts/Interfaces/IEverClearSpoke.sol b/test/modules/paymentProcessor/abstracts/Interfaces/IEverClearSpoke.sol deleted file mode 100644 index 716829a8c..000000000 --- a/test/modules/paymentProcessor/abstracts/Interfaces/IEverClearSpoke.sol +++ /dev/null @@ -1,116 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -/** - * @title IEverclearSpoke - * @notice Interface for the Everclear spoke contract - */ -interface IEverclearSpoke { - /*/////////////////////////////////////////////////////////////// - STRUCTS - //////////////////////////////////////////////////////////////*/ - struct Intent { - bytes32 initiator; - bytes32 receiver; - bytes32 inputAsset; - bytes32 outputAsset; - uint amount; - uint24 maxFee; - uint32 origin; - uint32[] destinations; - uint nonce; - uint48 timestamp; - uint48 ttl; - bytes data; - } - - struct FillMessage { - bytes32 intentId; - bytes32 initiator; - bytes32 solver; - uint48 executionTimestamp; - uint24 fee; - } - - struct Permit2Params { - uint nonce; - uint deadline; - bytes signature; - } - - struct SpokeInitializationParams { - address gateway; - address messageReceiver; - address lighthouse; - address watchtower; - address callExecutor; - uint32 hubDomain; - address owner; - } - - /*/////////////////////////////////////////////////////////////// - FUNCTIONS - //////////////////////////////////////////////////////////////*/ - function initialize(SpokeInitializationParams calldata init) external; - function pause() external; - function unpause() external; - function updateSecurityModule(address newSecurityModule) external; - function newIntent( - uint32[] memory destinations, - address to, - address inputAsset, - address outputAsset, - uint amount, - uint24 maxFee, - uint48 ttl, - bytes calldata data - ) external returns (bytes32 intentId, Intent memory intent); - function newIntent( - uint32[] memory destinations, - address to, - address inputAsset, - address outputAsset, - uint amount, - uint24 maxFee, - uint48 ttl, - bytes calldata data, - Permit2Params calldata permit2Params - ) external returns (bytes32 intentId, Intent memory intent); - function fillIntent(Intent calldata intent, uint24 fee) - external - returns (FillMessage memory fillMessage); - function fillIntentForSolver( - address solver, - Intent calldata intent, - uint nonce, - uint24 fee, - bytes calldata signature - ) external returns (FillMessage memory fillMessage); - function processIntentQueue(Intent[] calldata intents) external payable; - function processFillQueue(uint32 amount) external payable; - function processIntentQueueViaRelayer( - uint32 domain, - Intent[] calldata intents, - address relayer, - uint ttl, - uint nonce, - uint bufferBPS, - bytes calldata signature - ) external; - function processFillQueueViaRelayer( - uint32 domain, - uint32 amount, - address relayer, - uint ttl, - uint nonce, - uint bufferBPS, - bytes calldata signature - ) external; - function deposit(address asset, uint amount) external; - function withdraw(address asset, uint amount) external; - function updateGateway(address newGateway) external; - function updateMessageReceiver(address newMessageReceiver) external; - function authorizeGasReceiver(address receiver, bool authorized) external; - function updateMessageGasLimit(uint newGasLimit) external; - function executeIntentCalldata(Intent calldata intent) external; -} diff --git a/test/modules/paymentProcessor/abstracts/mocks/Mock_EverclearPayment.sol b/test/modules/paymentProcessor/abstracts/mocks/Mock_EverclearPayment.sol deleted file mode 100644 index 1d83eca7e..000000000 --- a/test/modules/paymentProcessor/abstracts/mocks/Mock_EverclearPayment.sol +++ /dev/null @@ -1,75 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.23; - -import {console} from "forge-std/console.sol"; - -contract Mock_EverclearPayment { - event IntentAdded(bytes32 intentId, uint queuePosition, Intent intent); - - uint public nonce; - uint32 public DOMAIN; - mapping(bytes32 => IntentStatus) public status; - - enum IntentStatus { - NONE, - ADDED, - SETTLED, - SETTLED_AND_MANUALLY_EXECUTED - } - - struct Intent { - address initiator; - address receiver; - address inputAsset; - address outputAsset; - uint amount; - uint24 maxFee; - uint32 origin; - uint32[] destinations; - uint nonce; - uint48 timestamp; - uint48 ttl; - bytes data; - } - - function newIntent( - uint32[] memory _destinations, - address _to, - address _inputAsset, - address _outputAsset, - uint _amount, - uint24 _maxFee, - uint48 _ttl, - bytes calldata _data - ) external returns (bytes32 _intentId) { - // Increment nonce for each new intent - nonce++; - if (_maxFee == 333) { - return bytes32(0); - } - //if data is the word "fail" intentional return bytes32(0) - Intent memory _intent = Intent({ - initiator: msg.sender, - receiver: _to, - inputAsset: _inputAsset, - outputAsset: _outputAsset, - amount: _amount, - maxFee: _maxFee, - origin: DOMAIN, - destinations: _destinations, - nonce: nonce, - timestamp: uint48(block.timestamp), - ttl: _ttl, - data: _data - }); - - // Generate a unique intent ID - _intentId = keccak256(abi.encode(_intent)); - - // // Set intent status to ADDED and emit the event - status[_intentId] = IntentStatus.ADDED; - emit IntentAdded(_intentId, nonce, _intent); - - return (_intentId); - } -} diff --git a/test/modules/paymentProcessor/interfaces/IEverClearSpoke.sol b/test/modules/paymentProcessor/interfaces/IEverClearSpoke.sol deleted file mode 100644 index 716829a8c..000000000 --- a/test/modules/paymentProcessor/interfaces/IEverClearSpoke.sol +++ /dev/null @@ -1,116 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -/** - * @title IEverclearSpoke - * @notice Interface for the Everclear spoke contract - */ -interface IEverclearSpoke { - /*/////////////////////////////////////////////////////////////// - STRUCTS - //////////////////////////////////////////////////////////////*/ - struct Intent { - bytes32 initiator; - bytes32 receiver; - bytes32 inputAsset; - bytes32 outputAsset; - uint amount; - uint24 maxFee; - uint32 origin; - uint32[] destinations; - uint nonce; - uint48 timestamp; - uint48 ttl; - bytes data; - } - - struct FillMessage { - bytes32 intentId; - bytes32 initiator; - bytes32 solver; - uint48 executionTimestamp; - uint24 fee; - } - - struct Permit2Params { - uint nonce; - uint deadline; - bytes signature; - } - - struct SpokeInitializationParams { - address gateway; - address messageReceiver; - address lighthouse; - address watchtower; - address callExecutor; - uint32 hubDomain; - address owner; - } - - /*/////////////////////////////////////////////////////////////// - FUNCTIONS - //////////////////////////////////////////////////////////////*/ - function initialize(SpokeInitializationParams calldata init) external; - function pause() external; - function unpause() external; - function updateSecurityModule(address newSecurityModule) external; - function newIntent( - uint32[] memory destinations, - address to, - address inputAsset, - address outputAsset, - uint amount, - uint24 maxFee, - uint48 ttl, - bytes calldata data - ) external returns (bytes32 intentId, Intent memory intent); - function newIntent( - uint32[] memory destinations, - address to, - address inputAsset, - address outputAsset, - uint amount, - uint24 maxFee, - uint48 ttl, - bytes calldata data, - Permit2Params calldata permit2Params - ) external returns (bytes32 intentId, Intent memory intent); - function fillIntent(Intent calldata intent, uint24 fee) - external - returns (FillMessage memory fillMessage); - function fillIntentForSolver( - address solver, - Intent calldata intent, - uint nonce, - uint24 fee, - bytes calldata signature - ) external returns (FillMessage memory fillMessage); - function processIntentQueue(Intent[] calldata intents) external payable; - function processFillQueue(uint32 amount) external payable; - function processIntentQueueViaRelayer( - uint32 domain, - Intent[] calldata intents, - address relayer, - uint ttl, - uint nonce, - uint bufferBPS, - bytes calldata signature - ) external; - function processFillQueueViaRelayer( - uint32 domain, - uint32 amount, - address relayer, - uint ttl, - uint nonce, - uint bufferBPS, - bytes calldata signature - ) external; - function deposit(address asset, uint amount) external; - function withdraw(address asset, uint amount) external; - function updateGateway(address newGateway) external; - function updateMessageReceiver(address newMessageReceiver) external; - function authorizeGasReceiver(address receiver, bool authorized) external; - function updateMessageGasLimit(uint newGasLimit) external; - function executeIntentCalldata(Intent calldata intent) external; -} diff --git a/test/modules/paymentProcessor/abstracts/CrossChainBase_v1_Exposed.sol b/test/utils/mocks/external/CrossChainBase_v1_Exposed.sol similarity index 100% rename from test/modules/paymentProcessor/abstracts/CrossChainBase_v1_Exposed.sol rename to test/utils/mocks/external/CrossChainBase_v1_Exposed.sol diff --git a/test/utils/mocks/external/Mock_EverclearPayment.sol b/test/utils/mocks/external/Mock_EverclearPayment.sol index fbf4aa0ff..1d83eca7e 100644 --- a/test/utils/mocks/external/Mock_EverclearPayment.sol +++ b/test/utils/mocks/external/Mock_EverclearPayment.sol @@ -1,6 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.23; +import {console} from "forge-std/console.sol"; + contract Mock_EverclearPayment { event IntentAdded(bytes32 intentId, uint queuePosition, Intent intent); @@ -39,11 +41,14 @@ contract Mock_EverclearPayment { uint24 _maxFee, uint48 _ttl, bytes calldata _data - ) external returns (bytes32 _intentId, Intent memory _intent) { + ) external returns (bytes32 _intentId) { // Increment nonce for each new intent nonce++; - - _intent = Intent({ + if (_maxFee == 333) { + return bytes32(0); + } + //if data is the word "fail" intentional return bytes32(0) + Intent memory _intent = Intent({ initiator: msg.sender, receiver: _to, inputAsset: _inputAsset, @@ -61,10 +66,10 @@ contract Mock_EverclearPayment { // Generate a unique intent ID _intentId = keccak256(abi.encode(_intent)); - // Set intent status to ADDED and emit the event + // // Set intent status to ADDED and emit the event status[_intentId] = IntentStatus.ADDED; emit IntentAdded(_intentId, nonce, _intent); - return (_intentId, _intent); + return (_intentId); } } diff --git a/test/modules/paymentProcessor/abstracts/PP_Connext_Crosschain_v1_Exposed.sol b/test/utils/mocks/external/PP_Connext_Crosschain_v1_Exposed.sol similarity index 100% rename from test/modules/paymentProcessor/abstracts/PP_Connext_Crosschain_v1_Exposed.sol rename to test/utils/mocks/external/PP_Connext_Crosschain_v1_Exposed.sol From 102cbd411df16389617e87acb35b4f74b0b99bf4 Mon Sep 17 00:00:00 2001 From: Leandro Faria Date: Tue, 17 Dec 2024 12:08:12 -0600 Subject: [PATCH 70/93] fix:update crosschain base format --- .../abstracts/CrossChainBase_v1_Test.t.sol | 180 ++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 test/modules/paymentProcessor/abstracts/CrossChainBase_v1_Test.t.sol diff --git a/test/modules/paymentProcessor/abstracts/CrossChainBase_v1_Test.t.sol b/test/modules/paymentProcessor/abstracts/CrossChainBase_v1_Test.t.sol new file mode 100644 index 000000000..afd40599d --- /dev/null +++ b/test/modules/paymentProcessor/abstracts/CrossChainBase_v1_Test.t.sol @@ -0,0 +1,180 @@ +//SPDX-License-Identifier: LGPL-3.0-only +pragma solidity ^0.8.0; + +//-------------------------------------------------------------------------- +// Imports + +// External Dependencies +import {Clones} from "@oz/proxy/Clones.sol"; + +// Test Framework +import { + ModuleTest, + IModule_v1, + IOrchestrator_v1 +} from "test/modules/ModuleTest.sol"; +import {OZErrors} from "test/utils/errors/OZErrors.sol"; + +// Interfaces +import {ICrossChainBase_v1} from + "src/modules/paymentProcessor/interfaces/ICrosschainBase_v1.sol"; +import {IPaymentProcessor_v1} from + "src/orchestrator/interfaces/IOrchestrator_v1.sol"; + +// Implementation +import {CrossChainBase_v1} from + "src/modules/paymentProcessor/abstracts/CrossChainBase_v1.sol"; + +// Mocks +import { + IERC20PaymentClientBase_v1, + ERC20PaymentClientBaseV1Mock, + ERC20Mock +} from "test/utils/mocks/modules/paymentClient/ERC20PaymentClientBaseV1Mock.sol"; +import {CrossChainBase_v1_Exposed} from + "test/utils/mocks/external/CrossChainBase_v1_Exposed.sol"; + +contract CrossChainBase_v1_Test is ModuleTest { + //-------------------------------------------------------------------------- + // Events + event PaymentOrderProcessed( + address indexed client, + address indexed recipient, + address indexed token, + uint amount, + uint start, + uint cliff, + uint end + ); + + //-------------------------------------------------------------------------- + // Test Storage + ERC20PaymentClientBaseV1Mock paymentClient; + CrossChainBase_v1_Exposed public crossChainBase; + + //-------------------------------------------------------------------------- + // Setup Function + + function setUp() public { + //This function is used to setup the unit test + //Deploy the SuT + address impl = address(new CrossChainBase_v1_Exposed(block.chainid)); + crossChainBase = CrossChainBase_v1_Exposed(Clones.clone(impl)); + + //Setup the module to test + _setUpOrchestrator(crossChainBase); + + //General setup for other contracts in the workflow + _authorizer.setIsAuthorized(address(this), true); + + //Initiate the PP with the medata and config data + crossChainBase.init(_orchestrator, _METADATA, abi.encode(1)); + + //Setup other modules needed in the unit tests. + //In this case a payment client is needed to test the PP_Template_v1. + impl = address(new ERC20PaymentClientBaseV1Mock()); + paymentClient = ERC20PaymentClientBaseV1Mock(Clones.clone(impl)); + //Adding the payment client is done through a timelock mechanism + _orchestrator.initiateAddModuleWithTimelock(address(paymentClient)); + vm.warp(block.timestamp + _orchestrator.MODULE_UPDATE_TIMELOCK()); + _orchestrator.executeAddModule(address(paymentClient)); + //Init payment client + paymentClient.init(_orchestrator, _METADATA, bytes("")); + paymentClient.setIsAuthorized(address(crossChainBase), true); + paymentClient.setToken(_token); + } + + //-------------------------------------------------------------------------- + // Test Cases + + //Test: Initialization + /* + └── Given the contract is not initialized + └── When initializing the contract + └── Then it should set the correct orchestrator address */ + function testInit() public override(ModuleTest) { + assertEq(address(crossChainBase.orchestrator()), address(_orchestrator)); + } + + //Test: Interface Support + /* + └── Given the contract is initialized + └─��� When checking for ICrossChainBase_v1 interface support + └── Then it should return true + └── When checking for an unknown interface + └── Then it should return false */ + function testSupportsInterface() public { + // Test for ICrossChainBase_v1 interface support + bytes4 interfaceId = type(ICrossChainBase_v1).interfaceId; + assertTrue(crossChainBase.supportsInterface(interfaceId)); + } + + function testSupportsInterface_revertsGivenUnknownInterface() public { + bytes4 randomInterfaceId = bytes4(keccak256("random()")); + assertFalse(crossChainBase.supportsInterface(randomInterfaceId)); + } + + /* + └── Given the contract is already initialized + └── When trying to reinitialize + └── Then it should revert with Initializable__InvalidInitialization */ + function testReinitFails() public override(ModuleTest) { + vm.expectRevert(OZErrors.Initializable__InvalidInitialization); + crossChainBase.init(_orchestrator, _METADATA, abi.encode(1)); + } + + //Test: Bridge Transfer + /* + └── Given an empty payment order is created + └── When executeBridgeTransfer is called + └── Then it should return empty bytes */ + function testExecuteBridgeTransfer_worksGivenEmptyPaymentOrder() public { + address[] memory setupRecipients = new address[](1); + setupRecipients[0] = address(1); + uint[] memory setupAmounts = new uint[](1); + setupAmounts[0] = 100 ether; + + IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = + _createPaymentOrders(1, setupRecipients, setupAmounts); + paymentClient.addPaymentOrders(orders); + + bytes memory executionData = abi.encode(0, 0); //maxFee and ttl setup + + bytes memory result = crossChainBase.exposed_executeBridgeTransfer( + orders[0], executionData + ); + assertEq(result, bytes("")); + } + + //-------------------------------------------------------------------------- + // Helper Functions + + function _createPaymentOrders( + uint orderCount, + address[] memory recipients, + uint[] memory amounts + ) + internal + view + returns (IERC20PaymentClientBase_v1.PaymentOrder[] memory) + { + // Sanity checks for array lengths + require( + recipients.length == orderCount && amounts.length == orderCount, + "Array lengths must match orderCount" + ); + IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = + new IERC20PaymentClientBase_v1.PaymentOrder[](orderCount); + for (uint i = 0; i < orderCount; i++) { + orders[i] = IERC20PaymentClientBase_v1.PaymentOrder({ + recipient: recipients[i], + paymentToken: address(0xabcd), + amount: amounts[i], + start: block.timestamp, + cliff: 0, + end: block.timestamp + 1 days + }); + } + return orders; + } +} From b93500c18f17c21f78db997563c6021d9576e13b Mon Sep 17 00:00:00 2001 From: Leandro Faria Date: Tue, 17 Dec 2024 12:17:32 -0600 Subject: [PATCH 71/93] fix:delete console import --- test/utils/mocks/external/Mock_EverclearPayment.sol | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/utils/mocks/external/Mock_EverclearPayment.sol b/test/utils/mocks/external/Mock_EverclearPayment.sol index 1d83eca7e..8745c3bd8 100644 --- a/test/utils/mocks/external/Mock_EverclearPayment.sol +++ b/test/utils/mocks/external/Mock_EverclearPayment.sol @@ -1,8 +1,6 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.23; -import {console} from "forge-std/console.sol"; - contract Mock_EverclearPayment { event IntentAdded(bytes32 intentId, uint queuePosition, Intent intent); From 9bdbf8a4d9f72ad1617e6ab8a6d08b09ae4f633c Mon Sep 17 00:00:00 2001 From: Leandro Faria Date: Tue, 17 Dec 2024 12:22:34 -0600 Subject: [PATCH 72/93] fix:move files to proper directories --- .../paymentProcessor/PP_Connext_Crosschain_v1_Test.t.sol | 4 ++-- .../paymentProcessor/abstracts/CrossChainBase_v1_Test.t.sol | 2 +- .../paymentProcessor/abstracts/CrosschainBase_v1.t.sol | 2 +- .../paymentProcessor}/CrossChainBase_v1_Exposed.sol | 0 .../paymentProcessor}/PP_Connext_Crosschain_v1_Exposed.sol | 0 5 files changed, 4 insertions(+), 4 deletions(-) rename test/utils/mocks/{external => modules/paymentProcessor}/CrossChainBase_v1_Exposed.sol (100%) rename test/utils/mocks/{external => modules/paymentProcessor}/PP_Connext_Crosschain_v1_Exposed.sol (100%) diff --git a/test/modules/paymentProcessor/PP_Connext_Crosschain_v1_Test.t.sol b/test/modules/paymentProcessor/PP_Connext_Crosschain_v1_Test.t.sol index 3dcd1f75d..fd075ff62 100644 --- a/test/modules/paymentProcessor/PP_Connext_Crosschain_v1_Test.t.sol +++ b/test/modules/paymentProcessor/PP_Connext_Crosschain_v1_Test.t.sol @@ -29,9 +29,9 @@ import {IERC20PaymentClientBase_v1} from // Tests and Mocks import {CrossChainBase_v1_Exposed} from - "test/utils/mocks/external/CrossChainBase_v1_Exposed.sol"; + "test/utils/mocks/modules/paymentProcessor/CrossChainBase_v1_Exposed.sol"; import {PP_Connext_Crosschain_v1_Exposed} from - "test/utils/mocks/external/PP_Connext_Crosschain_v1_Exposed.sol"; + "test/utils/mocks/modules/paymentProcessor/PP_Connext_Crosschain_v1_Exposed.sol"; import {Mock_EverclearPayment} from "test/utils/mocks/external/Mock_EverclearPayment.sol"; import { diff --git a/test/modules/paymentProcessor/abstracts/CrossChainBase_v1_Test.t.sol b/test/modules/paymentProcessor/abstracts/CrossChainBase_v1_Test.t.sol index afd40599d..906eb74ac 100644 --- a/test/modules/paymentProcessor/abstracts/CrossChainBase_v1_Test.t.sol +++ b/test/modules/paymentProcessor/abstracts/CrossChainBase_v1_Test.t.sol @@ -32,7 +32,7 @@ import { ERC20Mock } from "test/utils/mocks/modules/paymentClient/ERC20PaymentClientBaseV1Mock.sol"; import {CrossChainBase_v1_Exposed} from - "test/utils/mocks/external/CrossChainBase_v1_Exposed.sol"; + "test/utils/mocks/modules/paymentProcessor/CrossChainBase_v1_Exposed.sol"; contract CrossChainBase_v1_Test is ModuleTest { //-------------------------------------------------------------------------- diff --git a/test/modules/paymentProcessor/abstracts/CrosschainBase_v1.t.sol b/test/modules/paymentProcessor/abstracts/CrosschainBase_v1.t.sol index 4a0c0c958..2355c450f 100644 --- a/test/modules/paymentProcessor/abstracts/CrosschainBase_v1.t.sol +++ b/test/modules/paymentProcessor/abstracts/CrosschainBase_v1.t.sol @@ -18,7 +18,7 @@ import { ERC20Mock } from "test/utils/mocks/modules/paymentClient/ERC20PaymentClientBaseV1Mock.sol"; import {CrossChainBase_v1_Exposed} from - "test/utils/mocks/external/CrossChainBase_v1_Exposed.sol"; + "test/utils/mocks/modules/paymentProcessor/CrossChainBase_v1_Exposed.sol"; import {IPaymentProcessor_v1} from "src/orchestrator/interfaces/IOrchestrator_v1.sol"; diff --git a/test/utils/mocks/external/CrossChainBase_v1_Exposed.sol b/test/utils/mocks/modules/paymentProcessor/CrossChainBase_v1_Exposed.sol similarity index 100% rename from test/utils/mocks/external/CrossChainBase_v1_Exposed.sol rename to test/utils/mocks/modules/paymentProcessor/CrossChainBase_v1_Exposed.sol diff --git a/test/utils/mocks/external/PP_Connext_Crosschain_v1_Exposed.sol b/test/utils/mocks/modules/paymentProcessor/PP_Connext_Crosschain_v1_Exposed.sol similarity index 100% rename from test/utils/mocks/external/PP_Connext_Crosschain_v1_Exposed.sol rename to test/utils/mocks/modules/paymentProcessor/PP_Connext_Crosschain_v1_Exposed.sol From d6bdba64951a51eed9a2f66c241e3893ece74aac Mon Sep 17 00:00:00 2001 From: Leandro Faria Date: Mon, 6 Jan 2025 12:56:15 -0600 Subject: [PATCH 73/93] fix:remove impl for functions --- .../paymentProcessor/abstracts/CrossChainBase_v1.sol | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/modules/paymentProcessor/abstracts/CrossChainBase_v1.sol b/src/modules/paymentProcessor/abstracts/CrossChainBase_v1.sol index de3957681..24e658080 100644 --- a/src/modules/paymentProcessor/abstracts/CrossChainBase_v1.sol +++ b/src/modules/paymentProcessor/abstracts/CrossChainBase_v1.sol @@ -36,8 +36,7 @@ abstract contract CrossChainBase_v1 is ICrossChainBase_v1, Module_v1 { /// @param client The payment client to process payments for function processPayments(IERC20PaymentClientBase_v1 client) external - virtual - {} + virtual; // Public Functions /// @inheritdoc ERC165Upgradeable @@ -59,8 +58,7 @@ abstract contract CrossChainBase_v1 is ICrossChainBase_v1, Module_v1 { public view virtual - returns (bytes memory) - {} + returns (bytes memory); // Internal Functions /// @notice Execute the cross-chain bridge transfer @@ -70,5 +68,5 @@ abstract contract CrossChainBase_v1 is ICrossChainBase_v1, Module_v1 { function _executeBridgeTransfer( IERC20PaymentClientBase_v1.PaymentOrder memory order, bytes memory executionData - ) internal virtual returns (bytes memory) {} + ) internal virtual returns (bytes memory) } From bd555168c6827eb7a45ab665a5b231f7254bc03b Mon Sep 17 00:00:00 2001 From: Leandro Faria Date: Mon, 6 Jan 2025 16:36:39 -0600 Subject: [PATCH 74/93] fix: fix retry failed payments --- .../PP_Connext_Crosschain_v1.sol | 106 +++++++++--------- .../abstracts/CrossChainBase_v1.sol | 2 +- .../interfaces/IPP_Crosschain_v1.sol | 6 +- .../PP_Connext_Crosschain_v1_Test.t.sol | 35 +++--- .../abstracts/CrossChainBase_v1_Test.t.sol | 2 +- .../abstracts/CrosschainBase_v1.t.sol | 2 +- .../CrossChainBase_v1_Exposed.sol | 22 +++- .../PP_Connext_Crosschain_v1_Exposed.sol | 2 +- 8 files changed, 99 insertions(+), 78 deletions(-) diff --git a/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol b/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol index 82e8ad56c..71cba3c5b 100644 --- a/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol +++ b/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol @@ -50,7 +50,7 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { mapping( address paymentClient => mapping( - address recipient => mapping(bytes32 intentId => uint amount) + address recipient => mapping(bytes intentId => uint amount) ) ) public failedTransfers; @@ -88,25 +88,38 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { ) external { IERC20PaymentClientBase_v1.PaymentOrder[] memory orders; (orders,,) = client.collectPaymentOrders(); - + address clientAddress = address(client); for (uint i = 0; i < orders.length; i++) { - bytes memory bridgeData = _executeBridgeTransfer( - orders[i], executionData, address(client) - ); - - _bridgeData[i] = bridgeData; - emit PaymentOrderProcessed( - address(client), - orders[i].recipient, - orders[i].paymentToken, - orders[i].amount, - orders[i].start, - orders[i].cliff, - orders[i].end - ); - _paymentId++; - intentId[address(client)][orders[i].recipient] = bytes32(bridgeData); - client.amountPaid(orders[i].paymentToken, orders[i].amount); + bytes memory bridgeData = + _executeBridgeTransfer(orders[i], executionData); + + if (bytes32(bridgeData) == bytes32(0)) { + // Handle failed transfer + failedTransfers[clientAddress][orders[i].recipient][executionData] + = orders[i].amount; + emit TransferFailed( + clientAddress, + orders[i].recipient, + executionData, + orders[i].amount + ); + } else { + // Handle successful transfer + _bridgeData[i] = bridgeData; + emit PaymentOrderProcessed( + address(client), + orders[i].recipient, + orders[i].paymentToken, + orders[i].amount, + orders[i].start, + orders[i].cliff, + orders[i].end + ); + _paymentId++; + intentId[address(client)][orders[i].recipient] = + bytes32(bridgeData); + client.amountPaid(orders[i].paymentToken, orders[i].amount); + } } } @@ -114,50 +127,49 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { * @notice Cancels a pending transfer and returns funds to the client * @param client The payment client address * @param recipient The recipient address - * @param pendingIntentId The intentId to cancel + * @param executionData The execution data to cancel * @param order The payment order details */ function cancelTransfer( address client, address recipient, - bytes32 pendingIntentId, + bytes memory executionData, IERC20PaymentClientBase_v1.PaymentOrder memory order ) external { - _validateTransferRequest(client, recipient, pendingIntentId); - _cleanupFailedTransfer(client, recipient, pendingIntentId); + _validateTransferRequest(client, recipient, executionData); + _cleanupFailedTransfer(client, recipient, executionData); if (!IERC20(order.paymentToken).transfer(recipient, order.amount)) { revert FailedTransfer(); } - emit TransferCancelled(client, recipient, pendingIntentId, order.amount); + emit TransferCancelled(client, recipient, executionData, order.amount); } /** * @notice Retries a previously failed transfer * @param client The payment client address * @param recipient The recipient address - * @param pendingIntentId The failed intent ID * @param order The payment order details * @param executionData New execution data for retry */ function retryFailedTransfer( address client, address recipient, - bytes32 pendingIntentId, - IERC20PaymentClientBase_v1.PaymentOrder memory order, - bytes memory executionData + bytes memory executionData, + bytes memory newExecutionData, + IERC20PaymentClientBase_v1.PaymentOrder memory order ) external { - _validateTransferRequest(client, recipient, pendingIntentId); + _validateTransferRequest(client, recipient, executionData); - bytes32 newIntentId = _createCrossChainIntent(order, executionData); + bytes32 newIntentId = _createCrossChainIntent(order, newExecutionData); if (newIntentId == bytes32(0)) { revert Module__PP_Crosschain__MessageDeliveryFailed( 8453, 8453, executionData ); } - _cleanupFailedTransfer(client, recipient, pendingIntentId); + _cleanupFailedTransfer(client, recipient, executionData); intentId[client][recipient] = newIntentId; } @@ -181,24 +193,13 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { * @dev Execute the cross-chain bridge transfer * @param order The payment order containing transfer details * @param executionData Additional execution parameters - * @param client The client address * @return bridgeData Data returned by the bridge implementation */ function _executeBridgeTransfer( IERC20PaymentClientBase_v1.PaymentOrder memory order, - bytes memory executionData, - address client - ) internal returns (bytes memory) { + bytes memory executionData + ) internal override returns (bytes memory) { bytes32 _intentId = _createCrossChainIntent(order, executionData); - - if (_intentId == bytes32(0)) { - failedTransfers[client][order.recipient][_intentId] = order.amount; - intentId[client][order.recipient] = _intentId; - emit TransferFailed( - client, order.recipient, _intentId, order.amount - ); - } - return abi.encode(_intentId); } @@ -252,23 +253,18 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { * @dev Validates a transfer request * @param client The payment client address * @param recipient The recipient address - * @param pendingIntentId The intent ID to validate - * @return The amount of the failed transfer + * @param executionData The execution data */ function _validateTransferRequest( address client, address recipient, - bytes32 pendingIntentId + bytes memory executionData ) internal view returns (uint) { if (msg.sender != client) { revert Module__InvalidAddress(); } - if (intentId[client][recipient] != pendingIntentId) { - revert Module__PP_Crosschain__InvalidIntentId(); - } - - uint failedAmount = failedTransfers[client][recipient][pendingIntentId]; + uint failedAmount = failedTransfers[client][recipient][executionData]; if (failedAmount == 0) { revert Module__CrossChainBase__InvalidAmount(); } @@ -280,15 +276,15 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { * @dev Cleans up storage after handling a failed transfer * @param client The payment client address * @param recipient The recipient address - * @param pendingIntentId The intent ID to clean up + * @param executionData The execution data */ function _cleanupFailedTransfer( address client, address recipient, - bytes32 pendingIntentId + bytes memory executionData ) internal { delete intentId[client][recipient]; - delete failedTransfers[client][recipient][pendingIntentId]; + delete failedTransfers[client][recipient][executionData]; } /** diff --git a/src/modules/paymentProcessor/abstracts/CrossChainBase_v1.sol b/src/modules/paymentProcessor/abstracts/CrossChainBase_v1.sol index 24e658080..b2929d62f 100644 --- a/src/modules/paymentProcessor/abstracts/CrossChainBase_v1.sol +++ b/src/modules/paymentProcessor/abstracts/CrossChainBase_v1.sol @@ -68,5 +68,5 @@ abstract contract CrossChainBase_v1 is ICrossChainBase_v1, Module_v1 { function _executeBridgeTransfer( IERC20PaymentClientBase_v1.PaymentOrder memory order, bytes memory executionData - ) internal virtual returns (bytes memory) + ) internal virtual returns (bytes memory); } diff --git a/src/modules/paymentProcessor/interfaces/IPP_Crosschain_v1.sol b/src/modules/paymentProcessor/interfaces/IPP_Crosschain_v1.sol index 3d389cd4c..fc3668ec8 100644 --- a/src/modules/paymentProcessor/interfaces/IPP_Crosschain_v1.sol +++ b/src/modules/paymentProcessor/interfaces/IPP_Crosschain_v1.sol @@ -13,12 +13,12 @@ interface IPP_Crosschain_v1 is IPaymentProcessor_v1 { /// @notice Emitted when a cross-chain transfer fails to complete /// @param client The address initiating the transfer /// @param recipient The intended recipient of the transfer - /// @param intentId The unique identifier for this transfer attempt + /// @param executionData The unique identifier for this transfer attempt /// @param amount The amount that failed to transfer event TransferFailed( address indexed client, address indexed recipient, - bytes32 indexed intentId, + bytes indexed executionData, uint amount ); @@ -42,7 +42,7 @@ interface IPP_Crosschain_v1 is IPaymentProcessor_v1 { event TransferCancelled( address indexed client, address indexed recipient, - bytes32 indexed intentId, + bytes indexed intentId, uint amount ); diff --git a/test/modules/paymentProcessor/PP_Connext_Crosschain_v1_Test.t.sol b/test/modules/paymentProcessor/PP_Connext_Crosschain_v1_Test.t.sol index fd075ff62..b6cce6ad5 100644 --- a/test/modules/paymentProcessor/PP_Connext_Crosschain_v1_Test.t.sol +++ b/test/modules/paymentProcessor/PP_Connext_Crosschain_v1_Test.t.sol @@ -508,16 +508,21 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = _setupSinglePayment(recipient, amount); + // Store the initial execution data that will fail + bytes memory failingExecutionData = abi.encode(333, 1); // maxFee of 333 will cause failure + // First attempt with high maxFee to force failure paymentProcessor.processPayments( IERC20PaymentClientBase_v1(address(paymentClient)), - abi.encode(333, 1) // maxFee of 333 will cause failure + failingExecutionData ); - // Verify failed transfer was recorded + // Verify failed transfer was recorded with the failing execution data assertEq( paymentProcessor.failedTransfers( - address(paymentClient), recipient, bytes32(0) + address(paymentClient), + recipient, + failingExecutionData // Use the same execution data that was used in processPayments ), orders[0].amount ); @@ -527,16 +532,18 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { paymentProcessor.retryFailedTransfer( address(paymentClient), recipient, - bytes32(0), - orders[0], - abi.encode(0, 1) // proper maxFee and ttl + failingExecutionData, // Old execution data that failed + executionData, // New execution data for retry + orders[0] ); // Verify: // 1. Failed transfer record was cleared assertEq( paymentProcessor.failedTransfers( - address(paymentClient), recipient, bytes32(0) + address(paymentClient), + recipient, + failingExecutionData // Check using the original failing execution data ), 0 ); @@ -564,30 +571,28 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = _setupSinglePayment(recipient, amount); paymentClient.addPaymentOrder(orders[0]); + bytes memory executionData = abi.encode(333, 1); //call processPayments with maxFee = 333 paymentProcessor.processPayments( - IERC20PaymentClientBase_v1(address(paymentClient)), - abi.encode(333, 1) + IERC20PaymentClientBase_v1(address(paymentClient)), executionData ); - console2.log("Fucker who seneeed", address(this)); // see if failed failedTransfers updates assertEq( paymentProcessor.failedTransfers( - address(paymentClient), recipient, bytes32(0) + address(paymentClient), recipient, executionData ), orders[0].amount ); uint failedIntentId = paymentProcessor.failedTransfers( - address(paymentClient), recipient, bytes32(0) + address(paymentClient), recipient, executionData ); - console2.log("AMOUNT", failedIntentId); assertEq(failedIntentId, orders[0].amount); // Cancel as recipient vm.prank(address(paymentClient)); paymentProcessor.cancelTransfer( - address(paymentClient), recipient, bytes32(0), orders[0] + address(paymentClient), recipient, executionData, orders[0] ); // Verify intentId was cleared @@ -637,7 +642,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { vm.expectRevert(IModule_v1.Module__InvalidAddress.selector); paymentProcessor.cancelTransfer( - address(paymentClient), testRecipient, pendingIntentId, order + address(paymentClient), testRecipient, executionData, order ); } diff --git a/test/modules/paymentProcessor/abstracts/CrossChainBase_v1_Test.t.sol b/test/modules/paymentProcessor/abstracts/CrossChainBase_v1_Test.t.sol index 906eb74ac..ea967e3ed 100644 --- a/test/modules/paymentProcessor/abstracts/CrossChainBase_v1_Test.t.sol +++ b/test/modules/paymentProcessor/abstracts/CrossChainBase_v1_Test.t.sol @@ -58,7 +58,7 @@ contract CrossChainBase_v1_Test is ModuleTest { function setUp() public { //This function is used to setup the unit test //Deploy the SuT - address impl = address(new CrossChainBase_v1_Exposed(block.chainid)); + address impl = address(new CrossChainBase_v1_Exposed()); crossChainBase = CrossChainBase_v1_Exposed(Clones.clone(impl)); //Setup the module to test diff --git a/test/modules/paymentProcessor/abstracts/CrosschainBase_v1.t.sol b/test/modules/paymentProcessor/abstracts/CrosschainBase_v1.t.sol index 2355c450f..a4cd37699 100644 --- a/test/modules/paymentProcessor/abstracts/CrosschainBase_v1.t.sol +++ b/test/modules/paymentProcessor/abstracts/CrosschainBase_v1.t.sol @@ -41,7 +41,7 @@ contract CrossChainBase_v1_Test is ModuleTest { function setUp() public { //This function is used to setup the unit test //Deploy the SuT - address impl = address(new CrossChainBase_v1_Exposed(block.chainid)); + address impl = address(new CrossChainBase_v1_Exposed()); crossChainBase = CrossChainBase_v1_Exposed(Clones.clone(impl)); //Setup the module to test diff --git a/test/utils/mocks/modules/paymentProcessor/CrossChainBase_v1_Exposed.sol b/test/utils/mocks/modules/paymentProcessor/CrossChainBase_v1_Exposed.sol index 8d78f964f..48322ad2c 100644 --- a/test/utils/mocks/modules/paymentProcessor/CrossChainBase_v1_Exposed.sol +++ b/test/utils/mocks/modules/paymentProcessor/CrossChainBase_v1_Exposed.sol @@ -8,9 +8,29 @@ import {IERC20PaymentClientBase_v1} from "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; contract CrossChainBase_v1_Exposed is CrossChainBase_v1 { - constructor(uint chainId_) CrossChainBase_v1() {} ///// @inheritdoc CrossChainBase_v1 + function _executeBridgeTransfer( + IERC20PaymentClientBase_v1.PaymentOrder memory order, + bytes memory executionData + ) internal pure override returns (bytes memory) { + return ""; + } + + function getBridgeData(uint paymentId) + public + pure + override + returns (bytes memory) + { + return ""; + } + + function processPayments(IERC20PaymentClientBase_v1 client) + external + override + {} + function exposed_executeBridgeTransfer( IERC20PaymentClientBase_v1.PaymentOrder memory order, bytes memory executionData diff --git a/test/utils/mocks/modules/paymentProcessor/PP_Connext_Crosschain_v1_Exposed.sol b/test/utils/mocks/modules/paymentProcessor/PP_Connext_Crosschain_v1_Exposed.sol index f18befe37..1ee23cc0b 100644 --- a/test/utils/mocks/modules/paymentProcessor/PP_Connext_Crosschain_v1_Exposed.sol +++ b/test/utils/mocks/modules/paymentProcessor/PP_Connext_Crosschain_v1_Exposed.sol @@ -26,7 +26,7 @@ contract PP_Connext_Crosschain_v1_Exposed is PP_Connext_Crosschain_v1 { function exposed_setFailedTransfer( address client, address recipient, - bytes32 intentId, + bytes memory intentId, uint amount ) external { failedTransfers[client][recipient][intentId] = amount; From ed26634be7191698135dd4c7cabbe95a30ef73f9 Mon Sep 17 00:00:00 2001 From: Leandro Faria Date: Mon, 6 Jan 2025 16:40:42 -0600 Subject: [PATCH 75/93] chore:add comments for clarity --- .../paymentProcessor/PP_Connext_Crosschain_v1.sol | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol b/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol index 71cba3c5b..1ecfe7952 100644 --- a/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol +++ b/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol @@ -151,7 +151,8 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { * @param client The payment client address * @param recipient The recipient address * @param order The payment order details - * @param executionData New execution data for retry + * @param executionData Old execution data that failed + * @param newExecutionData New execution data for retry */ function retryFailedTransfer( address client, @@ -260,14 +261,19 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { address recipient, bytes memory executionData ) internal view returns (uint) { + //msg.sender should be the client if (msg.sender != client) { revert Module__InvalidAddress(); } - + //failedAmount should be stored if the transfer has failed uint failedAmount = failedTransfers[client][recipient][executionData]; if (failedAmount == 0) { revert Module__CrossChainBase__InvalidAmount(); } + //intentId should be 0 if the transfer has not been processed yet + if (intentId[client][recipient] != bytes32(0)) { + revert Module__PP_Crosschain__InvalidIntentId(); + } return failedAmount; } From 81e4275f8f25f7962b4825e2e4819a6724cb04de Mon Sep 17 00:00:00 2001 From: Leandro Faria Date: Mon, 6 Jan 2025 20:23:59 -0600 Subject: [PATCH 76/93] fix:remove files --- .../abstracts/FM_Template_v1.sol | 196 ------------- .../abstracts/IFM_Template_v1.sol | 72 ----- .../abstracts/IPP_Template_v1.sol | 103 ------- .../abstracts/PP_Template_v1.sol | 265 ------------------ 4 files changed, 636 deletions(-) delete mode 100644 src/modules/paymentProcessor/abstracts/FM_Template_v1.sol delete mode 100644 src/modules/paymentProcessor/abstracts/IFM_Template_v1.sol delete mode 100644 src/modules/paymentProcessor/abstracts/IPP_Template_v1.sol delete mode 100644 src/modules/paymentProcessor/abstracts/PP_Template_v1.sol diff --git a/src/modules/paymentProcessor/abstracts/FM_Template_v1.sol b/src/modules/paymentProcessor/abstracts/FM_Template_v1.sol deleted file mode 100644 index 437e7c7d3..000000000 --- a/src/modules/paymentProcessor/abstracts/FM_Template_v1.sol +++ /dev/null @@ -1,196 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity 0.8.23; - -// Internal -import {IOrchestrator_v1} from - "src/orchestrator/interfaces/IOrchestrator_v1.sol"; -import {ERC165Upgradeable, Module_v1} from "src/modules/base/Module_v1.sol"; -import {IFM_Template_v1} from "./IFM_Template_v1.sol"; -import {IFundingManager_v1} from "@fm/IFundingManager_v1.sol"; - -// External -import {IERC20} from "@oz/token/ERC20/IERC20.sol"; -import {SafeERC20} from "@oz/token/ERC20/utils/SafeERC20.sol"; - -/** - * @title Inverter Template Funding Manager - * - * @notice Basic template funding manager used as base for developing new - * funding managers. - * - * @dev This contract is used to showcase a basic setup for a funding - * manager. The contract showcases the following: - * - Inherit from the Module_v1 contract to enable interaction with - * the Inverter workflow. - * - Use of the IFundingManager_v1 interface to facilitate - * interaction as a Funding Manager. - * - Implement custom interface which has all the public facing - * functions, errors, events and structs. - * - Pre-defined layout for all contract functions, modifiers, state - * variables etc. - * - Use of the ERC165Upgradeable contract to check for interface - * support. - * - * @custom:security-contact security@inverter.network - * In case of any concerns or findings, please refer - * to our Security Policy at security.inverter.network - * or email us directly! - * - * @custom:version 1.0.0 - * - * @author Inverter Network - */ -contract FM_Template_v1 is IFM_Template_v1, Module_v1 { - // ========================================================================= - // Libraries - - using SafeERC20 for IERC20; - - // ========================================================================= - // ERC165 - - /// @inheritdoc ERC165Upgradeable - function supportsInterface(bytes4 interfaceId_) - public - view - virtual - override(Module_v1) - returns (bool) - { - return interfaceId_ == type(IFM_Template_v1).interfaceId - || interfaceId_ == type(IFundingManager_v1).interfaceId - || super.supportsInterface(interfaceId_); - } - - // ========================================================================= - // Constants - - // Add constants here - - // ========================================================================= - // State - - /// @notice Mapping of user addresses to their deposited token amounts. - mapping(address user => uint amount) internal _depositedAmounts; - - /// @notice The orchestrator token. - IERC20 internal _orchestratorToken; - - /// @notice Storage gap for future upgrades. - uint[50] private __gap; - - // ========================================================================= - // Modifiers - - // Add modifiers here - - // ========================================================================= - // Constructor & Init - - /// @inheritdoc Module_v1 - function init( - IOrchestrator_v1 orchestrator_, - Metadata memory metadata_, - bytes memory configData_ - ) external override(Module_v1) initializer { - __Module_init(orchestrator_, metadata_); - - // Decode module specific init data through use of configData bytes. - // This value is an example value used to showcase the setters/getters - // and internal functions/state formatting style. - (address orchestratorTokenAddress) = abi.decode(configData_, (address)); - - // Set init state. - _orchestratorToken = IERC20(orchestratorTokenAddress); - } - - // ========================================================================= - // Public - Getters - - /// @inheritdoc IFM_Template_v1 - function getDepositedAmount(address user_) - external - view - virtual - returns (uint amount_) - { - amount_ = _depositedAmounts[user_]; - } - - /// @inheritdoc IFundingManager_v1 - function token() - external - view - override - returns (IERC20 orchestratorToken_) - { - orchestratorToken_ = _orchestratorToken; - } - - // ========================================================================= - // Public - Mutating - - /// @inheritdoc IFM_Template_v1 - function deposit(uint amount_) external virtual { - // Validate parameters. - if (amount_ == 0) { - revert Module__FM_Template_InvalidAmount(); - } - - // Update state. - _depositedAmounts[_msgSender()] += amount_; - - // Transfer tokens. - _orchestratorToken.safeTransferFrom( - _msgSender(), address(this), amount_ - ); - - // Emit event. - emit Deposited(_msgSender(), amount_); - } - - /// @inheritdoc IFundingManager_v1 - /// @dev Only the payment client can call this function. - function transferOrchestratorToken(address to, uint amount) - external - virtual - override - onlyPaymentClient - { - // Validate parameters. - _validateOrchestratorTokenTransfer(to, amount); - - // Transfer tokens. - _orchestratorToken.safeTransfer(to, amount); - - // Emit event. - emit TransferOrchestratorToken(to, amount); - } - - // ========================================================================= - // Internal - - /// @notice Validates the transfer of orchestrator token. - /// @param to_ Address to transfer to. - /// @param amount_ Amount to transfer. - function _validateOrchestratorTokenTransfer(address to_, uint amount_) - internal - view - virtual - { - if (to_ == address(0)) { - revert Module__FM_Template__ReceiverNotValid(); - } - - if (amount_ == 0) { - revert Module__FM_Template_InvalidAmount(); - } - - if (_depositedAmounts[_msgSender()] < amount_) { - revert Module__FM_Template_InvalidAmount(); - } - } - - // ========================================================================= - // Overridden Internal Functions -} diff --git a/src/modules/paymentProcessor/abstracts/IFM_Template_v1.sol b/src/modules/paymentProcessor/abstracts/IFM_Template_v1.sol deleted file mode 100644 index f644c53df..000000000 --- a/src/modules/paymentProcessor/abstracts/IFM_Template_v1.sol +++ /dev/null @@ -1,72 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity ^0.8.0; - -import {IFundingManager_v1} from "@fm/IFundingManager_v1.sol"; - -/** - * @title Inverter Template Funding Manager - * - * @notice Basic template for funding manager inteface used as base for developing new - * funding managers. - * - * @dev This contract is used to showcase a basic setup for a funding - * manager. The contract showcases the following: - * - Inherit from the Module_v1 contract to enable interaction with - * the Inverter workflow. - * - Use of the IFundingManager_v1 interface to facilitate - * interaction as a Funding Manager. - * - Implement custom interface which has all the public facing - * functions, errors, events and structs. - * - Pre-defined layout for all contract functions, modifiers, state - * variables etc. - * - Use of the ERC165Upgradeable contract to check for interface - * support. - * - * @custom:security-contact security@inverter.network - * In case of any concerns or findings, please refer - * to our Security Policy at security.inverter.network - * or email us directly! - * - * @custom:version 1.0.0 - * - * @author Inverter Network - */ -interface IFM_Template_v1 is IFundingManager_v1 { - // ========================================================================= - // Structs - - // ========================================================================= - // Events - - /// @notice Emit when the token amount has been deposited. - /// @param sender_ The address of the depositor. - /// @param amount_ The amount of tokens deposited. - event Deposited(address indexed sender_, uint amount_); - - // ========================================================================= - // Errors - - /// @notice Amount can not be zero. - error Module__FM_Template_InvalidAmount(); - - /// @notice Token receiver is not valid. - error Module__FM_Template__ReceiverNotValid(); - - // ========================================================================= - // Public - Getters - - /// @notice Returns the deposited balance of a specific address. - /// @param user_ The address of the user. - /// @return amount_ Deposited amount of the user. - function getDepositedAmount(address user_) - external - view - returns (uint amount_); - - // ========================================================================= - // Public - Mutating - - /// @notice Deposits tokens to the funding manager. - /// @param amount_ The amount of tokens to deposit. - function deposit(uint amount_) external; -} diff --git a/src/modules/paymentProcessor/abstracts/IPP_Template_v1.sol b/src/modules/paymentProcessor/abstracts/IPP_Template_v1.sol deleted file mode 100644 index a8ca0d9ec..000000000 --- a/src/modules/paymentProcessor/abstracts/IPP_Template_v1.sol +++ /dev/null @@ -1,103 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity ^0.8.0; - -// Internal -import {IPaymentProcessor_v1} from "@pp/IPaymentProcessor_v1.sol"; - -/** - * @title Inverter Template Payment Processor - * - * @notice Basic template payment processor used as base for developing new - * payment processors. - * - * @dev This contract is used to showcase a basic setup for a payment - * processor. The contract showcases the following: - * - Inherit from the Module_v1 contract to enable interaction with - * the Inverter workflow. - * - Use of the IPaymentProcessor_v1 interface to facilitate - * interaction with a payment client. - * - Implement custom interface which has all the public facing - * functions, errors, events and structs. - * - Pre-defined layout for all contract functions, modifiers, state - * variables etc. - * - Use of the ERC165Upgradeable contract to check for interface - * support. - * - * @custom:security-contact security@inverter.network - * In case of any concerns or findings, please refer - * to our Security Policy at security.inverter.network - * or email us directly! - * - * @custom:version 1.0.0 - * - * @author Inverter Network - */ -interface IPP_Template_v1 is IPaymentProcessor_v1 { - //-------------------------------------------------------------------------- - // Structs - - /// @notice Struct to hold cross-chain message data - struct CrossChainMessage { - uint messageId; - address sourceChain; - address targetChain; - bytes payload; - bool executed; - } - - //-------------------------------------------------------------------------- - // Events - - /// @notice Emit when new payout amount has been set. - /// @param oldPayoutAmount_ Old payout amount. - /// @param newPayoutAmount_ Newly set payout amount. - event NewPayoutAmountMultiplierSet( - uint indexed oldPayoutAmount_, uint indexed newPayoutAmount_ - ); - - /// @notice Emitted when a cross-chain message is sent - /// @param messageId Unique identifier for the message - /// @param targetChain Address of the target chain - event CrossChainMessageSent( - uint indexed messageId, address indexed targetChain - ); - - /// @notice Emitted when a cross-chain message is received - /// @param messageId Unique identifier for the message - /// @param sourceChain Address of the source chain - event CrossChainMessageReceived( - uint indexed messageId, address indexed sourceChain - ); - - //-------------------------------------------------------------------------- - // Errors - - /// @notice Amount can not be zero. - error Module__PP_Template_InvalidAmount(); - - /// @notice Client is not valid. - error Module__PP_Template__ClientNotValid(); - - error Module__PP_CrossChain__NotValidClient(); - - error Module__PP_CrossChain__InvalidAmount(); - - /// @notice Message has already been executed - error Module__PP_CrossChain_MessageAlreadyExecuted(); - /// @notice Invalid chain ID provided - error Module__PP_CrossChain_InvalidChainId(); - /// @notice Message verification failed - error Module__PP_CrossChain_MessageVerificationFailed(); - - //-------------------------------------------------------------------------- - // Public (Getter) - - /// @notice Returns the payout amount for each payment order. - /// @return payoutAmountMultiplier_ The payout amount multiplier. - function getPayoutAmountMultiplier() - external - returns (uint payoutAmountMultiplier_); - - //-------------------------------------------------------------------------- - // Public (Mutating) -} diff --git a/src/modules/paymentProcessor/abstracts/PP_Template_v1.sol b/src/modules/paymentProcessor/abstracts/PP_Template_v1.sol deleted file mode 100644 index e1be6e87b..000000000 --- a/src/modules/paymentProcessor/abstracts/PP_Template_v1.sol +++ /dev/null @@ -1,265 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity 0.8.23; - -// Internal -import {IOrchestrator_v1} from - "src/orchestrator/interfaces/IOrchestrator_v1.sol"; -import {IPaymentProcessor_v1} from "@pp/IPaymentProcessor_v1.sol"; -import {IERC20PaymentClientBase_v1} from - "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; -import {IPP_Template_v1} from "./IPP_Template_v1.sol"; -import {ERC165Upgradeable, Module_v1} from "src/modules/base/Module_v1.sol"; - -// External -import {IERC20} from "@oz/token/ERC20/IERC20.sol"; - -/** - * @title Inverter Template Payment Processor - * - * @notice Basic template payment processor used as base for developing new - * payment processors. - * - * @dev This contract is used to showcase a basic setup for a payment - * processor. The contract showcases the following: - * - Inherit from the Module_v1 contract to enable interaction with - * the Inverter workflow. - * - Use of the IPaymentProcessor_v1 interface to facilitate - * interaction with a payment client. - * - Implement custom interface which has all the public facing - * functions, errors, events and structs. - * - Pre-defined layout for all contract functions, modifiers, state - * variables etc. - * - Use of the ERC165Upgradeable contract to check for interface - * support. - * - * @custom:security-contact security@inverter.network - * In case of any concerns or findings, please refer - * to our Security Policy at security.inverter.network - * or email us directly! - * - * @custom:version 1.0.0 - * - * @author Inverter Network - */ -contract PP_Template_v1 is IPP_Template_v1, Module_v1 { - //-------------------------------------------------------------------------- - // Libraries - - // Add library usage here - - //-------------------------------------------------------------------------- - // ERC165 - - /// @inheritdoc ERC165Upgradeable - function supportsInterface(bytes4 interfaceId_) - public - view - virtual - override(Module_v1) - returns (bool) - { - return interfaceId_ == type(IPP_Template_v1).interfaceId - || interfaceId_ == type(IPaymentProcessor_v1).interfaceId - || super.supportsInterface(interfaceId_); - } - - //-------------------------------------------------------------------------- - // Constants - - // Add constants here - - //-------------------------------------------------------------------------- - // State - - /// @dev Payout amount multiplier. - uint internal _payoutAmountMultiplier; - - /// @dev Payment ID of the last processed payment order. - uint internal _paymentId; - - //-------------------------------------------------------------------------- - // Modifiers - - /// @dev Checks that the client is calling for itself. - modifier clientIsValid(address client_) { - // Modifier logic moved to internal function for contract size reduction. - _ensureValidClient(client_); - _; - } - - //-------------------------------------------------------------------------- - // Constructor & Init - - /// @inheritdoc Module_v1 - function init( - IOrchestrator_v1 orchestrator_, - Metadata memory metadata_, - bytes memory configData_ - ) external override(Module_v1) initializer { - __Module_init(orchestrator_, metadata_); - - // Decode module specific init data through use of configData bytes. - // This value is an example value used to showcase the setters/getters - // and internal functions/state formating style. - (uint payoutAmountMultiplier_) = abi.decode(configData_, (uint)); - - // Set init state. - _setPayoutAmountMultiplier(payoutAmountMultiplier_); - } - - //-------------------------------------------------------------------------- - // Public (Getters) - - /// @inheritdoc IPP_Template_v1 - function getPayoutAmountMultiplier() - external - view - returns (uint payoutAmount_) - { - return _payoutAmountMultiplier; - } - - //-------------------------------------------------------------------------- - // Public (Mutating) - - /// @inheritdoc IPaymentProcessor_v1 - function processPayments(IERC20PaymentClientBase_v1 client_) - external - clientIsValid(address(client_)) - { - // The IERC20PaymentClientBase_v1 client should be used to access - // created payment orders in the Logic Module (LM) implementing the - // interface. The interface should be referenced to see the different - // functionalities provided by the ERC20PaymentClientBase_v1. - - // Collect orders from the client - IERC20PaymentClientBase_v1.PaymentOrder[] memory orders; - (orders,,) = client_.collectPaymentOrders(); - - // Custom logic to proces the payment orders should be implemented - // below. This template implements a straight forward token transfer - // using the first order of the payment order array for simplicity. - - // Get payment order details - address recipient_ = orders[0].recipient; - address token_ = orders[0].paymentToken; - uint amount_ = orders[0].amount * _payoutAmountMultiplier; - _paymentId = _paymentId + 1; - - // Emit event of the IPaymentProcessor_v1. This is used by Inverter's - // Indexer. - emit PaymentOrderProcessed( - address(client_), recipient_, token_, amount_, 0, 0, 0 - ); - - // Transfer tokens from {IERC20PaymentClientBase_v1} to order - // recipients. - // Please note: When processing multiple payment orders and then - // letting the call revert as in this example might not be the best - // solution. Ways to handle this by implementing the `unclaimable` - // function can be found in the other Payment Processor (PP) - // implementations. - IERC20(token_).transferFrom(address(client_), recipient_, amount_); - - // Inform the client about the amount that was released, to keep - // the accounting correct. - client_.amountPaid(token_, amount_); - - // Emit event of the IPaymentProcessor_v1. This is used by Inverter's - // Indexer. - emit TokensReleased(recipient_, token_, amount_); - } - - /// @inheritdoc IPaymentProcessor_v1 - function cancelRunningPayments(IERC20PaymentClientBase_v1 client_) - external - view - clientIsValid(address(client_)) - { - // This function is used to implement custom logic to cancel running - // payments. If the nature of processing payments is one of direct - // processing then this function can be left empty, return nothing. - return; - } - - /// @inheritdoc IPaymentProcessor_v1 - function unclaimable( - address, /*client_*/ - address, /*token_*/ - address /*paymentReceiver_*/ - ) external pure returns (uint amount_) { - // This function is used to check if there are unclaimable tokens for a - // specific client, token and payment receiver. As this template only - // executes one payment order at a time, this function is not utilzed - // and can return 0. - return 0; - } - - /// @inheritdoc IPaymentProcessor_v1 - function claimPreviouslyUnclaimable( - address, /*client_*/ - address, /*token_*/ - address /*receiver_*/ - ) external virtual { - return; - } - - /// @inheritdoc IPaymentProcessor_v1 - function validPaymentOrder( - IERC20PaymentClientBase_v1.PaymentOrder memory order_ - ) external view returns (bool) { - // This function is used to validate the payment order created on the - // client side (LM_PC) with the input required by the Payment Processor - // (PP). The function should return true if the payment order is valid - // and false if it is not. - - // For this template, only the receiver is validated. - return _validPaymentReceiver(order_.recipient); - } - - //-------------------------------------------------------------------------- - // Internal - - /// @dev Internal function to set the new payout amount multiplier. - /// @param newPayoutAmountMultiplier_ Payout amount multiplier to be set in - // the state. Cannot be zero. - function _setPayoutAmountMultiplier(uint newPayoutAmountMultiplier_) - internal - { - if (newPayoutAmountMultiplier_ == 0) { - revert Module__PP_Template_InvalidAmount(); - } - emit NewPayoutAmountMultiplierSet( - _payoutAmountMultiplier, newPayoutAmountMultiplier_ - ); - _payoutAmountMultiplier = newPayoutAmountMultiplier_; - } - - /// @dev Validate whether the address is a valid payment receiver. - /// @param receiver_ Address to validate. - /// @return validPaymentReceiver_ True if address is valid. - function _validPaymentReceiver(address receiver_) - internal - view - virtual - returns (bool) - { - return !( - receiver_ == address(0) || receiver_ == _msgSender() - || receiver_ == address(this) - || receiver_ == address(orchestrator()) - || receiver_ == address(orchestrator().fundingManager().token()) - ); - } - - /// @dev Internal function to check whether the client is valid. - /// @param client_ Address to validate. - function _ensureValidClient(address client_) internal view { - if (_msgSender() != client_) { - revert Module__PP_Template__ClientNotValid(); - } - } - - //-------------------------------------------------------------------------- - // Internal override -} From 54e595f7804afe235ce89352c02c08da14693c18 Mon Sep 17 00:00:00 2001 From: Leandro Faria Date: Mon, 6 Jan 2025 21:04:49 -0600 Subject: [PATCH 77/93] add tests for unhappy paths --- .../PP_Connext_Crosschain_v1_Test.t.sol | 122 +++++++++++++++++- .../mocks/external/Mock_EverclearPayment.sol | 6 + 2 files changed, 126 insertions(+), 2 deletions(-) diff --git a/test/modules/paymentProcessor/PP_Connext_Crosschain_v1_Test.t.sol b/test/modules/paymentProcessor/PP_Connext_Crosschain_v1_Test.t.sol index b6cce6ad5..305571d66 100644 --- a/test/modules/paymentProcessor/PP_Connext_Crosschain_v1_Test.t.sol +++ b/test/modules/paymentProcessor/PP_Connext_Crosschain_v1_Test.t.sol @@ -584,10 +584,10 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { orders[0].amount ); - uint failedIntentId = paymentProcessor.failedTransfers( + uint failedAmount = paymentProcessor.failedTransfers( address(paymentClient), recipient, executionData ); - assertEq(failedIntentId, orders[0].amount); + assertEq(failedAmount, orders[0].amount); // Cancel as recipient vm.prank(address(paymentClient)); @@ -646,6 +646,124 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { ); } + /* Test TTL validation + └── Given execution data with zero TTL + └── When processing payments + └── Then it should revert with InvalidTTL + */ + function testProcessPayments_revertsWithZeroTTL( + address testRecipient, + uint testAmount + ) public { + vm.assume(testRecipient != address(0)); + vm.assume(testAmount > 0 && testAmount < MINTED_SUPPLY); + + _setupSinglePayment(testRecipient, testAmount); + + bytes memory zeroTTLData = abi.encode(maxFee, 0); + vm.expectRevert(); + paymentProcessor.processPayments( + IERC20PaymentClientBase_v1(address(paymentClient)), zeroTTLData + ); + } + + /* Test retry with invalid client + └── Given a retry request from non-client address + └── When retrying failed transfer + └── Then it should revert with InvalidAddress + */ + function testRetryFailedTransfer_revertsWithInvalidCaller( + address testRecipient, + uint testAmount, + address invalidCaller + ) public { + vm.assume(testRecipient != address(0)); + vm.assume(testAmount > 0 && testAmount < MINTED_SUPPLY); + vm.assume(invalidCaller != address(paymentClient)); + + // Setup failed transfer + IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = + _setupSinglePayment(testRecipient, testAmount); + + bytes memory failingExecutionData = abi.encode(333, 1); + paymentProcessor.processPayments( + IERC20PaymentClientBase_v1(address(paymentClient)), + failingExecutionData + ); + + // Attempt retry from invalid caller + vm.prank(invalidCaller); + vm.expectRevert(IModule_v1.Module__InvalidAddress.selector); + paymentProcessor.retryFailedTransfer( + address(paymentClient), + testRecipient, + failingExecutionData, + executionData, + orders[0] + ); + } + + /* Test retry with no failed transfer record + └── Given a retry request for non-existent failed transfer + └── When retrying transfer + └── Then it should revert with InvalidAmount + */ + function testRetryFailedTransfer_revertsWithNoFailedTransfer( + address testRecipient, + uint testAmount + ) public { + vm.assume(testRecipient != address(0)); + vm.assume(testAmount > 0 && testAmount < MINTED_SUPPLY); + + IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = + _setupSinglePayment(testRecipient, testAmount); + + vm.prank(address(paymentClient)); + vm.expectRevert( + ICrossChainBase_v1.Module__CrossChainBase__InvalidAmount.selector + ); + paymentProcessor.retryFailedTransfer( + address(paymentClient), + testRecipient, + executionData, + executionData, + orders[0] + ); + } + + /* Test retry with existing intent + └── Given a retry request when intent already exists + └── When retrying transfer + └── Then it should revert with InvalidIntentId + */ + function testRetryFailedTransfer_revertsWithExistingIntent( + address testRecipient, + uint testAmount + ) public { + vm.assume(testRecipient != address(0)); + vm.assume(testAmount > 0 && testAmount < MINTED_SUPPLY); + + // Setup initial payment and process it + IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = + _setupSinglePayment(testRecipient, testAmount); + + // Create a successful intent first + paymentProcessor.processPayments( + IERC20PaymentClientBase_v1(address(paymentClient)), executionData + ); + + // Now try to retry (should fail because intent exists) + vm.prank(address(paymentClient)); + vm.expectRevert(); + paymentProcessor.retryFailedTransfer( + address(paymentClient), + testRecipient, + executionData, + executionData, + orders[0] + ); + } + //-------------------------------------------------------------------------- // Helper Functions diff --git a/test/utils/mocks/external/Mock_EverclearPayment.sol b/test/utils/mocks/external/Mock_EverclearPayment.sol index 8745c3bd8..598d32f44 100644 --- a/test/utils/mocks/external/Mock_EverclearPayment.sol +++ b/test/utils/mocks/external/Mock_EverclearPayment.sol @@ -6,12 +6,14 @@ contract Mock_EverclearPayment { uint public nonce; uint32 public DOMAIN; + IntentStatus public nextIntentStatus; mapping(bytes32 => IntentStatus) public status; enum IntentStatus { NONE, ADDED, SETTLED, + FAILED, SETTLED_AND_MANUALLY_EXECUTED } @@ -70,4 +72,8 @@ contract Mock_EverclearPayment { return (_intentId); } + + function setNextIntentStatus(IntentStatus _status) external { + nextIntentStatus = _status; + } } From a22d76b5747dd9d45888618f073cbb32189f08e3 Mon Sep 17 00:00:00 2001 From: Leandro Faria Date: Mon, 6 Jan 2025 21:09:53 -0600 Subject: [PATCH 78/93] chore:change mapping name --- .../PP_Connext_Crosschain_v1.sol | 10 ++++---- .../PP_Connext_Crosschain_v1_Test.t.sol | 25 ++++++++++++------- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol b/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol index 1ecfe7952..d4f62d944 100644 --- a/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol +++ b/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol @@ -43,7 +43,7 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { /// paymentClient => paymentReceiver => intentId. mapping( address paymentClient => mapping(address recipient => bytes32 intentId) - ) public intentId; + ) public processedIntentId; /// @dev Tracks failed transfers that can be retried /// paymentClient => recipient => intentId => amount @@ -116,7 +116,7 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { orders[i].end ); _paymentId++; - intentId[address(client)][orders[i].recipient] = + processedIntentId[address(client)][orders[i].recipient] = bytes32(bridgeData); client.amountPaid(orders[i].paymentToken, orders[i].amount); } @@ -171,7 +171,7 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { } _cleanupFailedTransfer(client, recipient, executionData); - intentId[client][recipient] = newIntentId; + processedIntentId[client][recipient] = newIntentId; } // Public Functions @@ -271,7 +271,7 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { revert Module__CrossChainBase__InvalidAmount(); } //intentId should be 0 if the transfer has not been processed yet - if (intentId[client][recipient] != bytes32(0)) { + if (processedIntentId[client][recipient] != bytes32(0)) { revert Module__PP_Crosschain__InvalidIntentId(); } @@ -289,7 +289,7 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { address recipient, bytes memory executionData ) internal { - delete intentId[client][recipient]; + delete processedIntentId[client][recipient]; delete failedTransfers[client][recipient][executionData]; } diff --git a/test/modules/paymentProcessor/PP_Connext_Crosschain_v1_Test.t.sol b/test/modules/paymentProcessor/PP_Connext_Crosschain_v1_Test.t.sol index 305571d66..c31c909ba 100644 --- a/test/modules/paymentProcessor/PP_Connext_Crosschain_v1_Test.t.sol +++ b/test/modules/paymentProcessor/PP_Connext_Crosschain_v1_Test.t.sol @@ -181,8 +181,9 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { // Process payments paymentProcessor.processPayments(client, executionData); - bytes32 intentId = - paymentProcessor.intentId(address(paymentClient), testRecipient); + bytes32 intentId = paymentProcessor.processedIntentId( + address(paymentClient), testRecipient + ); assertEq( uint(everclearPaymentMock.status(intentId)), uint(Mock_EverclearPayment.IntentStatus.ADDED) @@ -247,7 +248,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { paymentProcessor.processPayments(client, executionData); //should be checking in the mock for valid bridge data for (uint i = 0; i < numRecipients; i++) { - bytes32 intentId = paymentProcessor.intentId( + bytes32 intentId = paymentProcessor.processedIntentId( address(paymentClient), setupRecipients[i] ); assertEq( @@ -272,7 +273,9 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { "Bridge data should be empty" ); assertEq( - paymentProcessor.intentId(address(paymentClient), address(0)), + paymentProcessor.processedIntentId( + address(paymentClient), address(0) + ), bytes32(0) ); } @@ -549,8 +552,9 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { ); // 2. New intent was created (should be non-zero) - bytes32 newIntentId = - paymentProcessor.intentId(address(paymentClient), recipient); + bytes32 newIntentId = paymentProcessor.processedIntentId( + address(paymentClient), recipient + ); assertTrue(newIntentId != bytes32(0)); } @@ -597,7 +601,9 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { // Verify intentId was cleared assertEq( - paymentProcessor.intentId(address(paymentClient), recipient), + paymentProcessor.processedIntentId( + address(paymentClient), recipient + ), bytes32(0) ); } @@ -623,8 +629,9 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { IERC20PaymentClientBase_v1(address(paymentClient)), executionData ); - bytes32 pendingIntentId = - paymentProcessor.intentId(address(paymentClient), testRecipient); + bytes32 pendingIntentId = paymentProcessor.processedIntentId( + address(paymentClient), testRecipient + ); // Create payment order for cancellation IERC20PaymentClientBase_v1.PaymentOrder memory order = From d986ff1d89a359cd89466f76599a7a9b8179278a Mon Sep 17 00:00:00 2001 From: Leandro Faria Date: Mon, 6 Jan 2025 21:15:57 -0600 Subject: [PATCH 79/93] remove redundant file --- .../abstracts/CrossChainBase_v1_Test.t.sol | 180 ------------------ 1 file changed, 180 deletions(-) delete mode 100644 test/modules/paymentProcessor/abstracts/CrossChainBase_v1_Test.t.sol diff --git a/test/modules/paymentProcessor/abstracts/CrossChainBase_v1_Test.t.sol b/test/modules/paymentProcessor/abstracts/CrossChainBase_v1_Test.t.sol deleted file mode 100644 index ea967e3ed..000000000 --- a/test/modules/paymentProcessor/abstracts/CrossChainBase_v1_Test.t.sol +++ /dev/null @@ -1,180 +0,0 @@ -//SPDX-License-Identifier: LGPL-3.0-only -pragma solidity ^0.8.0; - -//-------------------------------------------------------------------------- -// Imports - -// External Dependencies -import {Clones} from "@oz/proxy/Clones.sol"; - -// Test Framework -import { - ModuleTest, - IModule_v1, - IOrchestrator_v1 -} from "test/modules/ModuleTest.sol"; -import {OZErrors} from "test/utils/errors/OZErrors.sol"; - -// Interfaces -import {ICrossChainBase_v1} from - "src/modules/paymentProcessor/interfaces/ICrosschainBase_v1.sol"; -import {IPaymentProcessor_v1} from - "src/orchestrator/interfaces/IOrchestrator_v1.sol"; - -// Implementation -import {CrossChainBase_v1} from - "src/modules/paymentProcessor/abstracts/CrossChainBase_v1.sol"; - -// Mocks -import { - IERC20PaymentClientBase_v1, - ERC20PaymentClientBaseV1Mock, - ERC20Mock -} from "test/utils/mocks/modules/paymentClient/ERC20PaymentClientBaseV1Mock.sol"; -import {CrossChainBase_v1_Exposed} from - "test/utils/mocks/modules/paymentProcessor/CrossChainBase_v1_Exposed.sol"; - -contract CrossChainBase_v1_Test is ModuleTest { - //-------------------------------------------------------------------------- - // Events - event PaymentOrderProcessed( - address indexed client, - address indexed recipient, - address indexed token, - uint amount, - uint start, - uint cliff, - uint end - ); - - //-------------------------------------------------------------------------- - // Test Storage - ERC20PaymentClientBaseV1Mock paymentClient; - CrossChainBase_v1_Exposed public crossChainBase; - - //-------------------------------------------------------------------------- - // Setup Function - - function setUp() public { - //This function is used to setup the unit test - //Deploy the SuT - address impl = address(new CrossChainBase_v1_Exposed()); - crossChainBase = CrossChainBase_v1_Exposed(Clones.clone(impl)); - - //Setup the module to test - _setUpOrchestrator(crossChainBase); - - //General setup for other contracts in the workflow - _authorizer.setIsAuthorized(address(this), true); - - //Initiate the PP with the medata and config data - crossChainBase.init(_orchestrator, _METADATA, abi.encode(1)); - - //Setup other modules needed in the unit tests. - //In this case a payment client is needed to test the PP_Template_v1. - impl = address(new ERC20PaymentClientBaseV1Mock()); - paymentClient = ERC20PaymentClientBaseV1Mock(Clones.clone(impl)); - //Adding the payment client is done through a timelock mechanism - _orchestrator.initiateAddModuleWithTimelock(address(paymentClient)); - vm.warp(block.timestamp + _orchestrator.MODULE_UPDATE_TIMELOCK()); - _orchestrator.executeAddModule(address(paymentClient)); - //Init payment client - paymentClient.init(_orchestrator, _METADATA, bytes("")); - paymentClient.setIsAuthorized(address(crossChainBase), true); - paymentClient.setToken(_token); - } - - //-------------------------------------------------------------------------- - // Test Cases - - //Test: Initialization - /* - └── Given the contract is not initialized - └── When initializing the contract - └── Then it should set the correct orchestrator address */ - function testInit() public override(ModuleTest) { - assertEq(address(crossChainBase.orchestrator()), address(_orchestrator)); - } - - //Test: Interface Support - /* - └── Given the contract is initialized - └─��� When checking for ICrossChainBase_v1 interface support - └── Then it should return true - └── When checking for an unknown interface - └── Then it should return false */ - function testSupportsInterface() public { - // Test for ICrossChainBase_v1 interface support - bytes4 interfaceId = type(ICrossChainBase_v1).interfaceId; - assertTrue(crossChainBase.supportsInterface(interfaceId)); - } - - function testSupportsInterface_revertsGivenUnknownInterface() public { - bytes4 randomInterfaceId = bytes4(keccak256("random()")); - assertFalse(crossChainBase.supportsInterface(randomInterfaceId)); - } - - /* - └── Given the contract is already initialized - └── When trying to reinitialize - └── Then it should revert with Initializable__InvalidInitialization */ - function testReinitFails() public override(ModuleTest) { - vm.expectRevert(OZErrors.Initializable__InvalidInitialization); - crossChainBase.init(_orchestrator, _METADATA, abi.encode(1)); - } - - //Test: Bridge Transfer - /* - └── Given an empty payment order is created - └── When executeBridgeTransfer is called - └── Then it should return empty bytes */ - function testExecuteBridgeTransfer_worksGivenEmptyPaymentOrder() public { - address[] memory setupRecipients = new address[](1); - setupRecipients[0] = address(1); - uint[] memory setupAmounts = new uint[](1); - setupAmounts[0] = 100 ether; - - IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = - _createPaymentOrders(1, setupRecipients, setupAmounts); - paymentClient.addPaymentOrders(orders); - - bytes memory executionData = abi.encode(0, 0); //maxFee and ttl setup - - bytes memory result = crossChainBase.exposed_executeBridgeTransfer( - orders[0], executionData - ); - assertEq(result, bytes("")); - } - - //-------------------------------------------------------------------------- - // Helper Functions - - function _createPaymentOrders( - uint orderCount, - address[] memory recipients, - uint[] memory amounts - ) - internal - view - returns (IERC20PaymentClientBase_v1.PaymentOrder[] memory) - { - // Sanity checks for array lengths - require( - recipients.length == orderCount && amounts.length == orderCount, - "Array lengths must match orderCount" - ); - IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = - new IERC20PaymentClientBase_v1.PaymentOrder[](orderCount); - for (uint i = 0; i < orderCount; i++) { - orders[i] = IERC20PaymentClientBase_v1.PaymentOrder({ - recipient: recipients[i], - paymentToken: address(0xabcd), - amount: amounts[i], - start: block.timestamp, - cliff: 0, - end: block.timestamp + 1 days - }); - } - return orders; - } -} From c9a9d9a67a5e4b00b5c31b27d6b73ed06e8e16b5 Mon Sep 17 00:00:00 2001 From: Leandro Faria Date: Mon, 6 Jan 2025 21:17:39 -0600 Subject: [PATCH 80/93] chore:rename file --- ...xt_Crosschain_v1_Test.t.sol => PP_Connext_Crosschain_v1.t.sol} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename test/modules/paymentProcessor/{PP_Connext_Crosschain_v1_Test.t.sol => PP_Connext_Crosschain_v1.t.sol} (100%) diff --git a/test/modules/paymentProcessor/PP_Connext_Crosschain_v1_Test.t.sol b/test/modules/paymentProcessor/PP_Connext_Crosschain_v1.t.sol similarity index 100% rename from test/modules/paymentProcessor/PP_Connext_Crosschain_v1_Test.t.sol rename to test/modules/paymentProcessor/PP_Connext_Crosschain_v1.t.sol From e17ae529797d6b526039a772d0019cc7b131786f Mon Sep 17 00:00:00 2001 From: Leandro Faria Date: Mon, 6 Jan 2025 22:46:42 -0600 Subject: [PATCH 81/93] add base test file --- .../modules/paymentProcessor/CrossChainBase_v1_Exposed.sol | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/utils/mocks/modules/paymentProcessor/CrossChainBase_v1_Exposed.sol b/test/utils/mocks/modules/paymentProcessor/CrossChainBase_v1_Exposed.sol index 48322ad2c..8eac2dbfb 100644 --- a/test/utils/mocks/modules/paymentProcessor/CrossChainBase_v1_Exposed.sol +++ b/test/utils/mocks/modules/paymentProcessor/CrossChainBase_v1_Exposed.sol @@ -1,15 +1,12 @@ // SPDX-License-Identifier: LGPL-3.0-only // Internal Dependencies -//import {PP_CrossChain_v1} from "src/templates/modules/PP_Template_v1.sol"; import {CrossChainBase_v1} from "src/modules/paymentProcessor/abstracts/CrossChainBase_v1.sol"; import {IERC20PaymentClientBase_v1} from "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; contract CrossChainBase_v1_Exposed is CrossChainBase_v1 { - ///// @inheritdoc CrossChainBase_v1 - function _executeBridgeTransfer( IERC20PaymentClientBase_v1.PaymentOrder memory order, bytes memory executionData From a3d5311856c1301cbb63950e8a69143882d9b87e Mon Sep 17 00:00:00 2001 From: Zuhaib Mohammed Date: Mon, 13 Jan 2025 12:54:50 +0530 Subject: [PATCH 82/93] fix: remove chainid impl --- .../interfaces/ICrosschainBase_v1.sol | 3 -- .../PP_Connext_Crosschain_v1.t.sol | 31 ++++++++++++++++--- .../abstracts/CrossChainBase_v1.t.sol | 5 +-- 3 files changed, 30 insertions(+), 9 deletions(-) diff --git a/src/modules/paymentProcessor/interfaces/ICrosschainBase_v1.sol b/src/modules/paymentProcessor/interfaces/ICrosschainBase_v1.sol index 5e5c7825f..ae19d5a5c 100644 --- a/src/modules/paymentProcessor/interfaces/ICrosschainBase_v1.sol +++ b/src/modules/paymentProcessor/interfaces/ICrosschainBase_v1.sol @@ -43,9 +43,6 @@ interface ICrossChainBase_v1 { /// @notice Thrown when attempting to execute a message that has already been processed error Module__CrossChainBase_MessageAlreadyExecuted(); - /// @notice Thrown when an unsupported or invalid chain ID is provided - error Module__CrossChainBase_InvalidChainId(); - /// @notice Thrown when the cross-chain message fails verification error Module__CrossChainBase_MessageVerificationFailed(); diff --git a/test/modules/paymentProcessor/PP_Connext_Crosschain_v1.t.sol b/test/modules/paymentProcessor/PP_Connext_Crosschain_v1.t.sol index c31c909ba..c0502b212 100644 --- a/test/modules/paymentProcessor/PP_Connext_Crosschain_v1.t.sol +++ b/test/modules/paymentProcessor/PP_Connext_Crosschain_v1.t.sol @@ -53,7 +53,6 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { ERC20PaymentClientBaseV1Mock paymentClient; IPP_Crosschain_v1 public crossChainBase; IWETH public weth; - uint public chainId; // Bridge-related storage address public mockConnextBridge; @@ -70,9 +69,6 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { // Setup Function function setUp() public { - // Set chain ID for test environment - chainId = block.chainid; - // Prepare execution data for bridge operations executionData = abi.encode(maxFee, ttl); invalidExecutionData = abi.encode(address(0)); @@ -771,6 +767,33 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { ); } + /* Test process payments without token approval + └── Given a payment order with zero approval + └── When attempting to process payment + └── Then it should revert with InvalidTokenApproval + */ + function testProcessPayments_revertsWithoutTokenApproval() public { + // Reset approval + _token.approve(address(paymentProcessor), 0); + + address recipient = address(0xBEEF); + uint amount = 1 ether; + _setupSinglePayment(recipient, amount); + + // Expect revert for insufficient allowance + vm.expectRevert( + abi.encodeWithSelector( + IERC20Errors.ERC20InsufficientAllowance.selector, + address(paymentProcessor), + 0, + amount + ) + ); + paymentProcessor.processPayments( + IERC20PaymentClientBase_v1(address(paymentClient)), executionData + ); + } + //-------------------------------------------------------------------------- // Helper Functions diff --git a/test/modules/paymentProcessor/abstracts/CrossChainBase_v1.t.sol b/test/modules/paymentProcessor/abstracts/CrossChainBase_v1.t.sol index 1e641fd5f..a4cd37699 100644 --- a/test/modules/paymentProcessor/abstracts/CrossChainBase_v1.t.sol +++ b/test/modules/paymentProcessor/abstracts/CrossChainBase_v1.t.sol @@ -17,7 +17,8 @@ import { ERC20PaymentClientBaseV1Mock, ERC20Mock } from "test/utils/mocks/modules/paymentClient/ERC20PaymentClientBaseV1Mock.sol"; -import {CrossChainBase_v1_Exposed} from "./CrossChainBase_v1_Exposed.sol"; +import {CrossChainBase_v1_Exposed} from + "test/utils/mocks/modules/paymentProcessor/CrossChainBase_v1_Exposed.sol"; import {IPaymentProcessor_v1} from "src/orchestrator/interfaces/IOrchestrator_v1.sol"; @@ -40,7 +41,7 @@ contract CrossChainBase_v1_Test is ModuleTest { function setUp() public { //This function is used to setup the unit test //Deploy the SuT - address impl = address(new CrossChainBase_v1_Exposed(block.chainid)); + address impl = address(new CrossChainBase_v1_Exposed()); crossChainBase = CrossChainBase_v1_Exposed(Clones.clone(impl)); //Setup the module to test From 6d0378cfe02afcad282f4bb4ea56073b33bd5b77 Mon Sep 17 00:00:00 2001 From: Zuhaib Mohammed Date: Mon, 13 Jan 2025 18:33:59 +0530 Subject: [PATCH 83/93] chore: add unit tests --- .../PP_Connext_Crosschain_v1.t.sol | 48 ++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/test/modules/paymentProcessor/PP_Connext_Crosschain_v1.t.sol b/test/modules/paymentProcessor/PP_Connext_Crosschain_v1.t.sol index c0502b212..7a9588f33 100644 --- a/test/modules/paymentProcessor/PP_Connext_Crosschain_v1.t.sol +++ b/test/modules/paymentProcessor/PP_Connext_Crosschain_v1.t.sol @@ -45,7 +45,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { //-------------------------------------------------------------------------- // Constants uint constant MINTED_SUPPLY = 1000 ether; - + uint constant ZERO_AMOUNT = 0; //-------------------------------------------------------------------------- // Test Storage PP_Connext_Crosschain_v1_Exposed public paymentProcessor; @@ -552,6 +552,24 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { address(paymentClient), recipient ); assertTrue(newIntentId != bytes32(0)); + + // uint processorBalanceBefore = + // _token.balanceOf(address(paymentProcessor)); + + // assertEq( + // paymentProcessor.failedTransfers( + // address(paymentClient), recipient, failingExecutionData + // ), + // 0 + // ); + + // bytes32 newIntentId = paymentProcessor.processedIntentId( + // address(paymentClient), recipient + // ); + // assertEq( + // uint(everclearPaymentMock.status(newIntentId)), + // uint(Mock_EverclearPayment.IntentStatus.ADDED) + // ); } /* Test cancel transfer @@ -794,6 +812,34 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { ); } + /* Test payment processing with zero token balance + └── Given payment processor has zero token balance + └── When processing payments + └── Then it should revert with ERC20InsufficientBalance + */ + function testProcessPayments_revertsWithZeroBalance( + address testRecipient, + address clearAddress, + uint testAmount + ) public { + vm.assume(testRecipient != address(0)); + vm.assume(testAmount > 0 && testAmount < MINTED_SUPPLY); + vm.assume(clearAddress != address(0)); + + // Clear processor balance + vm.prank(address(paymentProcessor)); + assertGt(_token.balanceOf(address(paymentProcessor)), ZERO_AMOUNT); + _token.transfer( + clearAddress, _token.balanceOf(address(paymentProcessor)) + ); + _setupSinglePayment(testRecipient, testAmount); + + vm.expectRevert(); + paymentProcessor.processPayments( + IERC20PaymentClientBase_v1(address(paymentClient)), executionData + ); + } + //-------------------------------------------------------------------------- // Helper Functions From f4164afc3637214ef7529e32db4f2b930a69d16f Mon Sep 17 00:00:00 2001 From: Zuhaib Mohammed Date: Thu, 16 Jan 2025 16:17:04 +0530 Subject: [PATCH 84/93] chore: add unit tests --- .../PP_Connext_Crosschain_v1.t.sol | 99 ++++++++++++++----- 1 file changed, 76 insertions(+), 23 deletions(-) diff --git a/test/modules/paymentProcessor/PP_Connext_Crosschain_v1.t.sol b/test/modules/paymentProcessor/PP_Connext_Crosschain_v1.t.sol index 7a9588f33..8e6a92918 100644 --- a/test/modules/paymentProcessor/PP_Connext_Crosschain_v1.t.sol +++ b/test/modules/paymentProcessor/PP_Connext_Crosschain_v1.t.sol @@ -1,3 +1,4 @@ +// External Dependencies // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.20; @@ -5,7 +6,6 @@ pragma solidity ^0.8.20; //-------------------------------------------------------------------------- // Imports -// External Dependencies import {Test} from "forge-std/Test.sol"; import {Clones} from "@oz/proxy/Clones.sol"; import {IERC20Errors} from "@oz/interfaces/draft-IERC6093.sol"; @@ -196,7 +196,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { function testPublicProcessPayments_succeedsGivenMultipleValidPaymentOrders( uint8 numRecipients, address testRecipient, - uint96 baseAmount + uint baseAmount ) public { // Assumptions to keep the test manageable and within bounds vm.assume(numRecipients > 0 && numRecipients <= 10); @@ -457,8 +457,8 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { address testRecipient, uint96 testAmount ) public { + // Assumptions vm.assume(testAmount > 0 && testAmount <= MINTED_SUPPLY); - // Assumption vm.assume(testRecipient != address(0)); // Setup - Clear existing balance @@ -471,6 +471,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { // Setup - Mint exact amount needed _token.mint(address(paymentProcessor), testAmount); + // Setup - Configure payment _setupSinglePayment(testRecipient, testAmount); // Expectations @@ -485,7 +486,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { block.timestamp + 1 days ); - // Action + // Action - Process payments paymentProcessor.processPayments( IERC20PaymentClientBase_v1(address(paymentClient)), executionData ); @@ -509,7 +510,6 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { // Store the initial execution data that will fail bytes memory failingExecutionData = abi.encode(333, 1); // maxFee of 333 will cause failure - // First attempt with high maxFee to force failure paymentProcessor.processPayments( IERC20PaymentClientBase_v1(address(paymentClient)), @@ -552,24 +552,6 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { address(paymentClient), recipient ); assertTrue(newIntentId != bytes32(0)); - - // uint processorBalanceBefore = - // _token.balanceOf(address(paymentProcessor)); - - // assertEq( - // paymentProcessor.failedTransfers( - // address(paymentClient), recipient, failingExecutionData - // ), - // 0 - // ); - - // bytes32 newIntentId = paymentProcessor.processedIntentId( - // address(paymentClient), recipient - // ); - // assertEq( - // uint(everclearPaymentMock.status(newIntentId)), - // uint(Mock_EverclearPayment.IntentStatus.ADDED) - // ); } /* Test cancel transfer @@ -667,6 +649,36 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { ); } + /* Test cancel transfer after processing + └── Given a successfully processed payment + └── When attempting to cancel the transfer + └── Then it should revert with InvalidAmount + └── And the intent ID should remain unchanged + └── And the payment order should remain processed + */ + function testCancelTransfer_revertsAfterProcessing() public { + // Setup + address recipient = address(0xBEEF); + uint amount = 1 ether; + + // Setup initial payment and process it + IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = + _setupSinglePayment(recipient, amount); + + paymentProcessor.processPayments( + IERC20PaymentClientBase_v1(address(paymentClient)), executionData + ); + + // Cancel the transfer + vm.prank(address(paymentClient)); + vm.expectRevert( + ICrossChainBase_v1.Module__CrossChainBase__InvalidAmount.selector + ); + paymentProcessor.cancelTransfer( + address(paymentClient), recipient, executionData, orders[0] + ); + } + /* Test TTL validation └── Given execution data with zero TTL └── When processing payments @@ -840,6 +852,47 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { ); } + /* Test payment processing with duplicate recipients + └── Given payment orders with duplicate recipients + └── When processing payments + └── Then it should handle duplicates correctly + └── And update intent IDs properly + └── And track total amounts correctly + */ + function testProcessPayments_handlesMultipleDuplicateRecipients( + address recipient, + uint amount + ) public { + vm.assume(recipient != address(0)); + vm.assume(amount > 0 && amount <= MINTED_SUPPLY / 3); + + // Create multiple orders for same recipient + address[] memory recipients = new address[](3); + uint[] memory amounts = new uint[](3); + + for (uint i = 0; i < 3; i++) { + recipients[i] = recipient; + amounts[i] = amount; + } + + IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = + _createPaymentOrders(3, recipients, amounts); + + // Process payments + paymentProcessor.processPayments( + IERC20PaymentClientBase_v1(address(paymentClient)), executionData + ); + + // Verify final intent ID exists + bytes32 finalIntentId = paymentProcessor.processedIntentId( + address(paymentClient), recipient + ); + assertTrue(finalIntentId != bytes32(0)); + + // // Verify total amount processed + // assertEq(uint(everclearPaymentMock.amount(finalIntentId)), amount * 3); + } + //-------------------------------------------------------------------------- // Helper Functions From 89184cdffecd7573bf33c3a81c3f7b0b0086cdde Mon Sep 17 00:00:00 2001 From: Zuhaib Mohammed Date: Thu, 16 Jan 2025 17:26:08 +0530 Subject: [PATCH 85/93] fix: make unit tests generic input --- .../PP_Connext_Crosschain_v1.t.sol | 96 +++++++++++++------ 1 file changed, 68 insertions(+), 28 deletions(-) diff --git a/test/modules/paymentProcessor/PP_Connext_Crosschain_v1.t.sol b/test/modules/paymentProcessor/PP_Connext_Crosschain_v1.t.sol index 8e6a92918..f3fd70ea3 100644 --- a/test/modules/paymentProcessor/PP_Connext_Crosschain_v1.t.sol +++ b/test/modules/paymentProcessor/PP_Connext_Crosschain_v1.t.sol @@ -499,14 +499,16 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { └── And clear the failed transfer record └── And emit FailedTransferRetried event */ - function testRetryFailedTransfer_succeeds() public { - // Setup - address recipient = address(0xBEEF); - uint amount = 1 ether; + function testRetryFailedTransfer_succeeds( + address testRecipient, + uint testAmount + ) public { + vm.assume(testRecipient != address(0)); + vm.assume(testAmount > 0 && testAmount < MINTED_SUPPLY); // Setup initial payment IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = - _setupSinglePayment(recipient, amount); + _setupSinglePayment(testRecipient, testAmount); // Store the initial execution data that will fail bytes memory failingExecutionData = abi.encode(333, 1); // maxFee of 333 will cause failure @@ -520,17 +522,17 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { assertEq( paymentProcessor.failedTransfers( address(paymentClient), - recipient, + testRecipient, failingExecutionData // Use the same execution data that was used in processPayments ), - orders[0].amount + testAmount ); // Now retry with proper execution data vm.prank(address(paymentClient)); paymentProcessor.retryFailedTransfer( address(paymentClient), - recipient, + testRecipient, failingExecutionData, // Old execution data that failed executionData, // New execution data for retry orders[0] @@ -541,7 +543,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { assertEq( paymentProcessor.failedTransfers( address(paymentClient), - recipient, + testRecipient, failingExecutionData // Check using the original failing execution data ), 0 @@ -549,7 +551,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { // 2. New intent was created (should be non-zero) bytes32 newIntentId = paymentProcessor.processedIntentId( - address(paymentClient), recipient + address(paymentClient), testRecipient ); assertTrue(newIntentId != bytes32(0)); } @@ -656,14 +658,16 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { └── And the intent ID should remain unchanged └── And the payment order should remain processed */ - function testCancelTransfer_revertsAfterProcessing() public { - // Setup - address recipient = address(0xBEEF); - uint amount = 1 ether; + function testCancelTransfer_revertsAfterProcessing( + address testRecipient, + uint testAmount + ) public { + vm.assume(testRecipient != address(0)); + vm.assume(testAmount > 0 && testAmount < MINTED_SUPPLY); // Setup initial payment and process it IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = - _setupSinglePayment(recipient, amount); + _setupSinglePayment(testRecipient, testAmount); paymentProcessor.processPayments( IERC20PaymentClientBase_v1(address(paymentClient)), executionData @@ -675,7 +679,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { ICrossChainBase_v1.Module__CrossChainBase__InvalidAmount.selector ); paymentProcessor.cancelTransfer( - address(paymentClient), recipient, executionData, orders[0] + address(paymentClient), testRecipient, executionData, orders[0] ); } @@ -802,13 +806,17 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { └── When attempting to process payment └── Then it should revert with InvalidTokenApproval */ - function testProcessPayments_revertsWithoutTokenApproval() public { + function testProcessPayments_revertsWithoutTokenApproval( + address testRecipient, + uint testAmount + ) public { + vm.assume(testRecipient != address(0)); + vm.assume(testAmount > 0 && testAmount < MINTED_SUPPLY); + // Reset approval _token.approve(address(paymentProcessor), 0); - address recipient = address(0xBEEF); - uint amount = 1 ether; - _setupSinglePayment(recipient, amount); + _setupSinglePayment(testRecipient, testAmount); // Expect revert for insufficient allowance vm.expectRevert( @@ -816,7 +824,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { IERC20Errors.ERC20InsufficientAllowance.selector, address(paymentProcessor), 0, - amount + testAmount ) ); paymentProcessor.processPayments( @@ -824,6 +832,38 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { ); } + /* Test payment processing with unsupported token + └── Given a payment order with an unsupported token + └── When attempting to process payment + └── Then it should revert with UnsupportedToken + */ + function testProcessPayments_revertsWithUnsupportedToken( + address testRecipient, + uint testAmount + ) public { + vm.assume(testRecipient != address(0)); + vm.assume(testAmount > 0 && testAmount < MINTED_SUPPLY); + + // Setup payment with unsupported token + IERC20PaymentClientBase_v1.PaymentOrder memory order = + IERC20PaymentClientBase_v1.PaymentOrder({ + recipient: testRecipient, + paymentToken: address(0xDEADBEEF), // Unsupported token address + amount: testAmount, + start: block.timestamp, + cliff: 0, + end: block.timestamp + 1 days + }); + + paymentClient.addPaymentOrder(order); + + // Expect revert due to unsupported token + vm.expectRevert(); + paymentProcessor.processPayments( + IERC20PaymentClientBase_v1(address(paymentClient)), executionData + ); + } + /* Test payment processing with zero token balance └── Given payment processor has zero token balance └── When processing payments @@ -860,19 +900,19 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { └── And track total amounts correctly */ function testProcessPayments_handlesMultipleDuplicateRecipients( - address recipient, - uint amount + address testRecipient, + uint testAmount ) public { - vm.assume(recipient != address(0)); - vm.assume(amount > 0 && amount <= MINTED_SUPPLY / 3); + vm.assume(testRecipient != address(0)); + vm.assume(testAmount > 0 && testAmount <= MINTED_SUPPLY / 3); // Create multiple orders for same recipient address[] memory recipients = new address[](3); uint[] memory amounts = new uint[](3); for (uint i = 0; i < 3; i++) { - recipients[i] = recipient; - amounts[i] = amount; + recipients[i] = testRecipient; + amounts[i] = testAmount; } IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = @@ -885,7 +925,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { // Verify final intent ID exists bytes32 finalIntentId = paymentProcessor.processedIntentId( - address(paymentClient), recipient + address(paymentClient), testRecipient ); assertTrue(finalIntentId != bytes32(0)); From 3e765245ed88fbc0288c2b2dab18407043ec8e2e Mon Sep 17 00:00:00 2001 From: Zuhaib Mohammed Date: Fri, 17 Jan 2025 19:26:09 +0530 Subject: [PATCH 86/93] fix: process payment if else --- .../PP_Connext_Crosschain_v1.sol | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol b/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol index d4f62d944..610a23cd7 100644 --- a/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol +++ b/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol @@ -93,17 +93,7 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { bytes memory bridgeData = _executeBridgeTransfer(orders[i], executionData); - if (bytes32(bridgeData) == bytes32(0)) { - // Handle failed transfer - failedTransfers[clientAddress][orders[i].recipient][executionData] - = orders[i].amount; - emit TransferFailed( - clientAddress, - orders[i].recipient, - executionData, - orders[i].amount - ); - } else { + if (bytes32(bridgeData) != bytes32(0)) { // Handle successful transfer _bridgeData[i] = bridgeData; emit PaymentOrderProcessed( @@ -118,8 +108,18 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { _paymentId++; processedIntentId[address(client)][orders[i].recipient] = bytes32(bridgeData); - client.amountPaid(orders[i].paymentToken, orders[i].amount); + } else { + // Handle failed transfer + failedTransfers[clientAddress][orders[i].recipient][executionData] + = orders[i].amount; + emit TransferFailed( + clientAddress, + orders[i].recipient, + executionData, + orders[i].amount + ); } + client.amountPaid(orders[i].paymentToken, orders[i].amount); } } From 3a753d4ffc341a1af988c0db23ea1a5d26de7e4c Mon Sep 17 00:00:00 2001 From: Zuhaib Mohammed Date: Mon, 20 Jan 2025 17:02:58 +0530 Subject: [PATCH 87/93] chore: add unit tests for PP_Crosschain_v1 --- .../PP_Connext_Crosschain_v1.t.sol | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) diff --git a/test/modules/paymentProcessor/PP_Connext_Crosschain_v1.t.sol b/test/modules/paymentProcessor/PP_Connext_Crosschain_v1.t.sol index f3fd70ea3..3512099d4 100644 --- a/test/modules/paymentProcessor/PP_Connext_Crosschain_v1.t.sol +++ b/test/modules/paymentProcessor/PP_Connext_Crosschain_v1.t.sol @@ -144,6 +144,88 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { //-------------------------------------------------------------------------- // Payment Processing Tests + function test_verifyValidPaymentOrder( + address testRecipient, + uint testAmount + ) public { + vm.assume(testRecipient != address(0)); + vm.assume(testAmount > 0 && testAmount < MINTED_SUPPLY); // Keeping within our minted balance + + address[] memory setupRecipients = new address[](1); + setupRecipients[0] = testRecipient; + uint[] memory setupAmounts = new uint[](1); + setupAmounts[0] = testAmount; + + IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = + new IERC20PaymentClientBase_v1.PaymentOrder[](1); + + //1. Test invalid recipient + orders[0] = IERC20PaymentClientBase_v1.PaymentOrder({ + recipient: address(0), + paymentToken: address(_token), + amount: setupAmounts[0], + start: block.timestamp, + cliff: 0, + end: block.timestamp + 1 days + }); + assertEq(paymentProcessor.validPaymentOrder(orders[0]), false); + + //2. Test invalid token + orders[0] = IERC20PaymentClientBase_v1.PaymentOrder({ + recipient: setupRecipients[0], + paymentToken: address(0), + amount: setupAmounts[0], + start: block.timestamp, + cliff: 0, + end: block.timestamp + 1 days + }); + assertEq(paymentProcessor.validPaymentOrder(orders[0]), false); + + //3. Test invalid amount + orders[0] = IERC20PaymentClientBase_v1.PaymentOrder({ + recipient: setupRecipients[0], + paymentToken: address(_token), + amount: 0, + start: block.timestamp, + cliff: 0, + end: block.timestamp + 1 days + }); + assertEq(paymentProcessor.validPaymentOrder(orders[0]), false); + + //4. Test invalid start + orders[0] = IERC20PaymentClientBase_v1.PaymentOrder({ + recipient: setupRecipients[0], + paymentToken: address(_token), + amount: setupAmounts[0], + start: block.timestamp + 1 days, + cliff: 0, + end: block.timestamp + }); + assertEq(paymentProcessor.validPaymentOrder(orders[0]), false); + + //5. Test invalid cliff + orders[0] = IERC20PaymentClientBase_v1.PaymentOrder({ + recipient: setupRecipients[0], + paymentToken: address(_token), + amount: setupAmounts[0], + start: block.timestamp, + cliff: 1 days, + end: block.timestamp - 1 days + }); + assertEq(paymentProcessor.validPaymentOrder(orders[0]), false); + + //6. Test Valid scenario + orders[0] = IERC20PaymentClientBase_v1.PaymentOrder({ + recipient: setupRecipients[0], + paymentToken: address(_token), + amount: setupAmounts[0], + start: block.timestamp, + cliff: 0, + end: block.timestamp + 1 days + }); + assertEq(paymentProcessor.validPaymentOrder(orders[0]), true); + } + /* Test single payment processing └── Given single valid payment order └── When processing cross-chain payments @@ -933,6 +1015,31 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { // assertEq(uint(everclearPaymentMock.amount(finalIntentId)), amount * 3); } + function testProcessPayments_revertsWithExpiredEndDate( + address testRecipient, + uint testAmount + ) public { + vm.assume(testRecipient != address(0)); + vm.assume(testAmount > 0 && testAmount < MINTED_SUPPLY); + + // Setup payment with expired end date + IERC20PaymentClientBase_v1.PaymentOrder memory order = + IERC20PaymentClientBase_v1.PaymentOrder({ + recipient: testRecipient, + paymentToken: address(_token), + amount: testAmount, + start: block.timestamp - 2 days, + cliff: 0, //@note 33audits -> shouldnt this revert since start and end time are in the past? + end: block.timestamp - 1 days // End date in the past + }); + + paymentClient.addPaymentOrder(order); + + paymentProcessor.processPayments( + IERC20PaymentClientBase_v1(address(paymentClient)), executionData + ); + } + //-------------------------------------------------------------------------- // Helper Functions From 7adcca763e23f030b2692dc6e77eafe9036e4798 Mon Sep 17 00:00:00 2001 From: Zuhaib Mohammed Date: Thu, 23 Jan 2025 11:50:09 +0530 Subject: [PATCH 88/93] fix: add auth checks and retrypayment issue and format unit tests --- .../PP_Connext_Crosschain_v1.sol | 30 +- .../PP_Connext_Crosschain_v1.t.sol | 354 ++++++++++-------- .../PP_Connext_Crosschain_v1_Exposed.sol | 2 +- 3 files changed, 215 insertions(+), 171 deletions(-) diff --git a/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol b/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol index 610a23cd7..5e5d97127 100644 --- a/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol +++ b/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol @@ -163,12 +163,13 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { ) external { _validateTransferRequest(client, recipient, executionData); - bytes32 newIntentId = _createCrossChainIntent(order, newExecutionData); + bytes32 newIntentId = + _createCrossChainIntent(order, newExecutionData, false); if (newIntentId == bytes32(0)) { revert Module__PP_Crosschain__MessageDeliveryFailed( 8453, 8453, executionData ); - } + } //@note -> verify authentication checks _cleanupFailedTransfer(client, recipient, executionData); processedIntentId[client][recipient] = newIntentId; @@ -200,7 +201,7 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { IERC20PaymentClientBase_v1.PaymentOrder memory order, bytes memory executionData ) internal override returns (bytes memory) { - bytes32 _intentId = _createCrossChainIntent(order, executionData); + bytes32 _intentId = _createCrossChainIntent(order, executionData, true); return abi.encode(_intentId); } @@ -208,11 +209,13 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { * @dev Creates a new cross-chain intent for payment transfer * @param order The payment order details * @param executionData Additional execution parameters + * @param transferFromRecipient Whether to transfer from the recipient * @return The ID of the created intent */ function _createCrossChainIntent( IERC20PaymentClientBase_v1.PaymentOrder memory order, - bytes memory executionData + bytes memory executionData, + bool transferFromRecipient ) internal returns (bytes32) { _validateOrder(order); @@ -227,13 +230,14 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { IPP_Connext_Crosschain_v1 .Module__PP_Connext_Crosschain__InvalidTTL(); } - - IERC20(order.paymentToken).transferFrom( - msg.sender, address(this), order.amount - ); - IERC20(order.paymentToken).approve( - address(everClearSpoke), order.amount - ); + if (transferFromRecipient) { + IERC20(order.paymentToken).transferFrom( + order.recipient, address(this), order.amount + ); + IERC20(order.paymentToken).approve( + address(everClearSpoke), order.amount + ); + } uint32[] memory destinations = new uint32[](1); destinations[0] = 8453; @@ -262,9 +266,9 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { bytes memory executionData ) internal view returns (uint) { //msg.sender should be the client - if (msg.sender != client) { + if (msg.sender != recipient) { revert Module__InvalidAddress(); - } + } //@note -> should the msg.sender be the recipient, since they are the one who is trying to retry or cancel the payment isntead of paymentClient? //failedAmount should be stored if the transfer has failed uint failedAmount = failedTransfers[client][recipient][executionData]; if (failedAmount == 0) { diff --git a/test/modules/paymentProcessor/PP_Connext_Crosschain_v1.t.sol b/test/modules/paymentProcessor/PP_Connext_Crosschain_v1.t.sol index 3512099d4..09e7d63c3 100644 --- a/test/modules/paymentProcessor/PP_Connext_Crosschain_v1.t.sol +++ b/test/modules/paymentProcessor/PP_Connext_Crosschain_v1.t.sol @@ -144,88 +144,6 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { //-------------------------------------------------------------------------- // Payment Processing Tests - function test_verifyValidPaymentOrder( - address testRecipient, - uint testAmount - ) public { - vm.assume(testRecipient != address(0)); - vm.assume(testAmount > 0 && testAmount < MINTED_SUPPLY); // Keeping within our minted balance - - address[] memory setupRecipients = new address[](1); - setupRecipients[0] = testRecipient; - uint[] memory setupAmounts = new uint[](1); - setupAmounts[0] = testAmount; - - IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = - new IERC20PaymentClientBase_v1.PaymentOrder[](1); - - //1. Test invalid recipient - orders[0] = IERC20PaymentClientBase_v1.PaymentOrder({ - recipient: address(0), - paymentToken: address(_token), - amount: setupAmounts[0], - start: block.timestamp, - cliff: 0, - end: block.timestamp + 1 days - }); - assertEq(paymentProcessor.validPaymentOrder(orders[0]), false); - - //2. Test invalid token - orders[0] = IERC20PaymentClientBase_v1.PaymentOrder({ - recipient: setupRecipients[0], - paymentToken: address(0), - amount: setupAmounts[0], - start: block.timestamp, - cliff: 0, - end: block.timestamp + 1 days - }); - assertEq(paymentProcessor.validPaymentOrder(orders[0]), false); - - //3. Test invalid amount - orders[0] = IERC20PaymentClientBase_v1.PaymentOrder({ - recipient: setupRecipients[0], - paymentToken: address(_token), - amount: 0, - start: block.timestamp, - cliff: 0, - end: block.timestamp + 1 days - }); - assertEq(paymentProcessor.validPaymentOrder(orders[0]), false); - - //4. Test invalid start - orders[0] = IERC20PaymentClientBase_v1.PaymentOrder({ - recipient: setupRecipients[0], - paymentToken: address(_token), - amount: setupAmounts[0], - start: block.timestamp + 1 days, - cliff: 0, - end: block.timestamp - }); - assertEq(paymentProcessor.validPaymentOrder(orders[0]), false); - - //5. Test invalid cliff - orders[0] = IERC20PaymentClientBase_v1.PaymentOrder({ - recipient: setupRecipients[0], - paymentToken: address(_token), - amount: setupAmounts[0], - start: block.timestamp, - cliff: 1 days, - end: block.timestamp - 1 days - }); - assertEq(paymentProcessor.validPaymentOrder(orders[0]), false); - - //6. Test Valid scenario - orders[0] = IERC20PaymentClientBase_v1.PaymentOrder({ - recipient: setupRecipients[0], - paymentToken: address(_token), - amount: setupAmounts[0], - start: block.timestamp, - cliff: 0, - end: block.timestamp + 1 days - }); - assertEq(paymentProcessor.validPaymentOrder(orders[0]), true); - } - /* Test single payment processing └── Given single valid payment order └── When processing cross-chain payments @@ -244,7 +162,6 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { // Get the client interface IERC20PaymentClientBase_v1 client = IERC20PaymentClientBase_v1(address(paymentClient)); - // Expect the event vm.expectEmit(true, true, true, true); emit IPaymentProcessor_v1.PaymentOrderProcessed( @@ -258,7 +175,14 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { ); // Process payments + + uint balanceBefore = _token.balanceOf(address(paymentProcessor)); paymentProcessor.processPayments(client, executionData); + assertEq(_token.balanceOf(address(testRecipient)), 0); + + uint balanceAfter = _token.balanceOf(address(paymentProcessor)); + assertEq(balanceAfter, balanceBefore + testAmount); + bytes32 intentId = paymentProcessor.processedIntentId( address(paymentClient), testRecipient ); @@ -277,32 +201,25 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { */ function testPublicProcessPayments_succeedsGivenMultipleValidPaymentOrders( uint8 numRecipients, - address testRecipient, - uint baseAmount + uint testAmount ) public { - // Assumptions to keep the test manageable and within bounds vm.assume(numRecipients > 0 && numRecipients <= 10); - vm.assume(testRecipient != address(0)); - - // Just make sure baseAmount * numRecipients doesn't exceed MINTED_SUPPLY - vm.assume( - baseAmount > 0 && baseAmount <= MINTED_SUPPLY / (numRecipients * 2) - ); + vm.assume(testAmount > 0 && testAmount < MINTED_SUPPLY); // Setup mock payment orders address[] memory setupRecipients = new address[](numRecipients); uint[] memory setupAmounts = new uint[](numRecipients); for (uint i = 0; i < numRecipients; i++) { - setupRecipients[i] = testRecipient; - // Simple amount calculation without any Math.min nonsense + setupRecipients[i] = address( + uint160(uint(keccak256(abi.encodePacked(i, block.timestamp)))) + ); setupAmounts[i] = - 1 + (uint(keccak256(abi.encode(i, baseAmount))) % baseAmount); + 1 + (uint64(uint(keccak256(abi.encode(i, testAmount))))); } IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = _createPaymentOrders(numRecipients, setupRecipients, setupAmounts); - paymentClient.addPaymentOrders(orders); // Get the client interface IERC20PaymentClientBase_v1 client = @@ -324,16 +241,17 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { // Process payments paymentProcessor.processPayments(client, executionData); - //should be checking in the mock for valid bridge data - for (uint i = 0; i < numRecipients; i++) { - bytes32 intentId = paymentProcessor.processedIntentId( - address(paymentClient), setupRecipients[i] - ); - assertEq( - uint(everclearPaymentMock.status(intentId)), - uint(Mock_EverclearPayment.IntentStatus.ADDED) - ); - } + + // //should be checking in the mock for valid bridge data + // for (uint i = 0; i < numRecipients; i++) { + // bytes32 intentId = paymentProcessor.processedIntentId( + // address(paymentClient), setupRecipients[i] + // ); + // assertEq( + // uint(everclearPaymentMock.status(intentId)), + // uint(Mock_EverclearPayment.IntentStatus.ADDED) + // ); + // } } /* Test empty payment processing @@ -514,14 +432,19 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { vm.assume(testAmount > MINTED_SUPPLY && testAmount <= type(uint96).max); _setupSinglePayment(testRecipient, testAmount); + + vm.prank(testRecipient); + _token.transfer(address(0xDEAD), testAmount); + assertEq(_token.balanceOf(address(testRecipient)), 0); + IERC20PaymentClientBase_v1 client = IERC20PaymentClientBase_v1(address(paymentClient)); vm.expectRevert( abi.encodeWithSelector( IERC20Errors.ERC20InsufficientBalance.selector, - address(this), - _token.balanceOf(address(paymentProcessor)), + testRecipient, + _token.balanceOf(testRecipient), testAmount ) ); @@ -547,7 +470,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { uint currentBalance = _token.balanceOf(address(paymentProcessor)); if (currentBalance > 0) { vm.prank(address(paymentProcessor)); - _token.transfer(address(1), currentBalance); + _token.transfer(address(0xDEAD), currentBalance); } // Setup - Mint exact amount needed @@ -611,7 +534,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { ); // Now retry with proper execution data - vm.prank(address(paymentClient)); + vm.prank(address(testRecipient)); paymentProcessor.retryFailedTransfer( address(paymentClient), testRecipient, @@ -645,44 +568,47 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { └── And return funds to recipient └── And emit TransferCancelled event */ - function testCancelTransfer_succeeds() public { + function testCancelTransfer_succeeds(address testRecipient, uint testAmount) + public + { // Setup - address recipient = address(0xBEEF); - - uint amount = 1 ether; + vm.assume(testRecipient != address(0)); + vm.assume(testAmount > 0 && testAmount < MINTED_SUPPLY); // Setup the payment and process it IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = - _setupSinglePayment(recipient, amount); - paymentClient.addPaymentOrder(orders[0]); + _setupSinglePayment(testRecipient, testAmount); + bytes memory executionData = abi.encode(333, 1); //call processPayments with maxFee = 333 paymentProcessor.processPayments( IERC20PaymentClientBase_v1(address(paymentClient)), executionData ); // see if failed failedTransfers updates + //@note -->make sure amountPaid Increases assertEq( paymentProcessor.failedTransfers( - address(paymentClient), recipient, executionData + address(paymentClient), testRecipient, executionData ), orders[0].amount ); uint failedAmount = paymentProcessor.failedTransfers( - address(paymentClient), recipient, executionData + address(paymentClient), testRecipient, executionData ); assertEq(failedAmount, orders[0].amount); // Cancel as recipient - vm.prank(address(paymentClient)); + vm.prank(address(testRecipient)); paymentProcessor.cancelTransfer( - address(paymentClient), recipient, executionData, orders[0] + address(paymentClient), testRecipient, executionData, orders[0] ); + //@note -->make sure amountPaid decreases // Verify intentId was cleared assertEq( paymentProcessor.processedIntentId( - address(paymentClient), recipient + address(paymentClient), testRecipient ), bytes32(0) ); @@ -756,7 +682,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { ); // Cancel the transfer - vm.prank(address(paymentClient)); + vm.prank(address(testRecipient)); vm.expectRevert( ICrossChainBase_v1.Module__CrossChainBase__InvalidAmount.selector ); @@ -837,7 +763,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = _setupSinglePayment(testRecipient, testAmount); - vm.prank(address(paymentClient)); + vm.prank(testRecipient); vm.expectRevert( ICrossChainBase_v1.Module__CrossChainBase__InvalidAmount.selector ); @@ -895,11 +821,12 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { vm.assume(testRecipient != address(0)); vm.assume(testAmount > 0 && testAmount < MINTED_SUPPLY); + _setupSinglePayment(testRecipient, testAmount); + // Reset approval + vm.prank(testRecipient); _token.approve(address(paymentProcessor), 0); - _setupSinglePayment(testRecipient, testAmount); - // Expect revert for insufficient allowance vm.expectRevert( abi.encodeWithSelector( @@ -912,6 +839,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { paymentProcessor.processPayments( IERC20PaymentClientBase_v1(address(paymentClient)), executionData ); + //@note -> assertion amount Paid in client is increased } /* Test payment processing with unsupported token @@ -951,24 +879,17 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { └── When processing payments └── Then it should revert with ERC20InsufficientBalance */ - function testProcessPayments_revertsWithZeroBalance( - address testRecipient, - address clearAddress, - uint testAmount - ) public { + function testProcessPayments_revertsWithZeroBalance(address testRecipient) + public + { vm.assume(testRecipient != address(0)); - vm.assume(testAmount > 0 && testAmount < MINTED_SUPPLY); - vm.assume(clearAddress != address(0)); - // Clear processor balance - vm.prank(address(paymentProcessor)); - assertGt(_token.balanceOf(address(paymentProcessor)), ZERO_AMOUNT); - _token.transfer( - clearAddress, _token.balanceOf(address(paymentProcessor)) - ); - _setupSinglePayment(testRecipient, testAmount); + _setupSinglePayment(testRecipient, ZERO_AMOUNT); + assertEq(_token.balanceOf(address(testRecipient)), ZERO_AMOUNT); - vm.expectRevert(); + vm.expectRevert( + ICrossChainBase_v1.Module__CrossChainBase__InvalidAmount.selector + ); paymentProcessor.processPayments( IERC20PaymentClientBase_v1(address(paymentClient)), executionData ); @@ -986,7 +907,6 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { uint testAmount ) public { vm.assume(testRecipient != address(0)); - vm.assume(testAmount > 0 && testAmount <= MINTED_SUPPLY / 3); // Create multiple orders for same recipient address[] memory recipients = new address[](3); @@ -994,12 +914,16 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { for (uint i = 0; i < 3; i++) { recipients[i] = testRecipient; + vm.assume(testAmount > 0 && testAmount <= MINTED_SUPPLY); amounts[i] = testAmount; } IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = _createPaymentOrders(3, recipients, amounts); + vm.prank(testRecipient); + _token.approve(address(paymentProcessor), type(uint).max); + // Process payments paymentProcessor.processPayments( IERC20PaymentClientBase_v1(address(paymentClient)), executionData @@ -1015,29 +939,140 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { // assertEq(uint(everclearPaymentMock.amount(finalIntentId)), amount * 3); } - function testProcessPayments_revertsWithExpiredEndDate( - address testRecipient, - uint testAmount - ) public { - vm.assume(testRecipient != address(0)); - vm.assume(testAmount > 0 && testAmount < MINTED_SUPPLY); + // function testProcessPayments_revertsWithExpiredEndDate( + // address testRecipient, + // uint testAmount + // ) public { + // vm.assume(testRecipient != address(0)); + // vm.assume(testAmount > 0 && testAmount < MINTED_SUPPLY); + + // // Setup payment with expired end date + // IERC20PaymentClientBase_v1.PaymentOrder memory order = + // IERC20PaymentClientBase_v1.PaymentOrder({ + // recipient: testRecipient, + // paymentToken: address(_token), + // amount: testAmount, + // start: block.timestamp - 2 days, + // cliff: 0, //@note 33audits -> shouldnt this revert since start and end time are in the past? + // end: block.timestamp - 1 days // End date in the past + // }); + + // paymentClient.addPaymentOrder(order); + + // paymentProcessor.processPayments( + // IERC20PaymentClientBase_v1(address(paymentClient)), executionData + // ); + // } + + //-------------------------------------------------------------------------- + // Payment Order Validation Tests - // Setup payment with expired end date + /* Test invalid recipient + └── Given a payment order with address(0) recipient + └── When validating the payment order + └── Then it should return false + */ + function testvalidPaymentOrder_InvalidRecipient() public { IERC20PaymentClientBase_v1.PaymentOrder memory order = IERC20PaymentClientBase_v1.PaymentOrder({ - recipient: testRecipient, + recipient: address(0), paymentToken: address(_token), - amount: testAmount, - start: block.timestamp - 2 days, - cliff: 0, //@note 33audits -> shouldnt this revert since start and end time are in the past? - end: block.timestamp - 1 days // End date in the past + amount: 1, + start: block.timestamp, + cliff: 0, + end: block.timestamp + 1 days }); + assertEq(paymentProcessor.validPaymentOrder(order), false); + } - paymentClient.addPaymentOrder(order); + /* Test invalid token + └── Given a payment order with address(0) token + └── When validating the payment order + └── Then it should return false + */ + function testvalidPaymentOrder_InvalidToken() public { + IERC20PaymentClientBase_v1.PaymentOrder memory order = + IERC20PaymentClientBase_v1.PaymentOrder({ + recipient: address(0xBEEF), + paymentToken: address(0), + amount: 1, + start: block.timestamp, + cliff: 0, + end: block.timestamp + 1 days + }); + assertEq(paymentProcessor.validPaymentOrder(order), false); + } - paymentProcessor.processPayments( - IERC20PaymentClientBase_v1(address(paymentClient)), executionData - ); + /* Test invalid amount + └── Given a payment order with zero amount + └── When validating the payment order + └── Then it should return false + */ + function testvalidPaymentOrder_InvalidAmount() public { + IERC20PaymentClientBase_v1.PaymentOrder memory order = + IERC20PaymentClientBase_v1.PaymentOrder({ + recipient: address(0xBEEF), + paymentToken: address(_token), + amount: 0, + start: block.timestamp, + cliff: 0, + end: block.timestamp + 1 days + }); + assertEq(paymentProcessor.validPaymentOrder(order), false); + } + + /* Test invalid start + └── Given a payment order with start time after end time + └── When validating the payment order + └── Then it should return false + */ + function testvalidPaymentOrder_InvalidStart() public { + IERC20PaymentClientBase_v1.PaymentOrder memory order = + IERC20PaymentClientBase_v1.PaymentOrder({ + recipient: address(0xBEEF), + paymentToken: address(_token), + amount: 1, + start: block.timestamp + 1 days, + cliff: 0, + end: block.timestamp + }); + assertEq(paymentProcessor.validPaymentOrder(order), false); + } + + /* Test invalid cliff + └── Given a payment order with cliff time after end time + └── When validating the payment order + └── Then it should return false + */ + function testvalidPaymentOrder_InvalidCliff() public { + IERC20PaymentClientBase_v1.PaymentOrder memory order = + IERC20PaymentClientBase_v1.PaymentOrder({ + recipient: address(0xBEEF), + paymentToken: address(_token), + amount: 1, + start: block.timestamp, + cliff: 1 days, + end: block.timestamp - 1 days + }); + assertEq(paymentProcessor.validPaymentOrder(order), false); + } + + /* Test valid payment order + └── Given a valid payment order + └── When validating the payment order + └── Then it should return true + */ + function testvalidPaymentOrder_Success() public { + IERC20PaymentClientBase_v1.PaymentOrder memory order = + IERC20PaymentClientBase_v1.PaymentOrder({ + recipient: address(0xBEEF), + paymentToken: address(_token), + amount: 1, + start: block.timestamp, + cliff: 0, + end: block.timestamp + 1 days + }); + assertEq(paymentProcessor.validPaymentOrder(order), true); } //-------------------------------------------------------------------------- @@ -1072,6 +1107,11 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { //add payment order to client for (uint i = 0; i < orderCount; i++) { + if (recipients[i] != address(0)) { + _token.mint(recipients[i], amounts[i]); + vm.prank(recipients[i]); + _token.approve(address(paymentProcessor), amounts[i]); + } orders[i] = IERC20PaymentClientBase_v1.PaymentOrder({ recipient: recipients[i], paymentToken: address(_token), diff --git a/test/utils/mocks/modules/paymentProcessor/PP_Connext_Crosschain_v1_Exposed.sol b/test/utils/mocks/modules/paymentProcessor/PP_Connext_Crosschain_v1_Exposed.sol index 1ee23cc0b..bac9b70d4 100644 --- a/test/utils/mocks/modules/paymentProcessor/PP_Connext_Crosschain_v1_Exposed.sol +++ b/test/utils/mocks/modules/paymentProcessor/PP_Connext_Crosschain_v1_Exposed.sol @@ -20,7 +20,7 @@ contract PP_Connext_Crosschain_v1_Exposed is PP_Connext_Crosschain_v1 { IERC20PaymentClientBase_v1.PaymentOrder memory order, bytes memory executionData ) external returns (bytes32) { - return _createCrossChainIntent(order, executionData); + return _createCrossChainIntent(order, executionData, false); } function exposed_setFailedTransfer( From 238254c2557d50388488b9dd42c37e5d42eb9970 Mon Sep 17 00:00:00 2001 From: Zuhaib Mohammed Date: Fri, 24 Jan 2025 15:05:55 +0530 Subject: [PATCH 89/93] fix: change test to fuzz tests, fix minor bugs --- .../PP_Connext_Crosschain_v1.sol | 2 +- .../PP_Connext_Crosschain_v1.t.sol | 255 ++++++++++++------ 2 files changed, 180 insertions(+), 77 deletions(-) diff --git a/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol b/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol index 5e5d97127..d1744ff98 100644 --- a/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol +++ b/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol @@ -169,7 +169,7 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { revert Module__PP_Crosschain__MessageDeliveryFailed( 8453, 8453, executionData ); - } //@note -> verify authentication checks + } _cleanupFailedTransfer(client, recipient, executionData); processedIntentId[client][recipient] = newIntentId; diff --git a/test/modules/paymentProcessor/PP_Connext_Crosschain_v1.t.sol b/test/modules/paymentProcessor/PP_Connext_Crosschain_v1.t.sol index 09e7d63c3..9288fab9f 100644 --- a/test/modules/paymentProcessor/PP_Connext_Crosschain_v1.t.sol +++ b/test/modules/paymentProcessor/PP_Connext_Crosschain_v1.t.sol @@ -1,10 +1,10 @@ -// External Dependencies // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.20; //-------------------------------------------------------------------------- // Imports +// External Dependencies import {Test} from "forge-std/Test.sol"; import {Clones} from "@oz/proxy/Clones.sol"; @@ -150,7 +150,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { └── Then it should emit PaymentProcessed events for payment └── And it should create cross-chain intent */ - function testPublicProcessPayments_succeedsGivenSingleValidPaymentOrder( + function testFuzz_PublicProcessPayments_succeedsGivenSingleValidPaymentOrder( address testRecipient, uint testAmount ) public { @@ -175,7 +175,6 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { ); // Process payments - uint balanceBefore = _token.balanceOf(address(paymentProcessor)); paymentProcessor.processPayments(client, executionData); assertEq(_token.balanceOf(address(testRecipient)), 0); @@ -199,7 +198,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { ├── And it should create multiple cross-chain intents └── And it should contain valid intent IDs */ - function testPublicProcessPayments_succeedsGivenMultipleValidPaymentOrders( + function testFuzz_PublicProcessPayments_succeedsGivenMultipleValidPaymentOrders( uint8 numRecipients, uint testAmount ) public { @@ -242,16 +241,16 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { // Process payments paymentProcessor.processPayments(client, executionData); - // //should be checking in the mock for valid bridge data - // for (uint i = 0; i < numRecipients; i++) { - // bytes32 intentId = paymentProcessor.processedIntentId( - // address(paymentClient), setupRecipients[i] - // ); - // assertEq( - // uint(everclearPaymentMock.status(intentId)), - // uint(Mock_EverclearPayment.IntentStatus.ADDED) - // ); - // } + //should be checking in the mock for valid bridge data + for (uint i = 0; i < numRecipients; i++) { + bytes32 intentId = paymentProcessor.processedIntentId( + address(paymentClient), setupRecipients[i] + ); + assertEq( + uint(everclearPaymentMock.status(intentId)), + uint(Mock_EverclearPayment.IntentStatus.ADDED) + ); + } } /* Test empty payment processing @@ -259,7 +258,9 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { ├── Then it should complete successfully └── And the bridge data should remain empty */ - function testPublicProcessPayments_succeedsGivenNoPaymentOrders() public { + function testFuzz_PublicProcessPayments_succeedsGivenNoPaymentOrders() + public + { // Process payments and verify _bridgeData mapping is not updated paymentProcessor.processPayments( IERC20PaymentClientBase_v1(address(paymentClient)), executionData @@ -283,7 +284,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { └── When processing with invalid Connext parameters └── Then it should revert */ - function testPublicProcessPayments_revertsGivenInvalidExecutionData( + function testFuzz_PublicProcessPayments_revertsGivenInvalidExecutionData( address testRecipient, uint testAmount ) public { @@ -306,7 +307,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { │ └── When attempting to process payment │ └── Then it should revert with InvalidExecutionData */ - function testPublicProcessPayments_revertsGivenEmptyExecutionData( + function testFuzz_PublicProcessPayments_revertsGivenEmptyExecutionData( address testRecipient, uint testAmount ) public { @@ -333,7 +334,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { │ └── When attempting to process payment │ └── Then it should revert with InvalidRecipient */ - function testPublicProcessPayments_revertsGivenInvalidRecipient( + function testFuzz_PublicProcessPayments_revertsGivenInvalidRecipient( uint testAmount ) public { vm.assume(testAmount > 0 && testAmount < MINTED_SUPPLY); // Keeping within our minted balance @@ -355,7 +356,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { │ └── When attempting to process payment │ └── Then it should revert with InvalidAmount */ - function testPublicProcessPayments_revertsGivenInvalidAmount( + function testFuzz_PublicProcessPayments_revertsGivenInvalidAmount( address testRecipient ) public { vm.assume(testRecipient != address(0)); @@ -379,7 +380,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { │ └── And intent ID should be stored correctly └── And intent status should be ADDED in Everclear spoke */ - function testPublicProcessPayments_worksGivenCorrectBridgeData( + function testFuzz_PublicProcessPayments_worksGivenCorrectBridgeData( address testRecipient, uint testAmount ) public { @@ -408,7 +409,9 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { ── When checking bridge data with no added payments └── Then it should return empty bytes */ - function testPublicProcessPayments_worksGivenEmptyBridgeData() public { + function testFuzz_PublicProcessPayments_worksGivenEmptyBridgeData() + public + { IERC20PaymentClientBase_v1 client = IERC20PaymentClientBase_v1(address(paymentClient)); // Process payments and verify _bridgeData mapping is updated @@ -424,7 +427,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { └── When attempting to process payment └── Then it should revert with ERC20InsufficientBalance */ - function testPublicProcessPayments_revertsGivenInsufficientBalance( + function testFuzz_PublicProcessPayments_revertsGivenInsufficientBalance( address testRecipient, uint testAmount ) public { @@ -458,7 +461,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { └── And should emit PaymentProcessed event └── And should handle exact balance correctly */ - function testPublicProcessPayments_worksGivenEdgeCaseAmounts( + function testFuzz_PublicProcessPayments_worksGivenEdgeCaseAmounts( address testRecipient, uint96 testAmount ) public { @@ -504,7 +507,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { └── And clear the failed transfer record └── And emit FailedTransferRetried event */ - function testRetryFailedTransfer_succeeds( + function testFuzz_RetryFailedTransfer_succeeds( address testRecipient, uint testAmount ) public { @@ -568,9 +571,10 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { └── And return funds to recipient └── And emit TransferCancelled event */ - function testCancelTransfer_succeeds(address testRecipient, uint testAmount) - public - { + function testFuzz_CancelTransfer_succeeds( + address testRecipient, + uint testAmount + ) public { // Setup vm.assume(testRecipient != address(0)); vm.assume(testAmount > 0 && testAmount < MINTED_SUPPLY); @@ -579,13 +583,16 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = _setupSinglePayment(testRecipient, testAmount); + uint balanceBefore = _token.balanceOf(address(paymentProcessor)); bytes memory executionData = abi.encode(333, 1); //call processPayments with maxFee = 333 paymentProcessor.processPayments( IERC20PaymentClientBase_v1(address(paymentClient)), executionData ); + uint balanceAfter = _token.balanceOf(address(paymentProcessor)); + assertEq(balanceAfter, balanceBefore + testAmount); + // see if failed failedTransfers updates - //@note -->make sure amountPaid Increases assertEq( paymentProcessor.failedTransfers( address(paymentClient), testRecipient, executionData @@ -598,12 +605,16 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { ); assertEq(failedAmount, orders[0].amount); + uint balanceBeforeCancel = _token.balanceOf(address(paymentProcessor)); // Cancel as recipient vm.prank(address(testRecipient)); paymentProcessor.cancelTransfer( address(paymentClient), testRecipient, executionData, orders[0] ); - //@note -->make sure amountPaid decreases + uint balanceAfterCancel = _token.balanceOf(address(paymentProcessor)); + console2.log("balanceAfterCancel", balanceAfterCancel); + + assertEq(balanceAfterCancel, balanceBeforeCancel - testAmount); // Verify intentId was cleared assertEq( @@ -619,7 +630,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { └── When cancelled by someone other than recipient └── Then it should revert with InvalidAddress */ - function testCancelTransfer_revertsForNonRecipient( + function testFuzz_CancelTransfer_revertsForNonRecipient( address testRecipient, address nonRecipient, uint testAmount @@ -652,7 +663,6 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { // Prank as non-recipient vm.prank(nonRecipient); - vm.expectRevert(IModule_v1.Module__InvalidAddress.selector); paymentProcessor.cancelTransfer( address(paymentClient), testRecipient, executionData, order @@ -666,7 +676,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { └── And the intent ID should remain unchanged └── And the payment order should remain processed */ - function testCancelTransfer_revertsAfterProcessing( + function testFuzz_CancelTransfer_revertsAfterProcessing( address testRecipient, uint testAmount ) public { @@ -696,7 +706,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { └── When processing payments └── Then it should revert with InvalidTTL */ - function testProcessPayments_revertsWithZeroTTL( + function testFuzz_ProcessPayments_revertsWithZeroTTL( address testRecipient, uint testAmount ) public { @@ -717,7 +727,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { └── When retrying failed transfer └── Then it should revert with InvalidAddress */ - function testRetryFailedTransfer_revertsWithInvalidCaller( + function testFuzz_RetryFailedTransfer_revertsWithInvalidCaller( address testRecipient, uint testAmount, address invalidCaller @@ -753,7 +763,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { └── When retrying transfer └── Then it should revert with InvalidAmount */ - function testRetryFailedTransfer_revertsWithNoFailedTransfer( + function testFuzz_RetryFailedTransfer_revertsWithNoFailedTransfer( address testRecipient, uint testAmount ) public { @@ -781,7 +791,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { └── When retrying transfer └── Then it should revert with InvalidIntentId */ - function testRetryFailedTransfer_revertsWithExistingIntent( + function testFuzz_RetryFailedTransfer_revertsWithExistingIntent( address testRecipient, uint testAmount ) public { @@ -809,12 +819,54 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { ); } + /* Test retry with invalid execution data + └── Given a retry request with invalid execution data + └── When retrying failed transfer + └── Then it should revert with InvalidExecutionData + */ + function testFuzz_RetryFailedTransferWithInvalidExecutionData( + address testRecipient, + uint testAmount + ) public { + vm.assume(testRecipient != address(0)); + vm.assume(testAmount > 0 && testAmount < MINTED_SUPPLY); + + // Setup failed transfer + IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = + _setupSinglePayment(testRecipient, testAmount); + + bytes memory failingExecutionData = abi.encode(333, 1); + paymentProcessor.processPayments( + IERC20PaymentClientBase_v1(address(paymentClient)), + failingExecutionData + ); + + vm.prank(address(testRecipient)); + vm.expectRevert( + abi.encodeWithSelector( + IPP_Crosschain_v1 + .Module__PP_Crosschain__MessageDeliveryFailed + .selector, + 8453, + 8453, + failingExecutionData + ) + ); + paymentProcessor.retryFailedTransfer( + address(paymentClient), + testRecipient, + failingExecutionData, + failingExecutionData, // use failedExecutionData as newExecutionData + orders[0] + ); + } + /* Test process payments without token approval └── Given a payment order with zero approval └── When attempting to process payment └── Then it should revert with InvalidTokenApproval */ - function testProcessPayments_revertsWithoutTokenApproval( + function testFuzz_ProcessPayments_revertsWithoutTokenApproval( address testRecipient, uint testAmount ) public { @@ -839,7 +891,6 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { paymentProcessor.processPayments( IERC20PaymentClientBase_v1(address(paymentClient)), executionData ); - //@note -> assertion amount Paid in client is increased } /* Test payment processing with unsupported token @@ -847,7 +898,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { └── When attempting to process payment └── Then it should revert with UnsupportedToken */ - function testProcessPayments_revertsWithUnsupportedToken( + function testFuzz_ProcessPayments_revertsWithUnsupportedToken( address testRecipient, uint testAmount ) public { @@ -879,9 +930,9 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { └── When processing payments └── Then it should revert with ERC20InsufficientBalance */ - function testProcessPayments_revertsWithZeroBalance(address testRecipient) - public - { + function testFuzz_ProcessPayments_revertsWithZeroBalance( + address testRecipient + ) public { vm.assume(testRecipient != address(0)); _setupSinglePayment(testRecipient, ZERO_AMOUNT); @@ -902,7 +953,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { └── And update intent IDs properly └── And track total amounts correctly */ - function testProcessPayments_handlesMultipleDuplicateRecipients( + function testFuzz_ProcessPayments_handlesMultipleDuplicateRecipients( address testRecipient, uint testAmount ) public { @@ -934,35 +985,86 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { address(paymentClient), testRecipient ); assertTrue(finalIntentId != bytes32(0)); + } + + /* Test payment processing with varying start/end times + └── Given payment orders with different time configurations + └── When processing payments + └── Then it should handle valid time ranges + └── And revert for invalid ones + */ + function testFuzz_ProcessPayments_handlesVariableTimeRanges( + address testRecipient, + uint testAmount, + uint32 timeOffset + ) public { + vm.assume(testRecipient != address(0)); + vm.assume(testAmount > 0 && testAmount < MINTED_SUPPLY); + vm.assume(timeOffset > 0 && timeOffset < 30 days); + + // Create payment order with fuzzed time range + IERC20PaymentClientBase_v1.PaymentOrder memory order = + IERC20PaymentClientBase_v1.PaymentOrder({ + recipient: testRecipient, + paymentToken: address(_token), + amount: testAmount, + start: block.timestamp, + cliff: 0, + end: block.timestamp + timeOffset + }); + + _token.mint(testRecipient, testAmount); + vm.prank(testRecipient); + _token.approve(address(paymentProcessor), testAmount); + paymentClient.addPaymentOrder(order); - // // Verify total amount processed - // assertEq(uint(everclearPaymentMock.amount(finalIntentId)), amount * 3); + paymentProcessor.processPayments( + IERC20PaymentClientBase_v1(address(paymentClient)), executionData + ); + + bytes32 intentId = paymentProcessor.processedIntentId( + address(paymentClient), testRecipient + ); + assertTrue(intentId != bytes32(0)); } - // function testProcessPayments_revertsWithExpiredEndDate( - // address testRecipient, - // uint testAmount - // ) public { - // vm.assume(testRecipient != address(0)); - // vm.assume(testAmount > 0 && testAmount < MINTED_SUPPLY); - - // // Setup payment with expired end date - // IERC20PaymentClientBase_v1.PaymentOrder memory order = - // IERC20PaymentClientBase_v1.PaymentOrder({ - // recipient: testRecipient, - // paymentToken: address(_token), - // amount: testAmount, - // start: block.timestamp - 2 days, - // cliff: 0, //@note 33audits -> shouldnt this revert since start and end time are in the past? - // end: block.timestamp - 1 days // End date in the past - // }); - - // paymentClient.addPaymentOrder(order); - - // paymentProcessor.processPayments( - // IERC20PaymentClientBase_v1(address(paymentClient)), executionData - // ); - // } + function testFuzz_ProcessPayments_revertsWithExpiredEndDate( + address testRecipient, + uint testAmount + ) public { + //@audit-issue -> discuss with 33 about this test + vm.assume(testRecipient != address(0)); + vm.assume(testAmount > 0 && testAmount < MINTED_SUPPLY); + + // Setup payment with expired end date + IERC20PaymentClientBase_v1.PaymentOrder memory order = + IERC20PaymentClientBase_v1.PaymentOrder({ + recipient: testRecipient, + paymentToken: address(_token), + amount: testAmount, + start: block.timestamp - 2 days, + cliff: 0, //@note 33audits -> shouldnt this revert since start and end time are in the past? + end: block.timestamp - 1 days // End date in the past + }); + + // Mint tokens to recipient + _token.mint(testRecipient, testAmount); + vm.prank(testRecipient); + _token.approve(address(paymentProcessor), testAmount); + + paymentClient.addPaymentOrder(order); + + // Process payments + uint balanceBefore = _token.balanceOf(address(paymentProcessor)); + paymentProcessor.processPayments( + IERC20PaymentClientBase_v1(address(paymentClient)), executionData + ); + assertEq(_token.balanceOf(testRecipient), 0); + assertEq( + _token.balanceOf(address(paymentProcessor)), + balanceBefore + testAmount + ); + } //-------------------------------------------------------------------------- // Payment Order Validation Tests @@ -972,7 +1074,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { └── When validating the payment order └── Then it should return false */ - function testvalidPaymentOrder_InvalidRecipient() public { + function testFuzz_validPaymentOrder_InvalidRecipient() public { IERC20PaymentClientBase_v1.PaymentOrder memory order = IERC20PaymentClientBase_v1.PaymentOrder({ recipient: address(0), @@ -990,7 +1092,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { └── When validating the payment order └── Then it should return false */ - function testvalidPaymentOrder_InvalidToken() public { + function testFuzz_validPaymentOrder_InvalidToken() public { IERC20PaymentClientBase_v1.PaymentOrder memory order = IERC20PaymentClientBase_v1.PaymentOrder({ recipient: address(0xBEEF), @@ -1008,7 +1110,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { └── When validating the payment order └── Then it should return false */ - function testvalidPaymentOrder_InvalidAmount() public { + function testFuzz_validPaymentOrder_InvalidAmount() public { IERC20PaymentClientBase_v1.PaymentOrder memory order = IERC20PaymentClientBase_v1.PaymentOrder({ recipient: address(0xBEEF), @@ -1026,7 +1128,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { └── When validating the payment order └── Then it should return false */ - function testvalidPaymentOrder_InvalidStart() public { + function testFuzz_validPaymentOrder_InvalidStart() public { IERC20PaymentClientBase_v1.PaymentOrder memory order = IERC20PaymentClientBase_v1.PaymentOrder({ recipient: address(0xBEEF), @@ -1044,7 +1146,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { └── When validating the payment order └── Then it should return false */ - function testvalidPaymentOrder_InvalidCliff() public { + function testFuzz_validPaymentOrder_InvalidCliff() public { IERC20PaymentClientBase_v1.PaymentOrder memory order = IERC20PaymentClientBase_v1.PaymentOrder({ recipient: address(0xBEEF), @@ -1062,7 +1164,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { └── When validating the payment order └── Then it should return true */ - function testvalidPaymentOrder_Success() public { + function testFuzz_validPaymentOrder_Success() public { IERC20PaymentClientBase_v1.PaymentOrder memory order = IERC20PaymentClientBase_v1.PaymentOrder({ recipient: address(0xBEEF), @@ -1104,10 +1206,10 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { ); IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = new IERC20PaymentClientBase_v1.PaymentOrder[](orderCount); - //add payment order to client for (uint i = 0; i < orderCount; i++) { if (recipients[i] != address(0)) { + //mint tokens to recipient & approve payment processor _token.mint(recipients[i], amounts[i]); vm.prank(recipients[i]); _token.approve(address(paymentProcessor), amounts[i]); @@ -1120,6 +1222,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { cliff: 0, end: block.timestamp + 1 days }); + //add payment order to client paymentClient.addPaymentOrder(orders[i]); } return orders; From e7d0660a52ac6273bcb1f4128e6045ce2bfe846c Mon Sep 17 00:00:00 2001 From: Zuhaib Mohammed Date: Fri, 24 Jan 2025 20:23:39 +0530 Subject: [PATCH 90/93] fix: code format invertor standard --- .../PP_Connext_Crosschain_v1.sol | 37 ++++++++---- .../abstracts/PP_Crosschain_v1.sol | 12 +--- .../PP_Connext_Crosschain_v1.t.sol | 59 +++++++++---------- .../abstracts/CrossChainBase_v1.t.sol | 32 ++++++---- 4 files changed, 80 insertions(+), 60 deletions(-) diff --git a/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol b/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol index d1744ff98..979291d90 100644 --- a/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol +++ b/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: MIT +// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; // External Imports @@ -24,15 +24,25 @@ import {IOrchestrator_v1} from /** * @title Connext Cross-chain Payment Processor - * @notice Payment processor implementation for cross-chain payments using the Connext protocol - * @dev This contract implements cross-chain payment processing via Connext and provides: - * - Integration with Connext's EverClear protocol for cross-chain transfers - * - WETH handling for native token wrapping - * - Implementation of bridge-specific transfer logic - * - Payment order processing and validation - * - Bridge data storage and retrieval + * + * @notice Specialized payment processor implementation for handling cross-chain payments via Connext protocol. + * + * @dev This contract extends PP_Crosschain_v1 and provides: + * - Integration with Connext's EverClear protocol for secure cross-chain transfers + * - Native token handling through WETH wrapper + * - Robust payment order processing and validation + * - Failed transfer handling with retry and cancellation mechanisms + * - Bridge-specific transfer logic implementation * - Support for Base network (chainId: 8453) + * - Comprehensive transfer state tracking + * * @custom:security-contact security@inverter.network + * In case of any concerns or findings, please refer to our Security Policy + * at security.inverter.network or email us directly! + * + * @author Inverter Network + * @custom:version 1.0.0 + * @custom:standard-version 1.0.0 */ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { // Storage Variables @@ -57,7 +67,10 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { // Errors error FailedTransfer(); - // External Functions + constructor() { + _disableInitializers(); + } + /** * @notice Initializes the payment processor module * @param orchestrator_ The orchestrator contract address @@ -77,6 +90,7 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { weth = IWETH(weth_); } + // External Mutating Functions /** * @notice Processes multiple payment orders through the bridge * @param client The payment client contract interface @@ -175,7 +189,7 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { processedIntentId[client][recipient] = newIntentId; } - // Public Functions + // View Functions /** * @notice Retrieves the bridge data for a specific payment ID * @param paymentId The unique identifier of the payment @@ -190,7 +204,7 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { return _bridgeData[paymentId]; } - // Internal Functions + // Overridden Internal Functions /** * @dev Execute the cross-chain bridge transfer * @param order The payment order containing transfer details @@ -205,6 +219,7 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { return abi.encode(_intentId); } + // Internal Helper Functions /** * @dev Creates a new cross-chain intent for payment transfer * @param order The payment order details diff --git a/src/modules/paymentProcessor/abstracts/PP_Crosschain_v1.sol b/src/modules/paymentProcessor/abstracts/PP_Crosschain_v1.sol index 03823091e..92c60f172 100644 --- a/src/modules/paymentProcessor/abstracts/PP_Crosschain_v1.sol +++ b/src/modules/paymentProcessor/abstracts/PP_Crosschain_v1.sol @@ -1,27 +1,21 @@ // SPDX-License-Identifier: LGPL-3.0-only pragma solidity 0.8.23; +// Internal Imports import {IOrchestrator_v1} from "src/orchestrator/interfaces/IOrchestrator_v1.sol"; import {IPaymentProcessor_v1} from "@pp/IPaymentProcessor_v1.sol"; import {IERC20PaymentClientBase_v1} from "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; import {ERC165Upgradeable, Module_v1} from "src/modules/base/Module_v1.sol"; - import {CrossChainBase_v1} from "./CrossChainBase_v1.sol"; - -// External Interfaces -import {IERC20} from "@oz/token/ERC20/IERC20.sol"; +import {IPP_Crosschain_v1} from "../interfaces/IPP_Crosschain_v1.sol"; // External Dependencies +import {IERC20} from "@oz/token/ERC20/IERC20.sol"; import {ERC20} from "@oz/token/ERC20/ERC20.sol"; - -// External Libraries import {SafeERC20} from "@oz/token/ERC20/utils/SafeERC20.sol"; -// Internal Interfaces -import {IPP_Crosschain_v1} from "../interfaces/IPP_Crosschain_v1.sol"; - /** * @title Cross-chain Payment Processor Base Contract * diff --git a/test/modules/paymentProcessor/PP_Connext_Crosschain_v1.t.sol b/test/modules/paymentProcessor/PP_Connext_Crosschain_v1.t.sol index 9288fab9f..561b5f01a 100644 --- a/test/modules/paymentProcessor/PP_Connext_Crosschain_v1.t.sol +++ b/test/modules/paymentProcessor/PP_Connext_Crosschain_v1.t.sol @@ -195,8 +195,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { └── Given multiple valid payment orders └── When processing cross-chain payments └── Then it should emit PaymentProcessed events for each payment - ├── And it should create multiple cross-chain intents - └── And it should contain valid intent IDs + └── And it should create multiple cross-chain intents */ function testFuzz_PublicProcessPayments_succeedsGivenMultipleValidPaymentOrders( uint8 numRecipients, @@ -254,9 +253,10 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { } /* Test empty payment processing - └── When processing with no payment orders - ├── Then it should complete successfully - └── And the bridge data should remain empty + └── Given no payment orders + └── When processing payments + └── Then it should complete successfully + └── And bridge data should remain empty */ function testFuzz_PublicProcessPayments_succeedsGivenNoPaymentOrders() public @@ -280,10 +280,6 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { //-------------------------------------------------------------------------- // Error Case Tests - /* Test invalid execution data - └── When processing with invalid Connext parameters - └── Then it should revert - */ function testFuzz_PublicProcessPayments_revertsGivenInvalidExecutionData( address testRecipient, uint testAmount @@ -409,7 +405,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { ── When checking bridge data with no added payments └── Then it should return empty bytes */ - function testFuzz_PublicProcessPayments_worksGivenEmptyBridgeData() + function testFuzz_PublicProcessPayments_succeedsGivenEmptyBridgeData() public { IERC20PaymentClientBase_v1 client = @@ -507,7 +503,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { └── And clear the failed transfer record └── And emit FailedTransferRetried event */ - function testFuzz_RetryFailedTransfer_succeeds( + function testFuzz_PublicRetryFailedTransfer_succeedsGivenValidFailedTransfer( address testRecipient, uint testAmount ) public { @@ -571,7 +567,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { └── And return funds to recipient └── And emit TransferCancelled event */ - function testFuzz_CancelTransfer_succeeds( + function testFuzz_PublicCancelTransfer_succeedsGivenValidPendingTransfer( address testRecipient, uint testAmount ) public { @@ -630,7 +626,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { └── When cancelled by someone other than recipient └── Then it should revert with InvalidAddress */ - function testFuzz_CancelTransfer_revertsForNonRecipient( + function testFuzz_PublicCancelTransfer_revertsGivenNonRecipientCaller( address testRecipient, address nonRecipient, uint testAmount @@ -676,7 +672,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { └── And the intent ID should remain unchanged └── And the payment order should remain processed */ - function testFuzz_CancelTransfer_revertsAfterProcessing( + function testFuzz_PublicCancelTransfer_revertsGivenProcessedTransfer( address testRecipient, uint testAmount ) public { @@ -727,7 +723,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { └── When retrying failed transfer └── Then it should revert with InvalidAddress */ - function testFuzz_RetryFailedTransfer_revertsWithInvalidCaller( + function testFuzz_PublicRetryFailedTransfer_revertsGivenInvalidCaller( address testRecipient, uint testAmount, address invalidCaller @@ -763,7 +759,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { └── When retrying transfer └── Then it should revert with InvalidAmount */ - function testFuzz_RetryFailedTransfer_revertsWithNoFailedTransfer( + function testFuzz_PublicRetryFailedTransfer_revertsGivenNoFailedTransfer( address testRecipient, uint testAmount ) public { @@ -791,7 +787,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { └── When retrying transfer └── Then it should revert with InvalidIntentId */ - function testFuzz_RetryFailedTransfer_revertsWithExistingIntent( + function testFuzz_PublicRetryFailedTransfer_revertsGivenExistingIntent( address testRecipient, uint testAmount ) public { @@ -824,7 +820,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { └── When retrying failed transfer └── Then it should revert with InvalidExecutionData */ - function testFuzz_RetryFailedTransferWithInvalidExecutionData( + function testFuzz_PublicRetryFailedTransfer_revertsGivenInvalidExecutionData( address testRecipient, uint testAmount ) public { @@ -866,7 +862,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { └── When attempting to process payment └── Then it should revert with InvalidTokenApproval */ - function testFuzz_ProcessPayments_revertsWithoutTokenApproval( + function testFuzz_PublicProcessPayments_revertsGivenNoTokenApproval( address testRecipient, uint testAmount ) public { @@ -898,7 +894,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { └── When attempting to process payment └── Then it should revert with UnsupportedToken */ - function testFuzz_ProcessPayments_revertsWithUnsupportedToken( + function testFuzz_PublicProcessPayments_revertsGivenUnsupportedToken( address testRecipient, uint testAmount ) public { @@ -930,7 +926,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { └── When processing payments └── Then it should revert with ERC20InsufficientBalance */ - function testFuzz_ProcessPayments_revertsWithZeroBalance( + function testFuzz_PublicProcessPayments_revertsGivenZeroBalance( address testRecipient ) public { vm.assume(testRecipient != address(0)); @@ -953,7 +949,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { └── And update intent IDs properly └── And track total amounts correctly */ - function testFuzz_ProcessPayments_handlesMultipleDuplicateRecipients( + function testFuzz_PublicProcessPayments_succeedsGivenDuplicateRecipients( address testRecipient, uint testAmount ) public { @@ -993,7 +989,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { └── Then it should handle valid time ranges └── And revert for invalid ones */ - function testFuzz_ProcessPayments_handlesVariableTimeRanges( + function testFuzz_PublicProcessPayments_succeedsGivenVariableTimeRanges( address testRecipient, uint testAmount, uint32 timeOffset @@ -1028,7 +1024,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { assertTrue(intentId != bytes32(0)); } - function testFuzz_ProcessPayments_revertsWithExpiredEndDate( + function testFuzz_PublicProcessPayments_succeedsGivenExpiredEndDate( address testRecipient, uint testAmount ) public { @@ -1074,7 +1070,9 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { └── When validating the payment order └── Then it should return false */ - function testFuzz_validPaymentOrder_InvalidRecipient() public { + function testPublicValidPaymentOrder_revertsGivenInvalidRecipient() + public + { IERC20PaymentClientBase_v1.PaymentOrder memory order = IERC20PaymentClientBase_v1.PaymentOrder({ recipient: address(0), @@ -1092,7 +1090,8 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { └── When validating the payment order └── Then it should return false */ - function testFuzz_validPaymentOrder_InvalidToken() public { + function testFuzz_validPaymentOrder_InvatestPublicValidPaymentOrder_revertsGivenInvalidTokenlidToken( + ) public { IERC20PaymentClientBase_v1.PaymentOrder memory order = IERC20PaymentClientBase_v1.PaymentOrder({ recipient: address(0xBEEF), @@ -1110,7 +1109,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { └── When validating the payment order └── Then it should return false */ - function testFuzz_validPaymentOrder_InvalidAmount() public { + function testPublicValidPaymentOrder_revertsGivenInvalidAmount() public { IERC20PaymentClientBase_v1.PaymentOrder memory order = IERC20PaymentClientBase_v1.PaymentOrder({ recipient: address(0xBEEF), @@ -1128,7 +1127,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { └── When validating the payment order └── Then it should return false */ - function testFuzz_validPaymentOrder_InvalidStart() public { + function testPublicValidPaymentOrder_revertsGivenInvalidStart() public { IERC20PaymentClientBase_v1.PaymentOrder memory order = IERC20PaymentClientBase_v1.PaymentOrder({ recipient: address(0xBEEF), @@ -1146,7 +1145,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { └── When validating the payment order └── Then it should return false */ - function testFuzz_validPaymentOrder_InvalidCliff() public { + function testPublicValidPaymentOrder_revertsGivenInvalidCliff() public { IERC20PaymentClientBase_v1.PaymentOrder memory order = IERC20PaymentClientBase_v1.PaymentOrder({ recipient: address(0xBEEF), @@ -1164,7 +1163,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { └── When validating the payment order └── Then it should return true */ - function testFuzz_validPaymentOrder_Success() public { + function testPublicValidPaymentOrder_succeeds() public { IERC20PaymentClientBase_v1.PaymentOrder memory order = IERC20PaymentClientBase_v1.PaymentOrder({ recipient: address(0xBEEF), diff --git a/test/modules/paymentProcessor/abstracts/CrossChainBase_v1.t.sol b/test/modules/paymentProcessor/abstracts/CrossChainBase_v1.t.sol index a4cd37699..1ec488022 100644 --- a/test/modules/paymentProcessor/abstracts/CrossChainBase_v1.t.sol +++ b/test/modules/paymentProcessor/abstracts/CrossChainBase_v1.t.sol @@ -91,11 +91,6 @@ contract CrossChainBase_v1_Test is ModuleTest { assertTrue(crossChainBase.supportsInterface(interfaceId)); } - function testSupportsInterface_revertsGivenUnknownInterface() public { - bytes4 randomInterfaceId = bytes4(keccak256("random()")); - assertFalse(crossChainBase.supportsInterface(randomInterfaceId)); - } - /* └── Given the contract is already initialized └── When trying to reinitialize @@ -104,14 +99,30 @@ contract CrossChainBase_v1_Test is ModuleTest { vm.expectRevert(OZErrors.Initializable__InvalidInitialization); crossChainBase.init(_orchestrator, _METADATA, abi.encode(1)); } + + /** + * @dev Test interface support failure case + * └── Given the contract is initialized + * └── When checking for an unknown interface + * └── Then it should return false + */ + function testSupportsInterface_failsGivenUnknownInterface() public { + bytes4 randomInterfaceId = bytes4(keccak256("random()")); + assertFalse(crossChainBase.supportsInterface(randomInterfaceId)); + } + //-------------------------------------------------------------------------- //Test: executeBridgeTransfer - /* - └── Given an empty payment order is created - └── When executeBridgeTransfer is called - └── Then it should return empty bytes */ - function testExecuteBridgeTransfer_worksGivenEmptyPaymentOrder() public { + /** + * @dev Test bridge transfer with empty payment order + * └── Given an empty payment order is created + * └── When executeBridgeTransfer is called + * └── Then it should return empty bytes + */ + function testExecuteBridgeTransfer_succeedsGivenEmptyPaymentOrder() + public + { address[] memory setupRecipients = new address[](1); setupRecipients[0] = address(1); uint[] memory setupAmounts = new uint[](1); @@ -128,6 +139,7 @@ contract CrossChainBase_v1_Test is ModuleTest { ); assertEq(result, bytes("")); } + //-------------------------------------------------------------------------- //Helper Functions From 002b3f6aa04f404fc776349c661e80d1dab0fb28 Mon Sep 17 00:00:00 2001 From: Zuhaib Mohammed Date: Mon, 27 Jan 2025 15:59:40 +0530 Subject: [PATCH 91/93] fix: change the _validateTransferRequest msg.sender to client --- .../paymentProcessor/PP_Connext_Crosschain_v1.sol | 5 ++--- .../paymentProcessor/PP_Connext_Crosschain_v1.t.sol | 12 ++++++------ 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol b/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol index 979291d90..d546e659e 100644 --- a/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol +++ b/src/modules/paymentProcessor/PP_Connext_Crosschain_v1.sol @@ -281,10 +281,9 @@ contract PP_Connext_Crosschain_v1 is PP_Crosschain_v1 { bytes memory executionData ) internal view returns (uint) { //msg.sender should be the client - if (msg.sender != recipient) { + if (msg.sender != client) { revert Module__InvalidAddress(); - } //@note -> should the msg.sender be the recipient, since they are the one who is trying to retry or cancel the payment isntead of paymentClient? - //failedAmount should be stored if the transfer has failed + } uint failedAmount = failedTransfers[client][recipient][executionData]; if (failedAmount == 0) { revert Module__CrossChainBase__InvalidAmount(); diff --git a/test/modules/paymentProcessor/PP_Connext_Crosschain_v1.t.sol b/test/modules/paymentProcessor/PP_Connext_Crosschain_v1.t.sol index 561b5f01a..903e3e2a9 100644 --- a/test/modules/paymentProcessor/PP_Connext_Crosschain_v1.t.sol +++ b/test/modules/paymentProcessor/PP_Connext_Crosschain_v1.t.sol @@ -533,7 +533,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { ); // Now retry with proper execution data - vm.prank(address(testRecipient)); + vm.prank(address(paymentClient)); paymentProcessor.retryFailedTransfer( address(paymentClient), testRecipient, @@ -603,12 +603,12 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { uint balanceBeforeCancel = _token.balanceOf(address(paymentProcessor)); // Cancel as recipient - vm.prank(address(testRecipient)); + vm.prank(address(paymentClient)); paymentProcessor.cancelTransfer( address(paymentClient), testRecipient, executionData, orders[0] ); uint balanceAfterCancel = _token.balanceOf(address(paymentProcessor)); - console2.log("balanceAfterCancel", balanceAfterCancel); + //console2.log("balanceAfterCancel", balanceAfterCancel); assertEq(balanceAfterCancel, balanceBeforeCancel - testAmount); @@ -688,7 +688,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { ); // Cancel the transfer - vm.prank(address(testRecipient)); + vm.prank(address(paymentClient)); vm.expectRevert( ICrossChainBase_v1.Module__CrossChainBase__InvalidAmount.selector ); @@ -769,7 +769,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = _setupSinglePayment(testRecipient, testAmount); - vm.prank(testRecipient); + vm.prank(address(paymentClient)); vm.expectRevert( ICrossChainBase_v1.Module__CrossChainBase__InvalidAmount.selector ); @@ -837,7 +837,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { failingExecutionData ); - vm.prank(address(testRecipient)); + vm.prank(address(paymentClient)); vm.expectRevert( abi.encodeWithSelector( IPP_Crosschain_v1 From 4e5b4881994c04ab1cd1ff2c5715da585c879ce8 Mon Sep 17 00:00:00 2001 From: Zuhaib Mohammed Date: Wed, 29 Jan 2025 17:35:50 +0530 Subject: [PATCH 92/93] fix: refractor the unit tests --- .../PP_Connext_Crosschain_v1.t.sol | 83 +++++++------------ 1 file changed, 31 insertions(+), 52 deletions(-) diff --git a/test/modules/paymentProcessor/PP_Connext_Crosschain_v1.t.sol b/test/modules/paymentProcessor/PP_Connext_Crosschain_v1.t.sol index 903e3e2a9..fd1fe330c 100644 --- a/test/modules/paymentProcessor/PP_Connext_Crosschain_v1.t.sol +++ b/test/modules/paymentProcessor/PP_Connext_Crosschain_v1.t.sol @@ -154,11 +154,8 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { address testRecipient, uint testAmount ) public { - vm.assume(testRecipient != address(0)); - vm.assume(testAmount > 0 && testAmount < MINTED_SUPPLY); // Keeping within our minted balance - + _assumeValidRecipientAndAmount(testRecipient, testAmount); _setupSinglePayment(testRecipient, testAmount); - // Get the client interface IERC20PaymentClientBase_v1 client = IERC20PaymentClientBase_v1(address(paymentClient)); @@ -178,7 +175,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { uint balanceBefore = _token.balanceOf(address(paymentProcessor)); paymentProcessor.processPayments(client, executionData); assertEq(_token.balanceOf(address(testRecipient)), 0); - + console2.log(_token.balanceOf(address(testRecipient))); uint balanceAfter = _token.balanceOf(address(paymentProcessor)); assertEq(balanceAfter, balanceBefore + testAmount); @@ -284,9 +281,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { address testRecipient, uint testAmount ) public { - vm.assume(testRecipient != address(0)); - vm.assume(testAmount > 0 && testAmount < MINTED_SUPPLY); // Keeping within our minted balance - + _assumeValidRecipientAndAmount(testRecipient, testAmount); _setupSinglePayment(testRecipient, testAmount); // Get the client interface @@ -307,9 +302,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { address testRecipient, uint testAmount ) public { - vm.assume(testRecipient != address(0)); - vm.assume(testAmount > 0 && testAmount < MINTED_SUPPLY); // Keeping within our minted balance - + _assumeValidRecipientAndAmount(testRecipient, testAmount); _setupSinglePayment(testRecipient, testAmount); // Get the client interface @@ -380,9 +373,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { address testRecipient, uint testAmount ) public { - vm.assume(testRecipient != address(0)); - vm.assume(testAmount > 0 && testAmount < MINTED_SUPPLY); // Keeping within our minted balance - + _assumeValidRecipientAndAmount(testRecipient, testAmount); _setupSinglePayment(testRecipient, testAmount); // Get the client interface IERC20PaymentClientBase_v1 client = @@ -427,9 +418,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { address testRecipient, uint testAmount ) public { - vm.assume(testRecipient != address(0)); - vm.assume(testAmount > MINTED_SUPPLY && testAmount <= type(uint96).max); - + _assumeValidRecipientAndAmount(testRecipient, testAmount); _setupSinglePayment(testRecipient, testAmount); vm.prank(testRecipient); @@ -438,6 +427,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { IERC20PaymentClientBase_v1 client = IERC20PaymentClientBase_v1(address(paymentClient)); + console2.log(address(paymentProcessor)); vm.expectRevert( abi.encodeWithSelector( @@ -461,9 +451,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { address testRecipient, uint96 testAmount ) public { - // Assumptions - vm.assume(testAmount > 0 && testAmount <= MINTED_SUPPLY); - vm.assume(testRecipient != address(0)); + _assumeValidRecipientAndAmount(testRecipient, testAmount); // Setup - Clear existing balance uint currentBalance = _token.balanceOf(address(paymentProcessor)); @@ -507,8 +495,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { address testRecipient, uint testAmount ) public { - vm.assume(testRecipient != address(0)); - vm.assume(testAmount > 0 && testAmount < MINTED_SUPPLY); + _assumeValidRecipientAndAmount(testRecipient, testAmount); // Setup initial payment IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = @@ -571,9 +558,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { address testRecipient, uint testAmount ) public { - // Setup - vm.assume(testRecipient != address(0)); - vm.assume(testAmount > 0 && testAmount < MINTED_SUPPLY); + _assumeValidRecipientAndAmount(testRecipient, testAmount); // Setup the payment and process it IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = @@ -608,7 +593,6 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { address(paymentClient), testRecipient, executionData, orders[0] ); uint balanceAfterCancel = _token.balanceOf(address(paymentProcessor)); - //console2.log("balanceAfterCancel", balanceAfterCancel); assertEq(balanceAfterCancel, balanceBeforeCancel - testAmount); @@ -631,10 +615,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { address nonRecipient, uint testAmount ) public { - vm.assume(testRecipient != address(0)); - vm.assume(nonRecipient != testRecipient); - vm.assume(testAmount > 0 && testAmount < MINTED_SUPPLY); - + _assumeValidRecipientAndAmount(testRecipient, testAmount); _setupSinglePayment(testRecipient, testAmount); // Process payment to create intent @@ -658,6 +639,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { }); // Prank as non-recipient + console2.log(address(paymentClient)); vm.prank(nonRecipient); vm.expectRevert(IModule_v1.Module__InvalidAddress.selector); paymentProcessor.cancelTransfer( @@ -676,8 +658,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { address testRecipient, uint testAmount ) public { - vm.assume(testRecipient != address(0)); - vm.assume(testAmount > 0 && testAmount < MINTED_SUPPLY); + _assumeValidRecipientAndAmount(testRecipient, testAmount); // Setup initial payment and process it IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = @@ -706,8 +687,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { address testRecipient, uint testAmount ) public { - vm.assume(testRecipient != address(0)); - vm.assume(testAmount > 0 && testAmount < MINTED_SUPPLY); + _assumeValidRecipientAndAmount(testRecipient, testAmount); _setupSinglePayment(testRecipient, testAmount); @@ -728,8 +708,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { uint testAmount, address invalidCaller ) public { - vm.assume(testRecipient != address(0)); - vm.assume(testAmount > 0 && testAmount < MINTED_SUPPLY); + _assumeValidRecipientAndAmount(testRecipient, testAmount); vm.assume(invalidCaller != address(paymentClient)); // Setup failed transfer @@ -763,8 +742,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { address testRecipient, uint testAmount ) public { - vm.assume(testRecipient != address(0)); - vm.assume(testAmount > 0 && testAmount < MINTED_SUPPLY); + _assumeValidRecipientAndAmount(testRecipient, testAmount); IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = _setupSinglePayment(testRecipient, testAmount); @@ -791,8 +769,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { address testRecipient, uint testAmount ) public { - vm.assume(testRecipient != address(0)); - vm.assume(testAmount > 0 && testAmount < MINTED_SUPPLY); + _assumeValidRecipientAndAmount(testRecipient, testAmount); // Setup initial payment and process it IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = @@ -824,8 +801,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { address testRecipient, uint testAmount ) public { - vm.assume(testRecipient != address(0)); - vm.assume(testAmount > 0 && testAmount < MINTED_SUPPLY); + _assumeValidRecipientAndAmount(testRecipient, testAmount); // Setup failed transfer IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = @@ -866,9 +842,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { address testRecipient, uint testAmount ) public { - vm.assume(testRecipient != address(0)); - vm.assume(testAmount > 0 && testAmount < MINTED_SUPPLY); - + _assumeValidRecipientAndAmount(testRecipient, testAmount); _setupSinglePayment(testRecipient, testAmount); // Reset approval @@ -876,6 +850,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { _token.approve(address(paymentProcessor), 0); // Expect revert for insufficient allowance + console2.log(_token.balanceOf(address(testRecipient))); vm.expectRevert( abi.encodeWithSelector( IERC20Errors.ERC20InsufficientAllowance.selector, @@ -898,8 +873,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { address testRecipient, uint testAmount ) public { - vm.assume(testRecipient != address(0)); - vm.assume(testAmount > 0 && testAmount < MINTED_SUPPLY); + _assumeValidRecipientAndAmount(testRecipient, testAmount); // Setup payment with unsupported token IERC20PaymentClientBase_v1.PaymentOrder memory order = @@ -933,7 +907,6 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { _setupSinglePayment(testRecipient, ZERO_AMOUNT); assertEq(_token.balanceOf(address(testRecipient)), ZERO_AMOUNT); - vm.expectRevert( ICrossChainBase_v1.Module__CrossChainBase__InvalidAmount.selector ); @@ -994,8 +967,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { uint testAmount, uint32 timeOffset ) public { - vm.assume(testRecipient != address(0)); - vm.assume(testAmount > 0 && testAmount < MINTED_SUPPLY); + _assumeValidRecipientAndAmount(testRecipient, testAmount); vm.assume(timeOffset > 0 && timeOffset < 30 days); // Create payment order with fuzzed time range @@ -1029,8 +1001,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { uint testAmount ) public { //@audit-issue -> discuss with 33 about this test - vm.assume(testRecipient != address(0)); - vm.assume(testAmount > 0 && testAmount < MINTED_SUPPLY); + _assumeValidRecipientAndAmount(testRecipient, testAmount); // Setup payment with expired end date IERC20PaymentClientBase_v1.PaymentOrder memory order = @@ -1236,4 +1207,12 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { vm.prank(address(paymentProcessor)); _token.approve(address(paymentProcessor), type(uint).max); // Processor approves bridge logic } + + function _assumeValidRecipientAndAmount( + address testRecipient, + uint testAmount + ) internal pure { + vm.assume(testRecipient != address(0)); + vm.assume(testAmount > 0 && testAmount < MINTED_SUPPLY); + } } From d400668b96ee2105d806b6533b950099547f630e Mon Sep 17 00:00:00 2001 From: Zuhaib Mohammed Date: Fri, 7 Feb 2025 15:42:43 +0530 Subject: [PATCH 93/93] chore: add unit tests for client.amountPaid functionality --- .../PP_Connext_Crosschain_v1.t.sol | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/test/modules/paymentProcessor/PP_Connext_Crosschain_v1.t.sol b/test/modules/paymentProcessor/PP_Connext_Crosschain_v1.t.sol index fd1fe330c..ba0ee42f4 100644 --- a/test/modules/paymentProcessor/PP_Connext_Crosschain_v1.t.sol +++ b/test/modules/paymentProcessor/PP_Connext_Crosschain_v1.t.sol @@ -188,6 +188,26 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { ); } + /* Test single payment outstanding token amounts + └── Given a single valid payment order + └── When processing cross-chain payments + └── Then it should verify the outstanding token amounts + */ + function testFuzz_PublicProcessPayments_verifyOutstandingTokenAmounts( + address testRecipient, + uint testAmount + ) public { + _assumeValidRecipientAndAmount(testRecipient, testAmount); + _setupSinglePayment(testRecipient, testAmount); + // Get the client interface + IERC20PaymentClientBase_v1 client = + IERC20PaymentClientBase_v1(address(paymentClient)); + + assertEq(client.outstandingTokenAmount(address(_token)), testAmount); + paymentProcessor.processPayments(client, executionData); + assertEq(client.outstandingTokenAmount(address(_token)), 0); + } + /* Test multiple payment processing └── Given multiple valid payment orders └── When processing cross-chain payments @@ -249,12 +269,55 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { } } + /* Test multiple payment outstanding token amounts + └── Given multiple valid payment orders + └── When processing cross-chain payments + └── Then it should verify the outstanding token amounts for each payment + */ + function testFuzz_PublicProcessPaymentsMultiple_verifyOutstandingTokenAmounts( + uint8 numRecipients, + uint testAmount + ) public { + vm.assume(numRecipients > 0 && numRecipients <= 10); + vm.assume(testAmount > 0 && testAmount < MINTED_SUPPLY); + + // Setup mock payment orders + address[] memory setupRecipients = new address[](numRecipients); + uint[] memory setupAmounts = new uint[](numRecipients); + + uint OutstandingAmount = 0; + + for (uint i = 0; i < numRecipients; i++) { + setupRecipients[i] = address( + uint160(uint(keccak256(abi.encodePacked(i, block.timestamp)))) + ); + setupAmounts[i] = + 1 + (uint64(uint(keccak256(abi.encode(i, testAmount))))); + OutstandingAmount += setupAmounts[i]; + } + + IERC20PaymentClientBase_v1.PaymentOrder[] memory orders = + _createPaymentOrders(numRecipients, setupRecipients, setupAmounts); + + // Get the client interface + IERC20PaymentClientBase_v1 client = + IERC20PaymentClientBase_v1(address(paymentClient)); + + assertEq( + client.outstandingTokenAmount(address(_token)), OutstandingAmount + ); + // Process payments + paymentProcessor.processPayments(client, executionData); + assertEq(client.outstandingTokenAmount(address(_token)), 0); + } + /* Test empty payment processing └── Given no payment orders └── When processing payments └── Then it should complete successfully └── And bridge data should remain empty */ + function testFuzz_PublicProcessPayments_succeedsGivenNoPaymentOrders() public { @@ -907,6 +970,7 @@ contract PP_Connext_Crosschain_v1_Test is ModuleTest { _setupSinglePayment(testRecipient, ZERO_AMOUNT); assertEq(_token.balanceOf(address(testRecipient)), ZERO_AMOUNT); + console2.log(_token.balanceOf(address(testRecipient))); vm.expectRevert( ICrossChainBase_v1.Module__CrossChainBase__InvalidAmount.selector );