Skip to content

Conversation

@ezynda3
Copy link
Contributor

@ezynda3 ezynda3 commented Sep 22, 2025

Which Jira task belongs to this PR?

Why did I implement it this way?

Checklist before requesting a review

Checklist for reviewer (DO NOT DEPLOY and contracts BEFORE CHECKING THIS!!!)

  • I have checked that any arbitrary calls to external contracts are validated and or restricted
  • I have checked that any privileged calls (i.e. storage modifications) are validated and or restricted
  • I have ensured that any new contracts have had AT A MINIMUM 1 preliminary audit conducted on by <company/auditor>

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 22, 2025

Walkthrough

Replaces ERC20Permit usage with a new IERC20PermitExtended interface in Permit2Proxy, adds packed-signature overloads for EIP‑2612, implements EIP‑3009 transferWithAuthorization (bytes and v/r/s variants), and updates tests with EIP‑3009 coverage and renamed test functions.

Changes

Cohort / File(s) Summary
Periphery contract updates
src/Periphery/Permit2Proxy.sol
Swap ERC20PermitIERC20PermitExtended; add packed permit overload for EIP‑2612; add two callDiamondWithEIP3009Signature paths (bytes and v/r/s); preserve max-approval, token transfer and calldata execution; update permit call sites and error handling.
New interface
src/Interfaces/IERC20PermitExtended.sol
Add IERC20PermitExtended extending IERC20Permit with permit(..., bytes signature) and two transferWithAuthorization overloads (bytes and v/r/s).
Tests
test/solidity/Periphery/Permit2Proxy.t.sol
Rename many tests to CamelCase, set fork block to 23496255, add TestDataEIP3009 and helpers _getTestDataEIP3009SignedBypermit2User / _generateEIP3009MsgHash, and add EIP‑3009 test cases (bytes and v/r/s flows).
Deployments / configs
deployments/*.staging.json
Update Permit2Proxy addresses in Arbitrum/base staging JSONs; reorder a Periphery entry and add Permit2Proxy key in base.staging.json; add ReceiverOIF entry in one file.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description uses the required template structure but lacks critical required information. The Jira task field and implementation rationale are empty, and the reviewer security checklist is completely unchecked. Fill in the Jira task identifier and explain the implementation approach. Address the reviewer checklist items, especially confirming audit completion with auditor details and dates.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly references the primary change (enhancing permit2 proxy with EIP-2612 and EIP-3009 support) and includes version tags for affected components.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch LF-15521-enhance-permit2-proxy

📜 Recent review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 0b118f7 and 3cf0867.

📒 Files selected for processing (4)
  • deployments/arbitrum.diamond.staging.json
  • deployments/arbitrum.staging.json
  • deployments/base.diamond.staging.json
  • deployments/base.staging.json
✅ Files skipped from review due to trivial changes (1)
  • deployments/base.diamond.staging.json
🧰 Additional context used
🧠 Learnings (36)
📓 Common learnings
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 782
File: deployments/base.diamond.json:148-148
Timestamp: 2024-11-25T09:05:43.045Z
Learning: In deployment configuration files (e.g., `deployments/base.diamond.json`), empty addresses for contracts like `Permit2Proxy` may be placeholders and will be updated after approvals are released from the multisig safe.
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 782
File: src/Periphery/Permit2Proxy.sol:75-108
Timestamp: 2024-10-09T03:47:21.269Z
Learning: In the `Permit2Proxy` contract (`src/Periphery/Permit2Proxy.sol`), reentrancy protection is not necessary for functions like `callDiamondWithEIP2612Signature` when calling our own trusted diamond contract (`LIFI_DIAMOND`).
Learnt from: mirooon
Repo: lifinance/contracts PR: 1283
File: deployments/ronin.diamond.json:65-68
Timestamp: 2025-08-07T10:20:01.383Z
Learning: When analyzing deployment PRs in the lifinance/contracts repository, carefully verify that target state configuration files (like script/deploy/_targetState.json) and deployment log files have been updated before flagging missing entries. The AI summary section should be consulted to understand all file changes, as manual searches might miss entries due to formatting differences or search limitations.
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1256
File: deployments/zksync.diamond.json:81-87
Timestamp: 2025-07-04T08:59:08.108Z
Learning: When analyzing deployment PRs in the lifinance/contracts repository, carefully verify that target state configuration files (like script/deploy/_targetState.json) have been updated before flagging missing entries. The AI summary section should be consulted to understand all file changes, as manual searches might miss entries due to formatting differences or search limitations.
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1334
File: deployments/mainnet.json:54-54
Timestamp: 2025-08-26T02:20:52.515Z
Learning: For deployment PRs in the lifinance/contracts repository, carefully verify the specific scope mentioned in the PR title and description before suggesting updates to other networks. Not all deployments are cross-network updates - some are targeted to specific chains only.
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 988
File: script/deploy/facets/DeployPermit2Proxy.s.sol:33-37
Timestamp: 2025-02-11T10:33:52.791Z
Learning: The Permit2Proxy contract must not be deployed with zero addresses for its critical dependencies (LiFiDiamond, permit2Address, safeAddress). This is enforced by passing `false` to `_getConfigContractAddress` function.
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1168
File: script/deploy/_targetState.json:1564-1589
Timestamp: 2025-05-27T12:36:26.987Z
Learning: When reviewing deployment PRs in the lifinance/contracts repository, target state configuration files (like script/deploy/_targetState.json) may be updated for multiple networks even when the PR is focused on deploying to a specific network. The scope should be determined by the PR title and description, not just by all configuration changes present in the files.
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Periphery/Lda/Facets/CoreRouteFacet.sol:4-6
Timestamp: 2025-08-26T12:11:21.073Z
Learning: In the lifinance/contracts repository, OpenZeppelin's SafeERC20 library provides safePermit functionality for IERC20Permit tokens. This is confirmed by working implementations in both CoreRouteFacet.sol and LiFiDEXAggregator.sol that use the pattern "using SafeERC20 for IERC20Permit;" and successfully call safePermit with permit parameters.
Learnt from: maxklenk
Repo: lifinance/contracts PR: 782
File: script/demoScripts/demoPermit2.ts:0-0
Timestamp: 2024-11-05T17:16:19.946Z
Learning: In `script/demoScripts/demoPermit2.ts`, the `nextNonce` function should be called on the `PERMIT2_PROXY_ADDRESS` because the nonce is stored in the proxy contract, not in the `Permit2` contract (`PERMIT2_ADDRESS`).
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1140
File: config/permit2Proxy.json:3-3
Timestamp: 2025-07-15T05:05:28.134Z
Learning: In config/permit2Proxy.json, the addresses represent the Permit2 contract addresses on each chain, NOT the Permit2Proxy contract addresses. These Permit2 addresses are used as constructor arguments when deploying the Permit2Proxy contract. The Permit2Proxy acts as a wrapper/proxy that interacts with the underlying Permit2 contract on each network.
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Periphery/Lda/Facets/CoreRouteFacet.sol:4-6
Timestamp: 2025-08-26T12:11:21.073Z
Learning: OpenZeppelin's SafeERC20 library provides safePermit functionality for IERC20Permit tokens. The pattern "using SafeERC20 for IERC20Permit;" enables calling safePermit on IERC20Permit tokens safely. This functionality is available in recent versions of OpenZeppelin contracts.
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Periphery/Lda/Facets/CoreRouteFacet.sol:4-6
Timestamp: 2025-08-26T12:11:21.073Z
Learning: OpenZeppelin's SafeERC20 library provides safePermit functionality for IERC20Permit tokens. The using SafeERC20 for IERC20Permit; directive enables calling safePermit on IERC20Permit tokens safely.
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Periphery/Lda/Facets/CoreRouteFacet.sol:4-6
Timestamp: 2025-08-26T12:11:21.073Z
Learning: OpenZeppelin's SafeERC20 library provides safePermit functionality for IERC20Permit tokens in modern versions. The usage pattern "using SafeERC20 for IERC20Permit;" is valid and enables safe permit operations. This is confirmed to be working in the lifinance/contracts codebase.
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Periphery/Lda/Facets/CoreRouteFacet.sol:4-6
Timestamp: 2025-08-26T12:11:21.073Z
Learning: OpenZeppelin's SafeERC20 library provides safePermit functionality for IERC20Permit tokens. This is confirmed by actual usage in the lifinance/contracts codebase where safePermit is used in multiple files including CoreRouteFacet.sol and LiFiDEXAggregator.sol. The pattern "using SafeERC20 for IERC20Permit;" is valid and working.
Learnt from: ezynda3
Repo: lifinance/contracts PR: 1124
File: script/demoScripts/utils/lib/cowShedSdk.ts:0-0
Timestamp: 2025-06-05T14:50:17.275Z
Learning: For demo scripts and example code, ezynda3 prefers to keep the code simple and focused on demonstrating the core functionality rather than adding extensive input validation or defensive programming measures.
📚 Learning: 2025-04-21T03:15:12.236Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1109
File: deployments/worldchain.diamond.json:84-85
Timestamp: 2025-04-21T03:15:12.236Z
Learning: In deployment JSON files that contain "diamond" in their filename (located in the deployments folder), periphery contracts may have empty string values for their addresses. This indicates that the contract is not deployed on that particular chain and should not be flagged as an issue during code reviews.

Applied to files:

  • deployments/arbitrum.diamond.staging.json
📚 Learning: 2024-10-09T03:47:21.269Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 819
File: deployments/scroll.diamond.json:82-82
Timestamp: 2024-10-09T03:47:21.269Z
Learning: In the `deployments/*.diamond.json` and `deployments/*.json` files, the `LiFiDEXAggregator` contract may intentionally have the same contract address across multiple networks. This is acceptable and should not be flagged as an issue in future code reviews.

Applied to files:

  • deployments/arbitrum.diamond.staging.json
📚 Learning: 2024-11-25T09:05:43.045Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 782
File: deployments/base.diamond.json:148-148
Timestamp: 2024-11-25T09:05:43.045Z
Learning: In deployment configuration files (e.g., `deployments/base.diamond.json`), empty addresses for contracts like `Permit2Proxy` may be placeholders and will be updated after approvals are released from the multisig safe.

Applied to files:

  • deployments/arbitrum.diamond.staging.json
  • deployments/arbitrum.staging.json
  • deployments/base.staging.json
📚 Learning: 2025-04-21T03:17:53.443Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1109
File: deployments/worldchain.json:28-28
Timestamp: 2025-04-21T03:17:53.443Z
Learning: For deployment PRs involving address updates like the RelayFacet to Worldchain, verify the actual presence of entries in files before reporting issues. The RelayFacet exists in the diamond log file and the PR diff already contains the necessary address change.

Applied to files:

  • deployments/arbitrum.diamond.staging.json
  • deployments/arbitrum.staging.json
  • deployments/base.staging.json
📚 Learning: 2025-09-09T10:39:26.383Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1357
File: deployments/lens.diamond.json:48-51
Timestamp: 2025-09-09T10:39:26.383Z
Learning: In the lifinance/contracts repository, when deployment JSON files show address changes (like AcrossFacetV3 address updates in deployments/*.diamond.json), the corresponding _deployments_log_file.json updates may be handled in separate PRs rather than the same PR that updates the diamond configuration files.

Applied to files:

  • deployments/arbitrum.diamond.staging.json
📚 Learning: 2025-04-22T09:04:44.244Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1112
File: deployments/soneium.diamond.json:81-81
Timestamp: 2025-04-22T09:04:44.244Z
Learning: In the lifinance/contracts repository, it's normal and expected for periphery contracts to have empty address values in deployment files when they are not deployed on a particular network. This applies to all periphery contracts including ReceiverChainflip, ReceiverStargateV2, and others. PRs should not flag empty periphery contract addresses as issues.

Applied to files:

  • deployments/arbitrum.diamond.staging.json
📚 Learning: 2024-10-24T06:17:25.211Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 807
File: deployments/bsc.diamond.staging.json:100-101
Timestamp: 2024-10-24T06:17:25.211Z
Learning: In `deployments/bsc.diamond.staging.json`, the trailing comma after the last property in the `Periphery` object is acceptable and should not be flagged as a JSON formatting error.

Applied to files:

  • deployments/arbitrum.diamond.staging.json
  • deployments/base.staging.json
📚 Learning: 2025-04-21T03:17:53.443Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1109
File: deployments/worldchain.json:28-28
Timestamp: 2025-04-21T03:17:53.443Z
Learning: For deployment PRs updating contract addresses (like RelayFacet on Worldchain), verify the presence of entries in all relevant files (worldchain.json, worldchain.diamond.json, _deployments_log_file.json) before reporting inconsistencies. The RelayFacet entry exists in all required deployment files with the correct address.

Applied to files:

  • deployments/arbitrum.diamond.staging.json
  • deployments/arbitrum.staging.json
📚 Learning: 2025-07-04T08:59:08.108Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1256
File: deployments/zksync.diamond.json:81-87
Timestamp: 2025-07-04T08:59:08.108Z
Learning: When analyzing deployment PRs in the lifinance/contracts repository, carefully verify that target state configuration files (like script/deploy/_targetState.json) have been updated before flagging missing entries. The AI summary section should be consulted to understand all file changes, as manual searches might miss entries due to formatting differences or search limitations.

Applied to files:

  • deployments/arbitrum.diamond.staging.json
  • deployments/arbitrum.staging.json
  • deployments/base.staging.json
📚 Learning: 2025-08-07T10:20:01.383Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1283
File: deployments/ronin.diamond.json:65-68
Timestamp: 2025-08-07T10:20:01.383Z
Learning: When analyzing deployment PRs in the lifinance/contracts repository, carefully verify that target state configuration files (like script/deploy/_targetState.json) and deployment log files have been updated before flagging missing entries. The AI summary section should be consulted to understand all file changes, as manual searches might miss entries due to formatting differences or search limitations.

Applied to files:

  • deployments/arbitrum.diamond.staging.json
  • deployments/arbitrum.staging.json
  • deployments/base.staging.json
📚 Learning: 2024-10-09T03:47:21.269Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 819
File: deployments/fuse.json:24-25
Timestamp: 2024-10-09T03:47:21.269Z
Learning: The `LiFiDEXAggregator.sol` contract is located in `src/Periphery/`.

Applied to files:

  • deployments/arbitrum.diamond.staging.json
📚 Learning: 2025-01-21T11:04:46.116Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 937
File: config/dexs.json:95-99
Timestamp: 2025-01-21T11:04:46.116Z
Learning: In the dexs.json configuration, utility contracts (like FeeCollector, LiFuelFeeCollector, TokenWrapper) can be treated as DEX contracts since they are handled similarly in the system's logic.

Applied to files:

  • deployments/arbitrum.diamond.staging.json
📚 Learning: 2025-01-21T11:07:36.236Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 937
File: script/deploy/facets/DeployLiFiDEXAggregator.s.sol:33-35
Timestamp: 2025-01-21T11:07:36.236Z
Learning: In the LiFiDEXAggregator contract deployment (DeployLiFiDEXAggregator.s.sol), the refundWallet address from global.json is used as the owner parameter in the contract's constructor, which is then passed to the WithdrawablePeriphery base contract.

Applied to files:

  • deployments/arbitrum.diamond.staging.json
📚 Learning: 2024-10-04T09:01:56.514Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 819
File: deployments/base.diamond.json:123-123
Timestamp: 2024-10-04T09:01:56.514Z
Learning: In the `lifinance/contracts` repository, it's acceptable to retain references to the old `LiFuelFeeCollector` address (`0xc4f7A34b8d283f66925eF0f5CCdFC2AF3030DeaE`) in deployment files when updating them is not necessary.

Applied to files:

  • deployments/arbitrum.diamond.staging.json
📚 Learning: 2025-12-17T10:28:34.379Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: .cursor/rules/104-receiver-contracts.mdc:0-0
Timestamp: 2025-12-17T10:28:34.379Z
Learning: Applies to src/Periphery/Receiver*.sol : Receiver contracts must include bridge-specific configuration storage (e.g., `recoverGas` for StargateV2, protocol-specific constants)

Applied to files:

  • deployments/arbitrum.diamond.staging.json
📚 Learning: 2025-06-25T06:27:38.873Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1140
File: deployments/fantom.diamond.json:97-105
Timestamp: 2025-06-25T06:27:38.873Z
Learning: When contracts are redeployed, they receive new addresses. Permit2Proxy addresses in deployment files reflect the actual deployed contract addresses for each network, not a standardized address across all networks.

Applied to files:

  • deployments/arbitrum.staging.json
📚 Learning: 2025-07-15T05:05:28.134Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1140
File: config/permit2Proxy.json:3-3
Timestamp: 2025-07-15T05:05:28.134Z
Learning: In config/permit2Proxy.json, the addresses represent the Permit2 contract addresses on each chain, NOT the Permit2Proxy contract addresses. These Permit2 addresses are used as constructor arguments when deploying the Permit2Proxy contract. The Permit2Proxy acts as a wrapper/proxy that interacts with the underlying Permit2 contract on each network.

Applied to files:

  • deployments/arbitrum.staging.json
  • deployments/base.staging.json
📚 Learning: 2024-11-26T01:17:13.212Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 782
File: deployments/cronos.diamond.json:67-67
Timestamp: 2024-11-26T01:17:13.212Z
Learning: In the `deployments/cronos.diamond.json` file, the `Permit2Proxy` address is intentionally left empty because `Permit2Proxy` is not deployed on the Cronos network.

Applied to files:

  • deployments/arbitrum.staging.json
  • deployments/base.staging.json
📚 Learning: 2025-02-11T10:33:52.791Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 988
File: script/deploy/facets/DeployPermit2Proxy.s.sol:33-37
Timestamp: 2025-02-11T10:33:52.791Z
Learning: The Permit2Proxy contract must not be deployed with zero addresses for its critical dependencies (LiFiDiamond, permit2Address, safeAddress). This is enforced by passing `false` to `_getConfigContractAddress` function.

Applied to files:

  • deployments/arbitrum.staging.json
📚 Learning: 2025-08-07T10:20:01.383Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1283
File: deployments/ronin.diamond.json:65-68
Timestamp: 2025-08-07T10:20:01.383Z
Learning: When analyzing deployment PRs in the lifinance/contracts repository, understand that _targetState.json tracks contract version numbers (not addresses) and _deployments_log_file.json contains deployment records with addresses that may not be organized in obvious network sections. Always verify the actual file structure before flagging missing entries.

Applied to files:

  • deployments/arbitrum.staging.json
📚 Learning: 2025-02-11T10:35:03.536Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 988
File: script/tasks/solidity/AddTokenApprovalsToCBridgeFacetPacked.s.sol:17-21
Timestamp: 2025-02-11T10:35:03.536Z
Learning: The CBridgeFacetPacked and cBridge addresses in AddTokenApprovalsToCBridgeFacetPacked.s.sol must not be zero addresses as they are required for token approvals to function properly.

Applied to files:

  • deployments/arbitrum.staging.json
  • deployments/base.staging.json
📚 Learning: 2025-08-29T10:02:09.041Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1299
File: src/Facets/AcrossFacetPackedV4.sol:118-136
Timestamp: 2025-08-29T10:02:09.041Z
Learning: In AcrossFacetPackedV4.sol, the team explicitly chooses to omit calldata length validation in gas-optimized packed functions like startBridgeTokensViaAcrossV4NativePacked to save gas, accepting the trade-off of potential out-of-bounds reverts for better gas efficiency.

Applied to files:

  • deployments/arbitrum.staging.json
📚 Learning: 2025-12-17T10:28:13.904Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: .cursor/rules/102-facets.mdc:0-0
Timestamp: 2025-12-17T10:28:13.904Z
Learning: Applies to src/Facets/**/*Facet.sol : Facets must use LibAsset, LibSwap, and LibAllowList utilities along with Validatable and SwapperV2 interfaces; reserve native fees via `_depositAndSwap` variants when needed

Applied to files:

  • deployments/arbitrum.staging.json
  • deployments/base.staging.json
📚 Learning: 2025-12-17T10:28:13.904Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: .cursor/rules/102-facets.mdc:0-0
Timestamp: 2025-12-17T10:28:13.904Z
Learning: Applies to src/Facets/**/*Facet.sol : Facets must implement required functions: `_startBridge` (internal), `swapAndStartBridgeTokensVia{FacetName}`, and `startBridgeTokensVia{FacetName}`

Applied to files:

  • deployments/arbitrum.staging.json
  • deployments/base.staging.json
📚 Learning: 2025-08-26T12:11:21.073Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Periphery/Lda/Facets/CoreRouteFacet.sol:4-6
Timestamp: 2025-08-26T12:11:21.073Z
Learning: In the lifinance/contracts repository, OpenZeppelin's SafeERC20 library provides safePermit functionality for IERC20Permit tokens. This is confirmed by working implementations in both CoreRouteFacet.sol and LiFiDEXAggregator.sol that use the pattern "using SafeERC20 for IERC20Permit;" and successfully call safePermit with permit parameters.

Applied to files:

  • deployments/arbitrum.staging.json
📚 Learning: 2025-10-10T10:56:04.861Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1413
File: src/Facets/EverclearFacet.sol:4-13
Timestamp: 2025-10-10T10:56:04.861Z
Learning: LibAllowList is only required for facets that make arbitrary external calls to DEX aggregators (e.g., GenericSwapFacetV3). Bridge facets that call specific protocol contracts (like EverclearFacet calling IEverclearFeeAdapter) do not need to import LibAllowList.

Applied to files:

  • deployments/arbitrum.staging.json
  • deployments/base.staging.json
📚 Learning: 2025-09-25T07:47:30.735Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 1324
File: src/Facets/EcoFacet.sol:306-336
Timestamp: 2025-09-25T07:47:30.735Z
Learning: In EcoFacet (src/Facets/EcoFacet.sol), the team intentionally uses variable-length validation for nonEVMReceiver addresses (up to 44 bytes) to support cross-ecosystem bridging with different address formats, rather than enforcing fixed 32-byte raw pubkeys.

Applied to files:

  • deployments/arbitrum.staging.json
  • deployments/base.staging.json
📚 Learning: 2025-08-26T12:11:21.073Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Periphery/Lda/Facets/CoreRouteFacet.sol:4-6
Timestamp: 2025-08-26T12:11:21.073Z
Learning: OpenZeppelin's SafeERC20 library provides safePermit functionality for IERC20Permit tokens. This is confirmed by actual usage in the lifinance/contracts codebase where safePermit is used in multiple files including CoreRouteFacet.sol and LiFiDEXAggregator.sol. The pattern "using SafeERC20 for IERC20Permit;" is valid and working.

Applied to files:

  • deployments/arbitrum.staging.json
📚 Learning: 2025-02-13T08:57:00.095Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 984
File: docs/ChainflipFacet.md:0-0
Timestamp: 2025-02-13T08:57:00.095Z
Learning: The ChainflipData struct in ChainflipFacet has three fields:
1. uint32 dstToken - The destination token identifier in Chainflip
2. bytes32 nonEvmAddress - The non-EVM destination address
3. bytes cfParameters - Additional parameters for Chainflip protocol

Applied to files:

  • deployments/arbitrum.staging.json
📚 Learning: 2024-10-09T03:47:21.269Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 807
File: deployments/bsc.diamond.staging.json:4-7
Timestamp: 2024-10-09T03:47:21.269Z
Learning: In files with "staging" in their filename, such as `deployments/bsc.diamond.staging.json`, it is acceptable for facets to have empty "Name" and "Version" fields. Do not flag missing facet information in such files during reviews.

Applied to files:

  • deployments/base.staging.json
📚 Learning: 2025-08-01T22:11:04.478Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1193
File: deployments/base.staging.json:6-6
Timestamp: 2025-08-01T22:11:04.478Z
Learning: The user mirooon prefers to maintain both DexManagerFacet and WhitelistManagerFacet entries in staging deployment configurations during migration periods, even when the DexManagerFacet contract no longer exists in the codebase. This approach allows for backward compatibility testing and rollback capabilities in the staging environment.

Applied to files:

  • deployments/base.staging.json
📚 Learning: 2025-02-12T09:44:12.961Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 985
File: script/deploy/_targetState.json:0-0
Timestamp: 2025-02-12T09:44:12.961Z
Learning: The bsca network intentionally maintains different facet versions between staging and production environments, specifically:
1. CalldataVerificationFacet: v1.1.1 in staging vs v1.1.2 in production
2. EmergencyPauseFacet: present only in production
3. Permit2Proxy: present only in production

Applied to files:

  • deployments/base.staging.json
📚 Learning: 2025-12-17T10:27:21.083Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: .cursor/rules/002-architecture.mdc:0-0
Timestamp: 2025-12-17T10:27:21.083Z
Learning: Applies to src/Facets/**/*.sol : Facets provide modular functionality grouped by concern (bridges, swaps, receivers, admin, etc.)

Applied to files:

  • deployments/base.staging.json
📚 Learning: 2025-09-04T09:26:35.060Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 1344
File: src/Facets/GardenFacet.sol:124-130
Timestamp: 2025-09-04T09:26:35.060Z
Learning: In src/Facets/GardenFacet.sol, the team has decided that registry lookup validation should only check for zero addresses and not perform additional contract existence checks (like htlcAddress.code.length > 0), preferring minimal validation for Garden HTLC registry addresses.

Applied to files:

  • deployments/base.staging.json
📚 Learning: 2025-12-17T10:28:13.904Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: .cursor/rules/102-facets.mdc:0-0
Timestamp: 2025-12-17T10:28:13.904Z
Learning: Applies to src/Facets/**/*Facet.sol : Facets must use modifiers: `nonReentrant`, `refundExcessNative`, `validateBridgeData`, `doesNotContainSourceSwaps`/`doesContainSourceSwaps`, `doesNotContainDestinationCalls`/`doesContainDestinationCalls` on applicable functions

Applied to files:

  • deployments/base.staging.json
🪛 Gitleaks (8.30.0)
deployments/arbitrum.diamond.staging.json

[high] 220-220: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: run-unit-tests
  • GitHub Check: enforce-min-test-coverage
🔇 Additional comments (4)
deployments/base.staging.json (1)

23-24: LGTM! Permit2Proxy added to base staging.

The addition of the Permit2Proxy entry with address 0xfaD2a4d7e19C4EDd5407a7F7673F01FE41431D51 correctly deploys the enhanced Permit2Proxy contract to the base staging environment. The address format is valid and the JSON structure is correct.

deployments/arbitrum.staging.json (1)

49-49: LGTM! Permit2Proxy redeployed to arbitrum staging.

The Permit2Proxy address update to 0x6DfAFe261B5d489aeF52e9AfA6303c291F520902 reflects the redeployment of the enhanced contract with EIP-3009 support. The address is consistent with the corresponding entry in arbitrum.diamond.staging.json.

deployments/arbitrum.diamond.staging.json (2)

214-214: LGTM! Permit2Proxy redeployed with enhanced functionality.

The Permit2Proxy address update to 0x6DfAFe261B5d489aeF52e9AfA6303c291F520902 correctly reflects the redeployment of the enhanced contract with EIP-3009 transferWithAuthorization support and IERC20PermitExtended interface. The address matches the corresponding entry in arbitrum.staging.json.


218-218: LGTM! ReceiverOIF placeholder added.

The empty value for ReceiverOIF indicates the contract is not yet deployed on the arbitrum network, which is acceptable for staging configurations.

Warning

Review ran into problems

🔥 Problems

Errors were encountered while retrieving linked issues.

Errors (1)
  • JIRA integration encountered authorization issues. Please disconnect and reconnect the integration in the CodeRabbit UI.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@lifi-action-bot lifi-action-bot marked this pull request as draft September 22, 2025 14:33
@lifi-action-bot lifi-action-bot changed the title Lf 15521 enhance permit2 proxy Lf 15521 enhance permit2 proxy [GenericErrors v1.0.3,IERC1271 v1.0.0,Permit2Proxy v1.1.0] Sep 22, 2025
@lifi-action-bot
Copy link
Collaborator

lifi-action-bot commented Sep 22, 2025

Test Coverage Report

Line Coverage: 85.13% (3019 / 3546 lines)
Function Coverage: 89.15% ( 485 / 544 functions)
Branch Coverage: 63.73% ( 485 / 761 branches)
Test coverage (85.13%) is above min threshold (83%). Check passed.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 24f55c5 and b0371e7.

⛔ Files ignored due to path filters (1)
  • foundry.lock is excluded by !**/*.lock
📒 Files selected for processing (4)
  • src/Errors/GenericErrors.sol (2 hunks)
  • src/Interfaces/IERC1271.sol (1 hunks)
  • src/Periphery/Permit2Proxy.sol (5 hunks)
  • test/solidity/Periphery/Permit2Proxy.t.sol (3 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
{src,test/solidity,script}/**/*.sol

📄 CodeRabbit inference engine (conventions.md)

{src,test/solidity,script}/**/*.sol: All Solidity files must start with a license identifier, followed by a blank line, then the pragma statement
Use SPDX license identifier // SPDX-License-Identifier: LGPL-3.0-only for all first-party Solidity files
Error names must be descriptive PascalCase, use custom errors instead of revert() strings, and omit error messages for gas optimization
Solidity variable/function naming: state variables camelCase; function params camelCase with leading underscore; constants and immutables in CONSTANT_CASE; private/internal variables camelCase; function names camelCase

Files:

  • src/Errors/GenericErrors.sol
  • src/Interfaces/IERC1271.sol
  • test/solidity/Periphery/Permit2Proxy.t.sol
  • src/Periphery/Permit2Proxy.sol
src/Errors/GenericErrors.sol

📄 CodeRabbit inference engine (conventions.md)

Define and maintain all generic, cross-contract errors exclusively in src/Errors/GenericErrors.sol and bump @Custom:version when adding new errors

Files:

  • src/Errors/GenericErrors.sol
src/**/*.sol

📄 CodeRabbit inference engine (conventions.md)

src/**/*.sol: Every contract must include NatSpec header in this order: @title, @author LI.FI (https://li.fi), @notice, @Custom:version X.Y.Z
All public/external functions must have NatSpec comments documenting purpose, params, and returns
Add inline comments for complex logic, optimizations, gas-saving techniques, and math steps
Emit GenericSwapCompleted after successful same-chain swap completion

Files:

  • src/Errors/GenericErrors.sol
  • src/Interfaces/IERC1271.sol
  • src/Periphery/Permit2Proxy.sol
**/*.sol

📄 CodeRabbit inference engine (conventions.md)

Follow solhint rules as configured in .solhint.json

Files:

  • src/Errors/GenericErrors.sol
  • src/Interfaces/IERC1271.sol
  • test/solidity/Periphery/Permit2Proxy.t.sol
  • src/Periphery/Permit2Proxy.sol
src/Interfaces/**/*.sol

📄 CodeRabbit inference engine (conventions.md)

src/Interfaces/**/*.sol: All interfaces must reside under src/Interfaces, be in separate files from implementations, and use an I prefix (e.g., ILiFi)
Every interface must include NatSpec in this order: @title, @notice, @author LI.FI (https://li.fi), @Custom:version X.Y.Z

Files:

  • src/Interfaces/IERC1271.sol
test/solidity/**/*.t.sol

📄 CodeRabbit inference engine (conventions.md)

test/solidity/**/*.t.sol: Solidity test files must use .t.sol extension; organize imports with system libraries first, then project files
Test naming: successful tests prefixed test_, failure tests prefixed testRevert_, base/inherited tests prefixed testBase_; negative tests must check specific revert reasons
Each test contract must implement setUp() that configures block numbers, initializes base contracts, sets up facets, and uses vm.label; contracts inheriting TestBase.sol must call initTestBase() in setUp()
Use vm.startPrank()/vm.stopPrank() for user simulation; maintain blank-line formatting rules for tests (expectRevert separation, assertion grouping, spacing)
Use assertEq for equality, custom assertion modifiers for balance changes, vm.expectRevert with precise reasons, and vm.expectEmit(true,true,true,true,) to verify events and parameters

Files:

  • test/solidity/Periphery/Permit2Proxy.t.sol
🧠 Learnings (15)
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
PR: lifinance/contracts#0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Errors/GenericErrors.sol : Define and maintain all generic, cross-contract errors exclusively in src/Errors/GenericErrors.sol and bump custom:version when adding new errors

Applied to files:

  • src/Errors/GenericErrors.sol
📚 Learning: 2024-10-09T03:47:21.269Z
Learnt from: 0xDEnYO
PR: lifinance/contracts#782
File: src/Periphery/Permit2Proxy.sol:75-108
Timestamp: 2024-10-09T03:47:21.269Z
Learning: In the `Permit2Proxy` contract (`src/Periphery/Permit2Proxy.sol`), reentrancy protection is not necessary for functions like `callDiamondWithEIP2612Signature` when calling our own trusted diamond contract (`LIFI_DIAMOND`).

Applied to files:

  • test/solidity/Periphery/Permit2Proxy.t.sol
  • src/Periphery/Permit2Proxy.sol
📚 Learning: 2024-10-10T03:18:20.721Z
Learnt from: 0xDEnYO
PR: lifinance/contracts#782
File: test/solidity/Helpers/WithdrawablePeriphery.t.sol:22-35
Timestamp: 2024-10-10T03:18:20.721Z
Learning: In Solidity tests for withdrawal functions in `test/solidity/Helpers/WithdrawablePeriphery.t.sol`, do not suggest adding tests where the withdrawal amount exceeds the contract's balance, as such tests are unnecessary because any contract will fail in that case.

Applied to files:

  • test/solidity/Periphery/Permit2Proxy.t.sol
📚 Learning: 2025-08-26T12:11:21.073Z
Learnt from: mirooon
PR: lifinance/contracts#1328
File: src/Periphery/Lda/Facets/CoreRouteFacet.sol:4-6
Timestamp: 2025-08-26T12:11:21.073Z
Learning: In the lifinance/contracts repository, OpenZeppelin's SafeERC20 library provides safePermit functionality for IERC20Permit tokens. This is confirmed by working implementations in both CoreRouteFacet.sol and LiFiDEXAggregator.sol that use the pattern "using SafeERC20 for IERC20Permit;" and successfully call safePermit with permit parameters.

Applied to files:

  • test/solidity/Periphery/Permit2Proxy.t.sol
  • src/Periphery/Permit2Proxy.sol
📚 Learning: 2025-08-26T12:11:21.073Z
Learnt from: mirooon
PR: lifinance/contracts#1328
File: src/Periphery/Lda/Facets/CoreRouteFacet.sol:4-6
Timestamp: 2025-08-26T12:11:21.073Z
Learning: OpenZeppelin's SafeERC20 library provides safePermit functionality for IERC20Permit tokens. This is confirmed by actual usage in the lifinance/contracts codebase where safePermit is used in multiple files including CoreRouteFacet.sol and LiFiDEXAggregator.sol. The pattern "using SafeERC20 for IERC20Permit;" is valid and working.

Applied to files:

  • test/solidity/Periphery/Permit2Proxy.t.sol
  • src/Periphery/Permit2Proxy.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
PR: lifinance/contracts#0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : Facet contracts must import/use: ILiFi, LibAsset, LibSwap, LibAllowList; and use security mixins ReentrancyGuard, SwapperV2, Validatable; ECDSA optional

Applied to files:

  • test/solidity/Periphery/Permit2Proxy.t.sol
📚 Learning: 2025-08-26T12:11:21.073Z
Learnt from: mirooon
PR: lifinance/contracts#1328
File: src/Periphery/Lda/Facets/CoreRouteFacet.sol:4-6
Timestamp: 2025-08-26T12:11:21.073Z
Learning: OpenZeppelin's SafeERC20 library provides safePermit functionality for IERC20Permit tokens in modern versions. The usage pattern "using SafeERC20 for IERC20Permit;" is valid and enables safe permit operations. This is confirmed to be working in the lifinance/contracts codebase.

Applied to files:

  • test/solidity/Periphery/Permit2Proxy.t.sol
  • src/Periphery/Permit2Proxy.sol
📚 Learning: 2025-08-26T12:11:21.073Z
Learnt from: mirooon
PR: lifinance/contracts#1328
File: src/Periphery/Lda/Facets/CoreRouteFacet.sol:4-6
Timestamp: 2025-08-26T12:11:21.073Z
Learning: OpenZeppelin's SafeERC20 library provides safePermit functionality for IERC20Permit tokens. The pattern "using SafeERC20 for IERC20Permit;" enables calling safePermit on IERC20Permit tokens safely. This functionality is available in recent versions of OpenZeppelin contracts.

Applied to files:

  • test/solidity/Periphery/Permit2Proxy.t.sol
  • src/Periphery/Permit2Proxy.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
PR: lifinance/contracts#0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : Facet contracts must implement: internal _startBridge, swapAndStartBridgeTokensVia{FacetName}, and startBridgeTokensVia{FacetName}

Applied to files:

  • test/solidity/Periphery/Permit2Proxy.t.sol
📚 Learning: 2025-08-26T12:11:21.073Z
Learnt from: mirooon
PR: lifinance/contracts#1328
File: src/Periphery/Lda/Facets/CoreRouteFacet.sol:4-6
Timestamp: 2025-08-26T12:11:21.073Z
Learning: OpenZeppelin's SafeERC20 library provides safePermit functionality for IERC20Permit tokens. The using SafeERC20 for IERC20Permit; directive enables calling safePermit on IERC20Permit tokens safely.

Applied to files:

  • test/solidity/Periphery/Permit2Proxy.t.sol
  • src/Periphery/Permit2Proxy.sol
📚 Learning: 2025-07-15T05:05:28.134Z
Learnt from: 0xDEnYO
PR: lifinance/contracts#1140
File: config/permit2Proxy.json:3-3
Timestamp: 2025-07-15T05:05:28.134Z
Learning: In config/permit2Proxy.json, the addresses represent the Permit2 contract addresses on each chain, NOT the Permit2Proxy contract addresses. These Permit2 addresses are used as constructor arguments when deploying the Permit2Proxy contract. The Permit2Proxy acts as a wrapper/proxy that interacts with the underlying Permit2 contract on each network.

Applied to files:

  • src/Periphery/Permit2Proxy.sol
📚 Learning: 2025-02-11T10:33:52.791Z
Learnt from: 0xDEnYO
PR: lifinance/contracts#988
File: script/deploy/facets/DeployPermit2Proxy.s.sol:33-37
Timestamp: 2025-02-11T10:33:52.791Z
Learning: The Permit2Proxy contract must not be deployed with zero addresses for its critical dependencies (LiFiDiamond, permit2Address, safeAddress). This is enforced by passing `false` to `_getConfigContractAddress` function.

Applied to files:

  • src/Periphery/Permit2Proxy.sol
📚 Learning: 2024-11-25T09:05:43.045Z
Learnt from: 0xDEnYO
PR: lifinance/contracts#782
File: deployments/base.diamond.json:148-148
Timestamp: 2024-11-25T09:05:43.045Z
Learning: In deployment configuration files (e.g., `deployments/base.diamond.json`), empty addresses for contracts like `Permit2Proxy` may be placeholders and will be updated after approvals are released from the multisig safe.

Applied to files:

  • src/Periphery/Permit2Proxy.sol
📚 Learning: 2024-11-05T17:16:19.946Z
Learnt from: maxklenk
PR: lifinance/contracts#782
File: script/demoScripts/demoPermit2.ts:0-0
Timestamp: 2024-11-05T17:16:19.946Z
Learning: In `script/demoScripts/demoPermit2.ts`, the `nextNonce` function should be called on the `PERMIT2_PROXY_ADDRESS` because the nonce is stored in the proxy contract, not in the `Permit2` contract (`PERMIT2_ADDRESS`).

Applied to files:

  • src/Periphery/Permit2Proxy.sol
📚 Learning: 2025-08-27T23:36:40.773Z
Learnt from: mirooon
PR: lifinance/contracts#1328
File: test/solidity/Periphery/GasZipPeriphery.t.sol:223-233
Timestamp: 2025-08-27T23:36:40.773Z
Learning: In bridge facet swap flows with requiresDeposit=false, tokens are already held by the diamond contract from previous swaps. When subsequent contracts like GasZipPeriphery call LibAsset.depositAsset, they pull tokens from msg.sender (the diamond) to themselves via transferFrom, requiring no additional approvals since the diamond already owns the tokens.

Applied to files:

  • src/Periphery/Permit2Proxy.sol
🪛 GitHub Check: Olympix Integrated Security
src/Periphery/Permit2Proxy.sol

[failure] 359-359:
The contract is vulnerable to signature replay attacks, potentially allowing malicious actors to reuse valid signatures. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/signature-replay-attacks

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: enforce-min-test-coverage
  • GitHub Check: run-unit-tests
🔇 Additional comments (20)
src/Errors/GenericErrors.sol (2)

2-2: LGTM! Version bump follows coding guidelines.

The version increment from 1.0.2 to 1.0.3 correctly follows the coding guidelines for maintaining GenericErrors.sol when adding new errors.


25-25: LGTM! Error placement and naming are appropriate.

The new InvalidSignature() error follows proper Solidity naming conventions (PascalCase) and is appropriately placed alphabetically in the error list. This supports EIP-1271 signature validation flows as referenced in the AI summary.

src/Interfaces/IERC1271.sol (2)

1-8: LGTM! Header follows coding guidelines.

The interface correctly follows the coding guidelines with SPDX license identifier, proper pragma statement, and NatSpec header in the required order (@title, @notice, @author, @Custom:version).


9-18: LGTM! Standard EIP-1271 implementation with proper documentation.

The interface correctly implements the standard EIP-1271 method for signature validation in smart contracts. The function signature matches the standard exactly, and the NatSpec documentation properly documents the magic value 0x1626ba7e, which is the keccak256 hash of "isValidSignature(bytes32,bytes)".

src/Periphery/Permit2Proxy.sol (8)

10-11: LGTM! Proper imports for EIP-1271 support.

The new imports for IERC1271 and InvalidSignature are correctly added to support the EIP-1271 functionality. The imports follow the established pattern in the codebase.


16-17: LGTM! Updated documentation and version.

The NatSpec comment correctly reflects the new EIP-1271 capability, and the version bump to 1.1.0 appropriately indicates the addition of new functionality.


42-45: LGTM! Standard EIP-1271 magic value constant.

The constant correctly uses the standard EIP-1271 magic value 0x1626ba7e and follows proper naming conventions.


92-102: LGTM! Clean refactoring to use helper function.

The refactoring to use _handleEOAPermit improves code reusability and maintains the same functionality while preparing for the bytes signature variant.


135-136: LGTM! Proper deadline validation.

The deadline check at the beginning of the function correctly prevents expired signatures from being processed, improving security.


138-142: LGTM! Proper delegated EOA detection logic.

The prefix detection logic correctly identifies delegated EOA signatures using the 0xef0100 prefix and validates the expected total length of 68 bytes (3-byte prefix + 65-byte signature).


144-163: LGTM! Appropriate routing logic for signature types.

The routing logic correctly distinguishes between EOAs (including delegated EOAs) and smart contract wallets, directing each to the appropriate handling function.


297-357: LGTM! Robust EOA permit handling with error management.

The _handleEOAPermit function properly:

  • Handles delegated signature unpacking
  • Validates signature length (65 bytes)
  • Extracts signature components using assembly
  • Implements comprehensive error handling for permit failures
  • Falls back to checking existing allowances when permit fails

The error handling covers various failure modes while still allowing transactions to proceed if adequate allowance exists.

test/solidity/Periphery/Permit2Proxy.t.sol (8)

12-12: LGTM! Proper error imports for testing.

The import of InvalidSignature and TransferFromFailed errors correctly supports the expanded test scenarios for EIP-1271 functionality.


83-101: LGTM! Comprehensive EIP-1271 mock implementations.

The mock contracts properly implement the EIP-1271 interface:

  • MockEIP1271Wallet correctly returns the magic value for authorized hashes
  • MaliciousEIP1271Wallet appropriately returns wrong magic value for testing invalid scenarios
  • Both follow standard EIP-1271 patterns used in production wallets

1047-1088: LGTM! Comprehensive EOA bytes format test.

The test properly validates the new bytes signature format for EOAs, including proper EIP-2612 digest generation and signature packing. The test flow correctly mirrors the expected usage pattern.


1090-1145: LGTM! Excellent delegated EOA test with realistic scenario.

The test properly simulates the delegated EOA flow with the 0xef0100 prefix, correctly demonstrating how smart contracts can submit transactions with delegated EOA signatures. The comments clearly explain the use case.


1147-1178: LGTM! Thorough error condition testing.

The tests properly validate:

  • Invalid prefix length detection (67 instead of 68 bytes)
  • Invalid signature length for EOAs (64 instead of 65 bytes)
  • Both tests correctly expect the InvalidSignature error

Also applies to: 1180-1206


1208-1259: LGTM! Comprehensive smart wallet success test.

The test properly validates the complete EIP-1271 flow:

  • Correct message hash construction matching the implementation
  • Proper wallet authorization setup
  • Token funding and approval
  • Successful execution verification

The test demonstrates the expected integration pattern for smart contract wallets.


1261-1290: LGTM! Comprehensive error scenario coverage.

The tests thoroughly validate error conditions:

  • Invalid signature (unauthorized hash)
  • Wrong magic value from malicious wallet
  • Missing token approval (expecting TransferFromFailed)

These tests provide excellent coverage of failure modes in EIP-1271 interactions.

Also applies to: 1292-1319, 1321-1360


1362-1391: LGTM! Proper deadline expiration test.

The test correctly validates that expired deadlines are rejected with InvalidSignature error, confirming the deadline validation logic works as expected.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (4)
docs/Permit2Proxy.md (2)

20-41: Old prototype doc is stale and has a wrong param description + missing return.

  • Fix param doc: diamondCalldata is calldata to execute (not “Address of the token to be bridged” on Line 31).
  • Add the return type to the snippet to match the contract: returns (bytes memory).

Apply this diff to the snippet:

-/// @param diamondCalldata Address of the token to be bridged
+/// @param diamondCalldata calldata to execute
 ) public payable
+  returns (bytes memory)

43-46: Clarify delegated-EOA format and scope.

Document that delegated-EOA signatures are exactly 68 bytes (0xef0100 prefix + 65-byte ECDSA) and only supported on the EOA path. This avoids ambiguity with 1271 signatures.

src/Periphery/Permit2Proxy.sol (2)

119-127: Name is misleading now.

This overload also handles 1271 signatures; consider renaming to callDiamondWithSignature(...) and keeping the legacy EIP2612-only overload for backward compatibility. Not blocking.


294-354: Gas/copying: slice delegated signature without byte-by-byte loop; normalize v.

  • Replace the 65-iteration copy with an assembly slice.
  • Normalize v to 27/28 for broader token compatibility.

Apply this diff:

     ) internal {
-        bytes memory actualSignature;
-
-        // Handle delegated signature by stripping prefix
-        if (isDelegatedEOA) {
-            // Skip the 3-byte prefix and extract the 65-byte signature
-            actualSignature = new bytes(65);
-            for (uint256 i = 0; i < 65; i++) {
-                actualSignature[i] = signature[i + 3];
-            }
-        } else {
-            actualSignature = signature;
-        }
+        bytes memory actualSignature = signature;
+        if (isDelegatedEOA) {
+            assembly {
+                // actualSignature := signature[3:68]
+                let len := 65
+                actualSignature := mload(0x40)
+                // new free mem = ptr + 32(length) + 65(padded to 96)
+                mstore(0x40, add(actualSignature, 0x80))
+                mstore(actualSignature, len)
+                // copy 65 bytes from signature+3 into actualSignature+32
+                // calldataload/mload depending on location; here `signature` is in memory
+                // so copy from memory: use memcpy
+                // copy 64 bytes
+                mstore(add(actualSignature, 0x20), mload(add(signature, 0x23)))
+                mstore(add(actualSignature, 0x40), mload(add(signature, 0x43)))
+                mstore8(add(actualSignature, 0x60), byte(0, mload(add(signature, 0x63))))
+            }
+        }
@@
         assembly {
             r := mload(add(actualSignature, 0x20))
             s := mload(add(actualSignature, 0x40))
             v := byte(0, mload(add(actualSignature, 0x60)))
         }
+        if (v < 27) v += 27;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between b0371e7 and 2a3a8f8.

📒 Files selected for processing (2)
  • docs/Permit2Proxy.md (3 hunks)
  • src/Periphery/Permit2Proxy.sol (5 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
{src,test/solidity,script}/**/*.sol

📄 CodeRabbit inference engine (conventions.md)

{src,test/solidity,script}/**/*.sol: All Solidity files must start with a license identifier, followed by a blank line, then the pragma statement
Use SPDX license identifier // SPDX-License-Identifier: LGPL-3.0-only for all first-party Solidity files
Error names must be descriptive PascalCase, use custom errors instead of revert() strings, and omit error messages for gas optimization
Solidity variable/function naming: state variables camelCase; function params camelCase with leading underscore; constants and immutables in CONSTANT_CASE; private/internal variables camelCase; function names camelCase

Files:

  • src/Periphery/Permit2Proxy.sol
src/**/*.sol

📄 CodeRabbit inference engine (conventions.md)

src/**/*.sol: Every contract must include NatSpec header in this order: @title, @author LI.FI (https://li.fi), @notice, @Custom:version X.Y.Z
All public/external functions must have NatSpec comments documenting purpose, params, and returns
Add inline comments for complex logic, optimizations, gas-saving techniques, and math steps
Emit GenericSwapCompleted after successful same-chain swap completion

Files:

  • src/Periphery/Permit2Proxy.sol
**/*.sol

📄 CodeRabbit inference engine (conventions.md)

Follow solhint rules as configured in .solhint.json

Files:

  • src/Periphery/Permit2Proxy.sol
{README.md,Deploy.md,docs/**}

📄 CodeRabbit inference engine (conventions.md)

Keep primary documentation up to date: README.md (overview/setup), Deploy.md (deployment), and docs/ (technical docs)

Files:

  • docs/Permit2Proxy.md
🧠 Learnings (16)
📚 Learning: 2024-10-09T03:47:21.269Z
Learnt from: 0xDEnYO
PR: lifinance/contracts#782
File: src/Periphery/Permit2Proxy.sol:75-108
Timestamp: 2024-10-09T03:47:21.269Z
Learning: In the `Permit2Proxy` contract (`src/Periphery/Permit2Proxy.sol`), reentrancy protection is not necessary for functions like `callDiamondWithEIP2612Signature` when calling our own trusted diamond contract (`LIFI_DIAMOND`).

Applied to files:

  • src/Periphery/Permit2Proxy.sol
  • docs/Permit2Proxy.md
📚 Learning: 2025-08-26T12:11:21.073Z
Learnt from: mirooon
PR: lifinance/contracts#1328
File: src/Periphery/Lda/Facets/CoreRouteFacet.sol:4-6
Timestamp: 2025-08-26T12:11:21.073Z
Learning: OpenZeppelin's SafeERC20 library provides safePermit functionality for IERC20Permit tokens. The pattern "using SafeERC20 for IERC20Permit;" enables calling safePermit on IERC20Permit tokens safely. This functionality is available in recent versions of OpenZeppelin contracts.

Applied to files:

  • src/Periphery/Permit2Proxy.sol
  • docs/Permit2Proxy.md
📚 Learning: 2025-08-26T12:11:21.073Z
Learnt from: mirooon
PR: lifinance/contracts#1328
File: src/Periphery/Lda/Facets/CoreRouteFacet.sol:4-6
Timestamp: 2025-08-26T12:11:21.073Z
Learning: In the lifinance/contracts repository, OpenZeppelin's SafeERC20 library provides safePermit functionality for IERC20Permit tokens. This is confirmed by working implementations in both CoreRouteFacet.sol and LiFiDEXAggregator.sol that use the pattern "using SafeERC20 for IERC20Permit;" and successfully call safePermit with permit parameters.

Applied to files:

  • src/Periphery/Permit2Proxy.sol
  • docs/Permit2Proxy.md
📚 Learning: 2025-08-26T12:11:21.073Z
Learnt from: mirooon
PR: lifinance/contracts#1328
File: src/Periphery/Lda/Facets/CoreRouteFacet.sol:4-6
Timestamp: 2025-08-26T12:11:21.073Z
Learning: OpenZeppelin's SafeERC20 library provides safePermit functionality for IERC20Permit tokens in modern versions. The usage pattern "using SafeERC20 for IERC20Permit;" is valid and enables safe permit operations. This is confirmed to be working in the lifinance/contracts codebase.

Applied to files:

  • src/Periphery/Permit2Proxy.sol
  • docs/Permit2Proxy.md
📚 Learning: 2025-08-26T12:11:21.073Z
Learnt from: mirooon
PR: lifinance/contracts#1328
File: src/Periphery/Lda/Facets/CoreRouteFacet.sol:4-6
Timestamp: 2025-08-26T12:11:21.073Z
Learning: OpenZeppelin's SafeERC20 library provides safePermit functionality for IERC20Permit tokens. This is confirmed by actual usage in the lifinance/contracts codebase where safePermit is used in multiple files including CoreRouteFacet.sol and LiFiDEXAggregator.sol. The pattern "using SafeERC20 for IERC20Permit;" is valid and working.

Applied to files:

  • src/Periphery/Permit2Proxy.sol
  • docs/Permit2Proxy.md
📚 Learning: 2024-10-14T08:23:38.076Z
Learnt from: 0xDEnYO
PR: lifinance/contracts#831
File: src/Helpers/WithdrawablePeriphery.sol:26-30
Timestamp: 2024-10-14T08:23:38.076Z
Learning: In the `WithdrawablePeriphery.sol` contract, when transferring Ether in the `withdrawToken` function, prefer using `SafeTransferLib.safeTransferETH` instead of low-level `call` for safer Ether transfers.

Applied to files:

  • src/Periphery/Permit2Proxy.sol
📚 Learning: 2025-08-26T12:11:21.073Z
Learnt from: mirooon
PR: lifinance/contracts#1328
File: src/Periphery/Lda/Facets/CoreRouteFacet.sol:4-6
Timestamp: 2025-08-26T12:11:21.073Z
Learning: OpenZeppelin's SafeERC20 library provides safePermit functionality for IERC20Permit tokens. The using SafeERC20 for IERC20Permit; directive enables calling safePermit on IERC20Permit tokens safely.

Applied to files:

  • src/Periphery/Permit2Proxy.sol
  • docs/Permit2Proxy.md
📚 Learning: 2025-07-16T01:03:08.106Z
Learnt from: 0xDEnYO
PR: lifinance/contracts#1275
File: src/Facets/AllBridgeFacet.sol:164-164
Timestamp: 2025-07-16T01:03:08.106Z
Learning: In src/Facets/AllBridgeFacet.sol, the team has decided that explicit validation for address downcasting from `_bridgeData.sendingAssetId` to `bytes32(uint256(uint160(_bridgeData.sendingAssetId)))` is not required, accepting the potential risk of silent overflow from unsafe downcasting.

Applied to files:

  • src/Periphery/Permit2Proxy.sol
📚 Learning: 2024-12-02T06:33:33.309Z
Learnt from: ezynda3
PR: lifinance/contracts#827
File: src/Facets/DeBridgeDlnFacet.sol:0-0
Timestamp: 2024-12-02T06:33:33.309Z
Learning: In Solidity version 0.8.0 and above, arithmetic underflows and overflows automatically cause a revert; therefore, explicit checks for arithmetic underflows are not necessary in functions like `_startBridge` in `DeBridgeDlnFacet.sol`.

Applied to files:

  • src/Periphery/Permit2Proxy.sol
📚 Learning: 2024-09-23T02:04:16.323Z
Learnt from: 0xDEnYO
PR: lifinance/contracts#807
File: src/Periphery/GasZipPeriphery.sol:49-53
Timestamp: 2024-09-23T02:04:16.323Z
Learning: When `LibAsset.maxApproveERC20` is used with `type(uint256).max`, the team acknowledges and accepts the associated security risks. In future reviews, avoid flagging this as a concern.

Applied to files:

  • src/Periphery/Permit2Proxy.sol
📚 Learning: 2024-11-05T17:16:19.946Z
Learnt from: maxklenk
PR: lifinance/contracts#782
File: script/demoScripts/demoPermit2.ts:0-0
Timestamp: 2024-11-05T17:16:19.946Z
Learning: In `script/demoScripts/demoPermit2.ts`, the `nextNonce` function should be called on the `PERMIT2_PROXY_ADDRESS` because the nonce is stored in the proxy contract, not in the `Permit2` contract (`PERMIT2_ADDRESS`).

Applied to files:

  • src/Periphery/Permit2Proxy.sol
📚 Learning: 2025-07-15T05:05:28.134Z
Learnt from: 0xDEnYO
PR: lifinance/contracts#1140
File: config/permit2Proxy.json:3-3
Timestamp: 2025-07-15T05:05:28.134Z
Learning: In config/permit2Proxy.json, the addresses represent the Permit2 contract addresses on each chain, NOT the Permit2Proxy contract addresses. These Permit2 addresses are used as constructor arguments when deploying the Permit2Proxy contract. The Permit2Proxy acts as a wrapper/proxy that interacts with the underlying Permit2 contract on each network.

Applied to files:

  • src/Periphery/Permit2Proxy.sol
📚 Learning: 2025-02-11T10:33:52.791Z
Learnt from: 0xDEnYO
PR: lifinance/contracts#988
File: script/deploy/facets/DeployPermit2Proxy.s.sol:33-37
Timestamp: 2025-02-11T10:33:52.791Z
Learning: The Permit2Proxy contract must not be deployed with zero addresses for its critical dependencies (LiFiDiamond, permit2Address, safeAddress). This is enforced by passing `false` to `_getConfigContractAddress` function.

Applied to files:

  • src/Periphery/Permit2Proxy.sol
📚 Learning: 2024-11-25T09:05:43.045Z
Learnt from: 0xDEnYO
PR: lifinance/contracts#782
File: deployments/base.diamond.json:148-148
Timestamp: 2024-11-25T09:05:43.045Z
Learning: In deployment configuration files (e.g., `deployments/base.diamond.json`), empty addresses for contracts like `Permit2Proxy` may be placeholders and will be updated after approvals are released from the multisig safe.

Applied to files:

  • src/Periphery/Permit2Proxy.sol
📚 Learning: 2025-08-27T23:36:40.773Z
Learnt from: mirooon
PR: lifinance/contracts#1328
File: test/solidity/Periphery/GasZipPeriphery.t.sol:223-233
Timestamp: 2025-08-27T23:36:40.773Z
Learning: In bridge facet swap flows with requiresDeposit=false, tokens are already held by the diamond contract from previous swaps. When subsequent contracts like GasZipPeriphery call LibAsset.depositAsset, they pull tokens from msg.sender (the diamond) to themselves via transferFrom, requiring no additional approvals since the diamond already owns the tokens.

Applied to files:

  • src/Periphery/Permit2Proxy.sol
  • docs/Permit2Proxy.md
📚 Learning: 2024-10-31T09:09:38.568Z
Learnt from: ezynda3
PR: lifinance/contracts#843
File: src/Facets/RelayFacet.sol:165-170
Timestamp: 2024-10-31T09:09:38.568Z
Learning: In the `RelayFacet` contract (`src/Facets/RelayFacet.sol`), within the `_startBridge` function, low-level `call` is intentionally used to transfer tokens so that extra bytes can be added to the calldata, as required for integrating with Relay Protocol.

Applied to files:

  • docs/Permit2Proxy.md
🪛 GitHub Check: Olympix Integrated Security
src/Periphery/Permit2Proxy.sol

[notice] 139-139:
Performing a narrowing downcast may result in silent overflow due to bit truncation. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/unsafe-downcast


[failure] 356-356:
The contract is vulnerable to signature replay attacks, potentially allowing malicious actors to reuse valid signatures. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/signature-replay-attacks

🔇 Additional comments (7)
docs/Permit2Proxy.md (1)

140-140: LGTM – helpful dev note.

src/Periphery/Permit2Proxy.sol (6)

10-11: Good: IERC1271 + InvalidSignature imported.


16-17: NatSpec and version bump look correct.


42-45: Good: ERC1271 magic value constant added.


92-103: Reasonable: pack v/r/s then reuse the EOA path.

Minor: you can pass the 65-byte signature directly without packing if you already had it, but current approach is fine.


119-174: Critical: 1271 path is replayable; add nonce + domain separation and consume it.

Current 1271 hash lacks nonce/chain/contract binding. A valid signature can be replayed multiple times while deadline is valid and across chains/contracts. Gate with a per-wallet nonce and include chainId + contract in the hash; consume nonce on use.

Apply this diff to the public entry point (adds nonce and passes it through):

-    /// @param signature Packed signature bytes (r,s,v for EOAs, arbitrary format for smart contracts)
+    /// @param signature Packed signature bytes (r,s,v for EOAs, arbitrary format for smart contracts)
+    /// @param nonce     Replay‑protection nonce for 1271 wallets (ignored for EOA path)
     /// @param diamondCalldata calldata to execute
-    function callDiamondWithEIP2612Signature(
+    function callDiamondWithEIP2612Signature(
         address tokenAddress,
         uint256 amount,
         uint256 deadline,
-        bytes calldata signature,
+        bytes calldata signature,
+        uint256 nonce,
         bytes calldata diamondCalldata
     ) public payable returns (bytes memory) {
@@
-        if (msg.sender.code.length == 0 || isDelegatedEOA) {
+        if (msg.sender.code.length == 0 || isDelegatedEOA) {
             // EOA path - handle permit
             _handleEOAPermit(
                 tokenAddress,
                 amount,
                 deadline,
                 signature,
                 isDelegatedEOA
             );
         } else {
             // Smart contract wallet path - handle EIP-1271
-            _handleEIP1271(
+            _handleEIP1271(
                 tokenAddress,
                 amount,
                 deadline,
-                signature,
+                signature,
+                nonce,
                 diamondCalldata
             );
         }

Add storage and expose current nonces (place in “Storage” section):

// 1271 per-wallet nonces for replay protection
mapping(address => uint256) public eip1271Nonces;

And update _handleEIP1271 as in my later comment.


356-384: Critical: 1271 message hash lacks nonce/chain/contract; consume nonce on success.

Bind the signature to this contract and chain and make it one‑time via per‑wallet nonce consumption.

Apply this diff:

-    ) internal view {
-        // Construct message hash for validation
-        bytes32 messageHash = keccak256(
-            abi.encode(
-                keccak256(
-                    "PermitProxyCall(address token,uint256 amount,uint256 deadline,bytes32 calldataHash)"
-                ),
-                tokenAddress,
-                amount,
-                deadline,
-                keccak256(diamondCalldata)
-            )
-        );
+        uint256 nonce,
+        bytes calldata diamondCalldata
+    ) internal {
+        // Enforce expected nonce and build replay-protected domain-bound hash
+        uint256 expected = eip1271Nonces[msg.sender];
+        if (nonce != expected) revert InvalidSignature();
+        bytes32 messageHash = keccak256(
+            abi.encode(
+                keccak256(
+                    "PermitProxyCall(address token,uint256 amount,uint256 deadline,bytes32 calldataHash,address verifyingContract,uint256 chainId,uint256 nonce)"
+                ),
+                tokenAddress,
+                amount,
+                deadline,
+                keccak256(diamondCalldata),
+                address(this),
+                block.chainid,
+                nonce
+            )
+        );
@@
-        if (magicValue != ERC1271_MAGIC_VALUE) {
-            revert InvalidSignature();
-        }
+        if (magicValue != ERC1271_MAGIC_VALUE) revert InvalidSignature();
+        // Consume nonce
+        unchecked { eip1271Nonces[msg.sender] = expected + 1; }

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
src/Periphery/Permit2Proxy.sol (2)

24-26: Good: per-wallet nonces for 1271 replay protection; consider naming clarity

“nonces” can be confused with Permit2 bitmap nonces already exposed in this contract. Consider renaming to eip1271Nonces for clarity.

Apply this minimal diff:

-    mapping(address => uint256) public nonces;
+    mapping(address => uint256) public eip1271Nonces;

And in _handleEIP1271:

-        uint256 currentNonce = nonces[msg.sender];
+        uint256 currentNonce = eip1271Nonces[msg.sender];
...
-        nonces[msg.sender] = currentNonce + 1;
+        eip1271Nonces[msg.sender] = currentNonce + 1;

281-327: Normalize ECDSA v for broader signer compatibility

Some signers/libraries emit v as 0/1. Normalize to 27/28 before calling permit to avoid brittle failures.

Apply:

         assembly {
             r := mload(add(signature, 0x20))
             s := mload(add(signature, 0x40))
             v := byte(0, mload(add(signature, 0x60)))
         }
+        if (v < 27) {
+            v += 27;
+        }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 2a3a8f8 and f35e574.

📒 Files selected for processing (3)
  • docs/Permit2Proxy.md (3 hunks)
  • src/Periphery/Permit2Proxy.sol (5 hunks)
  • test/solidity/Periphery/Permit2Proxy.t.sol (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • test/solidity/Periphery/Permit2Proxy.t.sol
  • docs/Permit2Proxy.md
🧰 Additional context used
📓 Path-based instructions (3)
{src,test/solidity,script}/**/*.sol

📄 CodeRabbit inference engine (conventions.md)

{src,test/solidity,script}/**/*.sol: All Solidity files must start with a license identifier, followed by a blank line, then the pragma statement
Use SPDX license identifier // SPDX-License-Identifier: LGPL-3.0-only for all first-party Solidity files
Error names must be descriptive PascalCase, use custom errors instead of revert() strings, and omit error messages for gas optimization
Solidity variable/function naming: state variables camelCase; function params camelCase with leading underscore; constants and immutables in CONSTANT_CASE; private/internal variables camelCase; function names camelCase

Files:

  • src/Periphery/Permit2Proxy.sol
src/**/*.sol

📄 CodeRabbit inference engine (conventions.md)

src/**/*.sol: Every contract must include NatSpec header in this order: @title, @author LI.FI (https://li.fi), @notice, @Custom:version X.Y.Z
All public/external functions must have NatSpec comments documenting purpose, params, and returns
Add inline comments for complex logic, optimizations, gas-saving techniques, and math steps
Emit GenericSwapCompleted after successful same-chain swap completion

Files:

  • src/Periphery/Permit2Proxy.sol
**/*.sol

📄 CodeRabbit inference engine (conventions.md)

Follow solhint rules as configured in .solhint.json

Files:

  • src/Periphery/Permit2Proxy.sol
🧠 Learnings (14)
📚 Learning: 2024-10-09T03:47:21.269Z
Learnt from: 0xDEnYO
PR: lifinance/contracts#782
File: src/Periphery/Permit2Proxy.sol:75-108
Timestamp: 2024-10-09T03:47:21.269Z
Learning: In the `Permit2Proxy` contract (`src/Periphery/Permit2Proxy.sol`), reentrancy protection is not necessary for functions like `callDiamondWithEIP2612Signature` when calling our own trusted diamond contract (`LIFI_DIAMOND`).

Applied to files:

  • src/Periphery/Permit2Proxy.sol
📚 Learning: 2025-08-26T12:11:21.073Z
Learnt from: mirooon
PR: lifinance/contracts#1328
File: src/Periphery/Lda/Facets/CoreRouteFacet.sol:4-6
Timestamp: 2025-08-26T12:11:21.073Z
Learning: In the lifinance/contracts repository, OpenZeppelin's SafeERC20 library provides safePermit functionality for IERC20Permit tokens. This is confirmed by working implementations in both CoreRouteFacet.sol and LiFiDEXAggregator.sol that use the pattern "using SafeERC20 for IERC20Permit;" and successfully call safePermit with permit parameters.

Applied to files:

  • src/Periphery/Permit2Proxy.sol
📚 Learning: 2025-08-26T12:11:21.073Z
Learnt from: mirooon
PR: lifinance/contracts#1328
File: src/Periphery/Lda/Facets/CoreRouteFacet.sol:4-6
Timestamp: 2025-08-26T12:11:21.073Z
Learning: OpenZeppelin's SafeERC20 library provides safePermit functionality for IERC20Permit tokens. The pattern "using SafeERC20 for IERC20Permit;" enables calling safePermit on IERC20Permit tokens safely. This functionality is available in recent versions of OpenZeppelin contracts.

Applied to files:

  • src/Periphery/Permit2Proxy.sol
📚 Learning: 2025-08-26T12:11:21.073Z
Learnt from: mirooon
PR: lifinance/contracts#1328
File: src/Periphery/Lda/Facets/CoreRouteFacet.sol:4-6
Timestamp: 2025-08-26T12:11:21.073Z
Learning: OpenZeppelin's SafeERC20 library provides safePermit functionality for IERC20Permit tokens in modern versions. The usage pattern "using SafeERC20 for IERC20Permit;" is valid and enables safe permit operations. This is confirmed to be working in the lifinance/contracts codebase.

Applied to files:

  • src/Periphery/Permit2Proxy.sol
📚 Learning: 2025-07-16T01:03:08.106Z
Learnt from: 0xDEnYO
PR: lifinance/contracts#1275
File: src/Facets/AllBridgeFacet.sol:164-164
Timestamp: 2025-07-16T01:03:08.106Z
Learning: In src/Facets/AllBridgeFacet.sol, the team has decided that explicit validation for address downcasting from `_bridgeData.sendingAssetId` to `bytes32(uint256(uint160(_bridgeData.sendingAssetId)))` is not required, accepting the potential risk of silent overflow from unsafe downcasting.

Applied to files:

  • src/Periphery/Permit2Proxy.sol
📚 Learning: 2024-12-02T06:33:33.309Z
Learnt from: ezynda3
PR: lifinance/contracts#827
File: src/Facets/DeBridgeDlnFacet.sol:0-0
Timestamp: 2024-12-02T06:33:33.309Z
Learning: In Solidity version 0.8.0 and above, arithmetic underflows and overflows automatically cause a revert; therefore, explicit checks for arithmetic underflows are not necessary in functions like `_startBridge` in `DeBridgeDlnFacet.sol`.

Applied to files:

  • src/Periphery/Permit2Proxy.sol
📚 Learning: 2024-09-23T02:04:16.323Z
Learnt from: 0xDEnYO
PR: lifinance/contracts#807
File: src/Periphery/GasZipPeriphery.sol:49-53
Timestamp: 2024-09-23T02:04:16.323Z
Learning: When `LibAsset.maxApproveERC20` is used with `type(uint256).max`, the team acknowledges and accepts the associated security risks. In future reviews, avoid flagging this as a concern.

Applied to files:

  • src/Periphery/Permit2Proxy.sol
📚 Learning: 2024-11-05T17:16:19.946Z
Learnt from: maxklenk
PR: lifinance/contracts#782
File: script/demoScripts/demoPermit2.ts:0-0
Timestamp: 2024-11-05T17:16:19.946Z
Learning: In `script/demoScripts/demoPermit2.ts`, the `nextNonce` function should be called on the `PERMIT2_PROXY_ADDRESS` because the nonce is stored in the proxy contract, not in the `Permit2` contract (`PERMIT2_ADDRESS`).

Applied to files:

  • src/Periphery/Permit2Proxy.sol
📚 Learning: 2025-08-26T12:11:21.073Z
Learnt from: mirooon
PR: lifinance/contracts#1328
File: src/Periphery/Lda/Facets/CoreRouteFacet.sol:4-6
Timestamp: 2025-08-26T12:11:21.073Z
Learning: OpenZeppelin's SafeERC20 library provides safePermit functionality for IERC20Permit tokens. This is confirmed by actual usage in the lifinance/contracts codebase where safePermit is used in multiple files including CoreRouteFacet.sol and LiFiDEXAggregator.sol. The pattern "using SafeERC20 for IERC20Permit;" is valid and working.

Applied to files:

  • src/Periphery/Permit2Proxy.sol
📚 Learning: 2025-07-15T05:05:28.134Z
Learnt from: 0xDEnYO
PR: lifinance/contracts#1140
File: config/permit2Proxy.json:3-3
Timestamp: 2025-07-15T05:05:28.134Z
Learning: In config/permit2Proxy.json, the addresses represent the Permit2 contract addresses on each chain, NOT the Permit2Proxy contract addresses. These Permit2 addresses are used as constructor arguments when deploying the Permit2Proxy contract. The Permit2Proxy acts as a wrapper/proxy that interacts with the underlying Permit2 contract on each network.

Applied to files:

  • src/Periphery/Permit2Proxy.sol
📚 Learning: 2025-02-11T10:33:52.791Z
Learnt from: 0xDEnYO
PR: lifinance/contracts#988
File: script/deploy/facets/DeployPermit2Proxy.s.sol:33-37
Timestamp: 2025-02-11T10:33:52.791Z
Learning: The Permit2Proxy contract must not be deployed with zero addresses for its critical dependencies (LiFiDiamond, permit2Address, safeAddress). This is enforced by passing `false` to `_getConfigContractAddress` function.

Applied to files:

  • src/Periphery/Permit2Proxy.sol
📚 Learning: 2025-08-26T12:11:21.073Z
Learnt from: mirooon
PR: lifinance/contracts#1328
File: src/Periphery/Lda/Facets/CoreRouteFacet.sol:4-6
Timestamp: 2025-08-26T12:11:21.073Z
Learning: OpenZeppelin's SafeERC20 library provides safePermit functionality for IERC20Permit tokens. The using SafeERC20 for IERC20Permit; directive enables calling safePermit on IERC20Permit tokens safely.

Applied to files:

  • src/Periphery/Permit2Proxy.sol
📚 Learning: 2024-11-25T09:05:43.045Z
Learnt from: 0xDEnYO
PR: lifinance/contracts#782
File: deployments/base.diamond.json:148-148
Timestamp: 2024-11-25T09:05:43.045Z
Learning: In deployment configuration files (e.g., `deployments/base.diamond.json`), empty addresses for contracts like `Permit2Proxy` may be placeholders and will be updated after approvals are released from the multisig safe.

Applied to files:

  • src/Periphery/Permit2Proxy.sol
📚 Learning: 2025-08-27T23:36:40.773Z
Learnt from: mirooon
PR: lifinance/contracts#1328
File: test/solidity/Periphery/GasZipPeriphery.t.sol:223-233
Timestamp: 2025-08-27T23:36:40.773Z
Learning: In bridge facet swap flows with requiresDeposit=false, tokens are already held by the diamond contract from previous swaps. When subsequent contracts like GasZipPeriphery call LibAsset.depositAsset, they pull tokens from msg.sender (the diamond) to themselves via transferFrom, requiring no additional approvals since the diamond already owns the tokens.

Applied to files:

  • src/Periphery/Permit2Proxy.sol
🔇 Additional comments (6)
src/Periphery/Permit2Proxy.sol (6)

10-11: LGTM: 1271 + error imports added

Interfaces and error type are appropriate for the new validation path.


16-18: Version/NatSpec updated — propagate docs and references

Please update any docs, deployment notes, and integrator references to 1.1.0 to avoid mismatches.


47-48: LGTM: ERC‑1271 magic value constant

Constant is correct and scoped appropriately.


95-99: Packing v,r,s into bytes is fine

No issues; matches extraction logic below.


329-368: Replay protection for 1271: solid fix; includes nonce, chainId, and contract domain

This addresses prior “signature replay” findings by binding to nonce, chain, and verifying contract and consuming the nonce before external effects.

Please ensure tests cover:

  • Cross‑chain replay attempts fail (different chainId).
  • Cross‑contract replay attempts fail (different verifying contract).
  • Same‑nonce double‑spend fails even with reentrancy via LIFI_DIAMOND.

You can grep tests quickly:

#!/bin/bash
rg -nP -C2 'isValidSignature|ERC1271_MAGIC_VALUE|nonce[s]?\\[|chainid|contractAddress' --type=sol test

116-130: Rename overloaded function to callDiamondWithSignature, add NatSpec @return, use leading-underscore params, and keep a backward-compatible alias

File: src/Periphery/Permit2Proxy.sol:116-130

  • Rename the misleading overload to callDiamondWithSignature and update NatSpec: @param _tokenAddress, _param _amount, @param _deadline, @param _signature, @param _diamondCalldata; add @return data (bytes).
  • Use leading-underscore param names throughout the body (check _deadline, call _handleEOAPermit/_handleEIP1271 with underscored args, transfer/approve using _tokenAddress/_amount, and call _executeCalldata(_diamondCalldata)).
  • Keep a thin external wrapper named callDiamondWithEIP2612Signature(...) that delegates to callDiamondWithSignature for backward compatibility.
  • Verify repository and integrator impact before merging (search callers, e.g., rg -nP '\bcallDiamondWithEIP2612Signature\s*(') to avoid breaking external integrations.

@ezynda3 ezynda3 marked this pull request as ready for review September 22, 2025 15:23
@lifi-action-bot
Copy link
Collaborator

🤖 GitHub Action: Security Alerts Review 🔍

🟢 Dismissed Security Alerts with Comments
The following alerts were dismissed with proper comments:

🟢 View Alert - File: src/Periphery/Permit2Proxy.sol
🔹 The contract uses low-level calls without properly verifying the input parameters, potentially leading to unexpected behavior or vulnerabilities. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/low-level-call-params-verified
🔹 Dismiss Reason: Won't fix
🔹 Dismiss Comment: Valid finding but expected behaviour. We will call our diamond with the diamondCalldata.

🟢 View Alert - File: src/Periphery/Permit2Proxy.sol
🔹 Making an external call without a gas budget may consume all of the transaction's gas, causing it to revert. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/call-without-gas-budget
🔹 Dismiss Reason: Won't fix
🔹 Dismiss Comment: That is expected behaviour. We do not want to reserve gas.

🟢 View Alert - File: src/Periphery/Permit2Proxy.sol
🔹 The contract is vulnerable to signature replay attacks, potentially allowing malicious actors to reuse valid signatures. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/signature-replay-attacks
🔹 Dismiss Reason: False positive
🔹 Dismiss Comment: replay protection is inherently provided by the underlying nonce mechanisms in the EIP-2612 and Permit2 implementations

🟢 View Alert - File: src/Periphery/Permit2Proxy.sol
🔹 The contract is vulnerable to signature replay attacks, potentially allowing malicious actors to reuse valid signatures. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/signature-replay-attacks
🔹 Dismiss Reason: False positive
🔹 Dismiss Comment: replay protection is inherently provided by the underlying nonce mechanisms in the EIP-2612 and Permit2 implementations

🟢 View Alert - File: src/Periphery/Permit2Proxy.sol
🔹 Parameters passed to a constructor that are not validated for correct values may lead to contract creation in an undesired state. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/no-parameter-validation-in-constructor
🔹 Dismiss Reason: Won't fix
🔹 Dismiss Comment: This is a valid observation but can be skipped for now. The parameters are set during deployment by us, so there’s no immediate risk. It’s not urgent but can be improved later if needed

🟢 View Alert - File: src/Periphery/Permit2Proxy.sol
🔹 Parameters passed to a constructor that are not validated for correct values may lead to contract creation in an undesired state. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/no-parameter-validation-in-constructor
🔹 Dismiss Reason: Won't fix
🔹 Dismiss Comment: This is a valid observation but can be skipped for now. The addresses (_lifiDiamond and _permit2) are known contracts controlled or trusted by the deployerm so there’s no immediate risk. It’s not urgent but can be improved later

🟢 View Alert - File: src/Periphery/Permit2Proxy.sol
🔹 Performing a narrowing downcast may result in silent overflow due to bit truncation. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/unsafe-downcast
🔹 Dismiss Reason: False positive
🔹 Dismiss Comment: the narrowing cast is intentional and fits our nonce encoding scheme. The lower 8 bits extracted with uint8(start) are guaranteed to be within the valid range (0–255) by design

🟢 View Alert - File: src/Periphery/Permit2Proxy.sol
🔹 Performing a narrowing downcast may result in silent overflow due to bit truncation. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/unsafe-downcast
🔹 Dismiss Reason: False positive
🔹 Dismiss Comment: the cast from uint256 to uint248 is intentionally safe because start >> 8 is guaranteed not to exceed 2^248 - 1. so narrowing is safe

🟢 View Alert - File: src/Periphery/Permit2Proxy.sol
🔹 Using a payable fallback (including receive) with no explicit functionality may indicate incomplete contract logic. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/empty-payable-fallback
🔹 Dismiss Reason: False positive
🔹 Dismiss Comment: it's for receiving native token which later on can be withdrawn with WithdrawablePeriphery parent contract

No unresolved security alerts! 🎉

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (4)
test/solidity/Periphery/Permit2Proxy.t.sol (1)

1118-1177: Avoid duplicating the EIP‑1271 type string in tests

Recommend exposing PERMIT_PROXY_CALL_TYPEHASH (or a helper that returns the hash) from the proxy and referencing it here to eliminate drift risk if the type string changes.

Would you like me to add a view constant/getter in the proxy and adjust this test accordingly?

src/Periphery/Permit2Proxy.sol (3)

124-161: Naming/API clarity: EIP‑2612 function now accepts EIP‑1271 too

The overloaded bytes version handles both EOAs and 1271 wallets but keeps the EIP‑2612 name. Consider adding an alias (e.g., callDiamondWithSignature) that forwards to this overload to make intent clear without breaking ABI.


367-369: Optional: emit an event when consuming 1271 nonces

Helps off‑chain tracking and auditability.

Example:

event EIP1271NonceUsed(address indexed wallet, uint256 nonce);

Emit before increment: emit EIP1271NonceUsed(msg.sender, currentNonce);


131-147: Design note: 1271 path restricted to wallet-calls only

Current check requires msg.sender to be the wallet. If meta‑tx/relayer support is desired later, allow third parties to call with a wallet address parameter and verify via IERC1271(wallet).isValidSignature(...). Safe to defer.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between f35e574 and bd1fde6.

📒 Files selected for processing (2)
  • src/Periphery/Permit2Proxy.sol (5 hunks)
  • test/solidity/Periphery/Permit2Proxy.t.sol (3 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
{src,test/solidity,script}/**/*.sol

📄 CodeRabbit inference engine (conventions.md)

{src,test/solidity,script}/**/*.sol: All Solidity files must start with a license identifier, followed by a blank line, then the pragma statement
Use SPDX license identifier // SPDX-License-Identifier: LGPL-3.0-only for all first-party Solidity files
Error names must be descriptive PascalCase, use custom errors instead of revert() strings, and omit error messages for gas optimization
Solidity variable/function naming: state variables camelCase; function params camelCase with leading underscore; constants and immutables in CONSTANT_CASE; private/internal variables camelCase; function names camelCase

Files:

  • src/Periphery/Permit2Proxy.sol
  • test/solidity/Periphery/Permit2Proxy.t.sol
src/**/*.sol

📄 CodeRabbit inference engine (conventions.md)

src/**/*.sol: Every contract must include NatSpec header in this order: @title, @author LI.FI (https://li.fi), @notice, @Custom:version X.Y.Z
All public/external functions must have NatSpec comments documenting purpose, params, and returns
Add inline comments for complex logic, optimizations, gas-saving techniques, and math steps
Emit GenericSwapCompleted after successful same-chain swap completion

Files:

  • src/Periphery/Permit2Proxy.sol
**/*.sol

📄 CodeRabbit inference engine (conventions.md)

Follow solhint rules as configured in .solhint.json

Files:

  • src/Periphery/Permit2Proxy.sol
  • test/solidity/Periphery/Permit2Proxy.t.sol
test/solidity/**/*.t.sol

📄 CodeRabbit inference engine (conventions.md)

test/solidity/**/*.t.sol: Solidity test files must use .t.sol extension; organize imports with system libraries first, then project files
Test naming: successful tests prefixed test_, failure tests prefixed testRevert_, base/inherited tests prefixed testBase_; negative tests must check specific revert reasons
Each test contract must implement setUp() that configures block numbers, initializes base contracts, sets up facets, and uses vm.label; contracts inheriting TestBase.sol must call initTestBase() in setUp()
Use vm.startPrank()/vm.stopPrank() for user simulation; maintain blank-line formatting rules for tests (expectRevert separation, assertion grouping, spacing)
Use assertEq for equality, custom assertion modifiers for balance changes, vm.expectRevert with precise reasons, and vm.expectEmit(true,true,true,true,) to verify events and parameters

Files:

  • test/solidity/Periphery/Permit2Proxy.t.sol
🧠 Learnings (17)
📚 Learning: 2024-10-09T03:47:21.269Z
Learnt from: 0xDEnYO
PR: lifinance/contracts#782
File: src/Periphery/Permit2Proxy.sol:75-108
Timestamp: 2024-10-09T03:47:21.269Z
Learning: In the `Permit2Proxy` contract (`src/Periphery/Permit2Proxy.sol`), reentrancy protection is not necessary for functions like `callDiamondWithEIP2612Signature` when calling our own trusted diamond contract (`LIFI_DIAMOND`).

Applied to files:

  • src/Periphery/Permit2Proxy.sol
  • test/solidity/Periphery/Permit2Proxy.t.sol
📚 Learning: 2025-07-16T01:03:08.106Z
Learnt from: 0xDEnYO
PR: lifinance/contracts#1275
File: src/Facets/AllBridgeFacet.sol:164-164
Timestamp: 2025-07-16T01:03:08.106Z
Learning: In src/Facets/AllBridgeFacet.sol, the team has decided that explicit validation for address downcasting from `_bridgeData.sendingAssetId` to `bytes32(uint256(uint160(_bridgeData.sendingAssetId)))` is not required, accepting the potential risk of silent overflow from unsafe downcasting.

Applied to files:

  • src/Periphery/Permit2Proxy.sol
📚 Learning: 2024-12-02T06:33:33.309Z
Learnt from: ezynda3
PR: lifinance/contracts#827
File: src/Facets/DeBridgeDlnFacet.sol:0-0
Timestamp: 2024-12-02T06:33:33.309Z
Learning: In Solidity version 0.8.0 and above, arithmetic underflows and overflows automatically cause a revert; therefore, explicit checks for arithmetic underflows are not necessary in functions like `_startBridge` in `DeBridgeDlnFacet.sol`.

Applied to files:

  • src/Periphery/Permit2Proxy.sol
📚 Learning: 2024-09-23T02:04:16.323Z
Learnt from: 0xDEnYO
PR: lifinance/contracts#807
File: src/Periphery/GasZipPeriphery.sol:49-53
Timestamp: 2024-09-23T02:04:16.323Z
Learning: When `LibAsset.maxApproveERC20` is used with `type(uint256).max`, the team acknowledges and accepts the associated security risks. In future reviews, avoid flagging this as a concern.

Applied to files:

  • src/Periphery/Permit2Proxy.sol
📚 Learning: 2024-11-05T17:16:19.946Z
Learnt from: maxklenk
PR: lifinance/contracts#782
File: script/demoScripts/demoPermit2.ts:0-0
Timestamp: 2024-11-05T17:16:19.946Z
Learning: In `script/demoScripts/demoPermit2.ts`, the `nextNonce` function should be called on the `PERMIT2_PROXY_ADDRESS` because the nonce is stored in the proxy contract, not in the `Permit2` contract (`PERMIT2_ADDRESS`).

Applied to files:

  • src/Periphery/Permit2Proxy.sol
📚 Learning: 2025-08-26T12:11:21.073Z
Learnt from: mirooon
PR: lifinance/contracts#1328
File: src/Periphery/Lda/Facets/CoreRouteFacet.sol:4-6
Timestamp: 2025-08-26T12:11:21.073Z
Learning: In the lifinance/contracts repository, OpenZeppelin's SafeERC20 library provides safePermit functionality for IERC20Permit tokens. This is confirmed by working implementations in both CoreRouteFacet.sol and LiFiDEXAggregator.sol that use the pattern "using SafeERC20 for IERC20Permit;" and successfully call safePermit with permit parameters.

Applied to files:

  • src/Periphery/Permit2Proxy.sol
  • test/solidity/Periphery/Permit2Proxy.t.sol
📚 Learning: 2025-08-26T12:11:21.073Z
Learnt from: mirooon
PR: lifinance/contracts#1328
File: src/Periphery/Lda/Facets/CoreRouteFacet.sol:4-6
Timestamp: 2025-08-26T12:11:21.073Z
Learning: OpenZeppelin's SafeERC20 library provides safePermit functionality for IERC20Permit tokens. This is confirmed by actual usage in the lifinance/contracts codebase where safePermit is used in multiple files including CoreRouteFacet.sol and LiFiDEXAggregator.sol. The pattern "using SafeERC20 for IERC20Permit;" is valid and working.

Applied to files:

  • src/Periphery/Permit2Proxy.sol
  • test/solidity/Periphery/Permit2Proxy.t.sol
📚 Learning: 2025-07-15T05:05:28.134Z
Learnt from: 0xDEnYO
PR: lifinance/contracts#1140
File: config/permit2Proxy.json:3-3
Timestamp: 2025-07-15T05:05:28.134Z
Learning: In config/permit2Proxy.json, the addresses represent the Permit2 contract addresses on each chain, NOT the Permit2Proxy contract addresses. These Permit2 addresses are used as constructor arguments when deploying the Permit2Proxy contract. The Permit2Proxy acts as a wrapper/proxy that interacts with the underlying Permit2 contract on each network.

Applied to files:

  • src/Periphery/Permit2Proxy.sol
📚 Learning: 2025-08-26T12:11:21.073Z
Learnt from: mirooon
PR: lifinance/contracts#1328
File: src/Periphery/Lda/Facets/CoreRouteFacet.sol:4-6
Timestamp: 2025-08-26T12:11:21.073Z
Learning: OpenZeppelin's SafeERC20 library provides safePermit functionality for IERC20Permit tokens in modern versions. The usage pattern "using SafeERC20 for IERC20Permit;" is valid and enables safe permit operations. This is confirmed to be working in the lifinance/contracts codebase.

Applied to files:

  • src/Periphery/Permit2Proxy.sol
  • test/solidity/Periphery/Permit2Proxy.t.sol
📚 Learning: 2025-02-11T10:33:52.791Z
Learnt from: 0xDEnYO
PR: lifinance/contracts#988
File: script/deploy/facets/DeployPermit2Proxy.s.sol:33-37
Timestamp: 2025-02-11T10:33:52.791Z
Learning: The Permit2Proxy contract must not be deployed with zero addresses for its critical dependencies (LiFiDiamond, permit2Address, safeAddress). This is enforced by passing `false` to `_getConfigContractAddress` function.

Applied to files:

  • src/Periphery/Permit2Proxy.sol
📚 Learning: 2025-08-26T12:11:21.073Z
Learnt from: mirooon
PR: lifinance/contracts#1328
File: src/Periphery/Lda/Facets/CoreRouteFacet.sol:4-6
Timestamp: 2025-08-26T12:11:21.073Z
Learning: OpenZeppelin's SafeERC20 library provides safePermit functionality for IERC20Permit tokens. The pattern "using SafeERC20 for IERC20Permit;" enables calling safePermit on IERC20Permit tokens safely. This functionality is available in recent versions of OpenZeppelin contracts.

Applied to files:

  • src/Periphery/Permit2Proxy.sol
  • test/solidity/Periphery/Permit2Proxy.t.sol
📚 Learning: 2025-08-26T12:11:21.073Z
Learnt from: mirooon
PR: lifinance/contracts#1328
File: src/Periphery/Lda/Facets/CoreRouteFacet.sol:4-6
Timestamp: 2025-08-26T12:11:21.073Z
Learning: OpenZeppelin's SafeERC20 library provides safePermit functionality for IERC20Permit tokens. The using SafeERC20 for IERC20Permit; directive enables calling safePermit on IERC20Permit tokens safely.

Applied to files:

  • src/Periphery/Permit2Proxy.sol
  • test/solidity/Periphery/Permit2Proxy.t.sol
📚 Learning: 2024-11-25T09:05:43.045Z
Learnt from: 0xDEnYO
PR: lifinance/contracts#782
File: deployments/base.diamond.json:148-148
Timestamp: 2024-11-25T09:05:43.045Z
Learning: In deployment configuration files (e.g., `deployments/base.diamond.json`), empty addresses for contracts like `Permit2Proxy` may be placeholders and will be updated after approvals are released from the multisig safe.

Applied to files:

  • src/Periphery/Permit2Proxy.sol
📚 Learning: 2025-08-27T23:36:40.773Z
Learnt from: mirooon
PR: lifinance/contracts#1328
File: test/solidity/Periphery/GasZipPeriphery.t.sol:223-233
Timestamp: 2025-08-27T23:36:40.773Z
Learning: In bridge facet swap flows with requiresDeposit=false, tokens are already held by the diamond contract from previous swaps. When subsequent contracts like GasZipPeriphery call LibAsset.depositAsset, they pull tokens from msg.sender (the diamond) to themselves via transferFrom, requiring no additional approvals since the diamond already owns the tokens.

Applied to files:

  • src/Periphery/Permit2Proxy.sol
📚 Learning: 2024-10-10T03:18:20.721Z
Learnt from: 0xDEnYO
PR: lifinance/contracts#782
File: test/solidity/Helpers/WithdrawablePeriphery.t.sol:22-35
Timestamp: 2024-10-10T03:18:20.721Z
Learning: In Solidity tests for withdrawal functions in `test/solidity/Helpers/WithdrawablePeriphery.t.sol`, do not suggest adding tests where the withdrawal amount exceeds the contract's balance, as such tests are unnecessary because any contract will fail in that case.

Applied to files:

  • test/solidity/Periphery/Permit2Proxy.t.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
PR: lifinance/contracts#0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : Facet contracts must import/use: ILiFi, LibAsset, LibSwap, LibAllowList; and use security mixins ReentrancyGuard, SwapperV2, Validatable; ECDSA optional

Applied to files:

  • test/solidity/Periphery/Permit2Proxy.t.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
PR: lifinance/contracts#0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : Facet contracts must implement: internal _startBridge, swapAndStartBridgeTokensVia{FacetName}, and startBridgeTokensVia{FacetName}

Applied to files:

  • test/solidity/Periphery/Permit2Proxy.t.sol
🪛 GitHub Check: Olympix Integrated Security
src/Periphery/Permit2Proxy.sol

[notice] 297-297:
Performing a narrowing downcast may result in silent overflow due to bit truncation. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/unsafe-downcast


[notice] 298-298:
Performing a narrowing downcast may result in silent overflow due to bit truncation. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/unsafe-downcast


[notice] 300-300:
Performing a narrowing downcast may result in silent overflow due to bit truncation. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/unsafe-downcast

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: enforce-min-test-coverage
  • GitHub Check: run-unit-tests
🔇 Additional comments (7)
test/solidity/Periphery/Permit2Proxy.t.sol (7)

83-101: Mocks: EIP-1271 coverage looks solid

The mock/wallet contracts accurately model happy/wrong-magic paths and keep state minimal. No issues.


1047-1089: This test will fail until signature parsing for EOAs is fixed

The new EOA bytes path depends on correct (r,s,v) extraction in the proxy. Current implementation in _handleEOAPermit is broken; fix there and this should pass.


1090-1116: Good negative: invalid EOA signature length

Asserts the custom InvalidSignature error; aligns with proxy checks.


1179-1208: Negative path: unauthorized EIP‑1271 signature

Covers invalid authorization correctly; assert on InvalidSignature matches proxy behavior.


1210-1237: Negative path: wrong magic value

Good coverage of wallets that return non‑standard magic; matches spec expectations.


1239-1285: Approval missing path is meaningful

Asserts TransferFromFailed, proving token approval is still required for 1271 flows.


1286-1315: Expired deadline check

Covers expiry in the new bytes‑based entry point; matches proxy’s InvalidSignature revert.

Comment on lines 45 to 48
/// Constants ///

bytes4 internal constant ERC1271_MAGIC_VALUE = 0x1626ba7e;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Define and reuse a PERMIT_PROXY_CALL_TYPEHASH constant

Reduce risk of typos and enable reuse in tests by exposing a constant for the 1271 struct type hash.

Apply this diff:

 /// Constants ///
 
-bytes4 internal constant ERC1271_MAGIC_VALUE = 0x1626ba7e;
+bytes4 internal constant ERC1271_MAGIC_VALUE = 0x1626ba7e;
+bytes32 public constant PERMIT_PROXY_CALL_TYPEHASH = keccak256(
+    "PermitProxyCall(address token,uint256 amount,uint256 deadline,bytes32 calldataHash,uint256 nonce,uint256 chainId,address contractAddress)"
+);

And update usage:

-        bytes32 messageHash = keccak256(
-            abi.encode(
-                keccak256(
-                    "PermitProxyCall(address token,uint256 amount,uint256 deadline,bytes32 calldataHash,uint256 nonce,uint256 chainId,address contractAddress)"
-                ),
+        bytes32 messageHash = keccak256(
+            abi.encode(
+                PERMIT_PROXY_CALL_TYPEHASH,
                 tokenAddress,
                 amount,
                 deadline,
                 keccak256(diamondCalldata),
                 currentNonce,
                 block.chainid,
                 address(this)
             )
         );

Also applies to: 340-354

🤖 Prompt for AI Agents
In src/Periphery/Permit2Proxy.sol around lines 45-48, add a new bytes32 constant
PERMIT_PROXY_CALL_TYPEHASH (computed as keccak256 of the 1271 struct definition)
alongside ERC1271_MAGIC_VALUE so tests and code can reuse it; then replace any
hard-coded/inline typehash computations or duplicated keccak256 calls with this
constant at the other occurrences (notably around lines 340-354) to avoid typos
and ensure a single source of truth for the 1271 permit proxy struct type hash.

Comment on lines 281 to 328
function _handleEOAPermit(
address tokenAddress,
uint256 amount,
uint256 deadline,
bytes memory signature
) internal {
// Validate signature length
if (signature.length != 65) revert InvalidSignature();

// Extract r, s, v from packed signature
bytes32 r;
bytes32 s;
uint8 v;

// Copy bytes manually without assembly
for (uint256 i = 0; i < 32; i++) {
r |= bytes32(signature[i]) >> (i * 8);
s |= bytes32(signature[i + 32]) >> (i * 8);
}
v = uint8(signature[64]);

// Call permit
try
ERC20Permit(tokenAddress).permit(
msg.sender,
address(this),
amount,
deadline,
v,
r,
s
)
{} catch Error(string memory reason) {
if (
IERC20(tokenAddress).allowance(msg.sender, address(this)) <
amount
) {
revert(reason);
}
} catch (bytes memory reason) {
if (
IERC20(tokenAddress).allowance(msg.sender, address(this)) <
amount
) {
LibUtil.revertWith(reason);
}
}
}
Copy link
Contributor

@coderabbitai coderabbitai bot Sep 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Blocker: EOA signature parsing is incorrect (r/s assembly needed)

The loop that builds r and s zeroes out most bytes; permits will fail for valid signatures. Replace with standard 65‑byte unpacking and normalize v.

Apply this diff:

 function _handleEOAPermit(
     address tokenAddress,
     uint256 amount,
     uint256 deadline,
     bytes memory signature
 ) internal {
-    // Validate signature length
-    if (signature.length != 65) revert InvalidSignature();
-
-    // Extract r, s, v from packed signature
-    bytes32 r;
-    bytes32 s;
-    uint8 v;
-
-    // Copy bytes manually without assembly
-    for (uint256 i = 0; i < 32; i++) {
-        r |= bytes32(signature[i]) >> (i * 8);
-        s |= bytes32(signature[i + 32]) >> (i * 8);
-    }
-    v = uint8(signature[64]);
+    if (signature.length != 65) revert InvalidSignature();
+    bytes32 r;
+    bytes32 s;
+    uint8 v;
+    // Unpack (r,s,v) from bytes[65]
+    assembly {
+        r := mload(add(signature, 0x20))
+        s := mload(add(signature, 0x40))
+        v := byte(0, mload(add(signature, 0x60)))
+    }
+    // Normalize v to {27,28}
+    if (v < 27) v += 27;
+    if (v != 27 && v != 28) revert InvalidSignature();
 
     // Call permit
     try
         ERC20Permit(tokenAddress).permit(
             msg.sender,
             address(this),
             amount,
             deadline,
             v,
             r,
             s
         )
     {} catch Error(string memory reason) {
         if (
             IERC20(tokenAddress).allowance(msg.sender, address(this)) <
             amount
         ) {
             revert(reason);
         }
     } catch (bytes memory reason) {
         if (
             IERC20(tokenAddress).allowance(msg.sender, address(this)) <
             amount
         ) {
             LibUtil.revertWith(reason);
         }
     }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function _handleEOAPermit(
address tokenAddress,
uint256 amount,
uint256 deadline,
bytes memory signature
) internal {
// Validate signature length
if (signature.length != 65) revert InvalidSignature();
// Extract r, s, v from packed signature
bytes32 r;
bytes32 s;
uint8 v;
// Copy bytes manually without assembly
for (uint256 i = 0; i < 32; i++) {
r |= bytes32(signature[i]) >> (i * 8);
s |= bytes32(signature[i + 32]) >> (i * 8);
}
v = uint8(signature[64]);
// Call permit
try
ERC20Permit(tokenAddress).permit(
msg.sender,
address(this),
amount,
deadline,
v,
r,
s
)
{} catch Error(string memory reason) {
if (
IERC20(tokenAddress).allowance(msg.sender, address(this)) <
amount
) {
revert(reason);
}
} catch (bytes memory reason) {
if (
IERC20(tokenAddress).allowance(msg.sender, address(this)) <
amount
) {
LibUtil.revertWith(reason);
}
}
}
function _handleEOAPermit(
address tokenAddress,
uint256 amount,
uint256 deadline,
bytes memory signature
) internal {
if (signature.length != 65) revert InvalidSignature();
bytes32 r;
bytes32 s;
uint8 v;
// Unpack (r,s,v) from bytes[65]
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
// Normalize v to {27,28}
if (v < 27) v += 27;
if (v != 27 && v != 28) revert InvalidSignature();
// Call permit
try
ERC20Permit(tokenAddress).permit(
msg.sender,
address(this),
amount,
deadline,
v,
r,
s
)
{} catch Error(string memory reason) {
if (
IERC20(tokenAddress).allowance(msg.sender, address(this)) <
amount
) {
revert(reason);
}
} catch (bytes memory reason) {
if (
IERC20(tokenAddress).allowance(msg.sender, address(this)) <
amount
) {
LibUtil.revertWith(reason);
}
}
}
🧰 Tools
🪛 GitHub Check: Olympix Integrated Security

[notice] 297-297:
Performing a narrowing downcast may result in silent overflow due to bit truncation. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/unsafe-downcast


[notice] 298-298:
Performing a narrowing downcast may result in silent overflow due to bit truncation. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/unsafe-downcast


[notice] 300-300:
Performing a narrowing downcast may result in silent overflow due to bit truncation. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/unsafe-downcast

🤖 Prompt for AI Agents
In src/Periphery/Permit2Proxy.sol around lines 281 to 328, the manual loop that
tries to build r and s from the signature is incorrect and zeroes most bytes;
replace it with standard 65-byte unpacking using assembly (e.g., mload at
signature+32 for r and signature+64 for s) and set v = uint8(signature[64]) then
normalize v to 27 or 28 if it is 0 or 1 before calling permit; keep the existing
signature.length == 65 check and the existing try/catch behavior but use the
correctly-extracted r, s, v values.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you certain it's not possible to split a signature without solidity using a modern solidity version?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧩 Analysis chain

@ezynda3 Let me check the current best practices by examining how signature splitting is handled in your codebase and checking for any existing patterns or OpenZeppelin usage.


🏁 Script executed:

#!/bin/bash
# Check if OpenZeppelin's ECDSA library is used in the codebase
rg -n "ECDSA" --type=sol -C3

# Look for signature splitting patterns in the codebase
rg -n "assembly.*signature" --type=sol -C5

# Check for any existing r, s, v extraction patterns
rg -nP "\b(bytes32\s+r|bytes32\s+s|uint8\s+v)" --type=sol -C3

Length of output: 83


🏁 Script executed:

#!/bin/bash
# Check if OpenZeppelin's ECDSA library is used in the codebase
rg -n "ECDSA" -g "*.sol" -C3

# Look for signature splitting patterns in the codebase
rg -n "assembly.*signature" -g "*.sol" -C5

# Check for any existing r, s, v extraction patterns
rg -nP "\b(bytes32\s+r|bytes32\s+s|uint8\s+v)" -g "*.sol" -C3

# Look for OpenZeppelin imports
rg -n "@openzeppelin" -g "*.sol"

# Check for existing signature handling methods
rg -n "signature.*split|split.*signature" -g "*.sol" -C3

Length of output: 42485


@ezynda3 You're absolutely right to question this! After examining your codebase, I can confirm that assembly is still the standard approach for splitting ECDSA signatures in modern Solidity, but you have better alternatives than writing custom assembly.

Key findings from your codebase:

  1. Solady's ECDSA library is already used in RelayFacet.sol:

    import { ECDSA } from "solady/utils/ECDSA.sol";
    // Later used as: ECDSA.recover(message, signature)
  2. Standard pattern everywhere else: Your test files consistently use:

    (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, msgHash);
    bytes memory signature = abi.encodePacked(r, s, v);
  3. Even Safe contracts use assembly: The Safe contract has a signatureSplit function that uses assembly for the same operation.

Why the manual loop fails:
Your current approach has this fundamental issue:

r |= bytes32(signature[i]) >> (i * 8);  // This zeros most bytes instead of copying them

Recommended solutions (in order of preference):

  1. Use Solady's ECDSA library (already in your dependencies):

    import { ECDSA } from "solady/utils/ECDSA.sol";
    // Then just: ECDSA.recover(messageHash, signature)
  2. Use the proven assembly pattern (like Safe contracts):

    assembly {
        r := mload(add(signature, 0x20))
        s := mload(add(signature, 0x40))
        v := byte(0, mload(add(signature, 0x60)))
    }
  3. Use OpenZeppelin's ECDSA library (also handles the assembly internally)

Since you're already using Solady in RelayFacet, would you prefer to refactor to use Solady's ECDSA.recover() instead of manually splitting signatures? This would eliminate the need for custom assembly while following patterns already established in your codebase.


🧠 Learnings used
Learnt from: 0xDEnYO
PR: lifinance/contracts#782
File: src/Periphery/Permit2Proxy.sol:75-108
Timestamp: 2024-10-09T03:47:21.269Z
Learning: In the `Permit2Proxy` contract (`src/Periphery/Permit2Proxy.sol`), reentrancy protection is not necessary for functions like `callDiamondWithEIP2612Signature` when calling our own trusted diamond contract (`LIFI_DIAMOND`).

Learnt from: mirooon
PR: lifinance/contracts#1328
File: src/Periphery/Lda/Facets/CoreRouteFacet.sol:4-6
Timestamp: 2025-08-26T12:11:21.073Z
Learning: In the lifinance/contracts repository, OpenZeppelin's SafeERC20 library provides safePermit functionality for IERC20Permit tokens. This is confirmed by working implementations in both CoreRouteFacet.sol and LiFiDEXAggregator.sol that use the pattern "using SafeERC20 for IERC20Permit;" and successfully call safePermit with permit parameters.

Learnt from: mirooon
PR: lifinance/contracts#1328
File: src/Periphery/Lda/Facets/CoreRouteFacet.sol:4-6
Timestamp: 2025-08-26T12:11:21.073Z
Learning: OpenZeppelin's SafeERC20 library provides safePermit functionality for IERC20Permit tokens in modern versions. The usage pattern "using SafeERC20 for IERC20Permit;" is valid and enables safe permit operations. This is confirmed to be working in the lifinance/contracts codebase.

@ezynda3 ezynda3 force-pushed the LF-15521-enhance-permit2-proxy branch from bd1fde6 to 0b118f7 Compare October 3, 2025 09:13
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/Periphery/Permit2Proxy.sol (1)

15-15: Update contract version to reflect new functionality.

The contract has significant changes (new EIP-2612 overload with bytes signature and EIP-3009 support). The version should be incremented from 1.0.4 to at least 1.1.0.

Apply this diff:

-/// @custom:version 1.0.4
+/// @custom:version 1.1.0

Based on pipeline failures.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between bd1fde6 and 0b118f7.

📒 Files selected for processing (3)
  • src/Interfaces/IERC20PermitExtended.sol (1 hunks)
  • src/Periphery/Permit2Proxy.sol (4 hunks)
  • test/solidity/Periphery/Permit2Proxy.t.sol (21 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
{src,test/solidity,script}/**/*.sol

📄 CodeRabbit inference engine (conventions.md)

{src,test/solidity,script}/**/*.sol: All Solidity files must start with a license identifier, followed by a blank line, then the pragma statement
Use SPDX license identifier // SPDX-License-Identifier: LGPL-3.0-only for all first-party Solidity files
Error names must be descriptive PascalCase, use custom errors instead of revert() strings, and omit error messages for gas optimization
Solidity variable/function naming: state variables camelCase; function params camelCase with leading underscore; constants and immutables in CONSTANT_CASE; private/internal variables camelCase; function names camelCase

Files:

  • src/Interfaces/IERC20PermitExtended.sol
  • src/Periphery/Permit2Proxy.sol
  • test/solidity/Periphery/Permit2Proxy.t.sol
src/Interfaces/**/*.sol

📄 CodeRabbit inference engine (conventions.md)

src/Interfaces/**/*.sol: All interfaces must reside under src/Interfaces, be in separate files from implementations, and use an I prefix (e.g., ILiFi)
Every interface must include NatSpec in this order: @title, @notice, @author LI.FI (https://li.fi), @Custom:version X.Y.Z

Files:

  • src/Interfaces/IERC20PermitExtended.sol
src/**/*.sol

📄 CodeRabbit inference engine (conventions.md)

src/**/*.sol: Every contract must include NatSpec header in this order: @title, @author LI.FI (https://li.fi), @notice, @Custom:version X.Y.Z
All public/external functions must have NatSpec comments documenting purpose, params, and returns
Add inline comments for complex logic, optimizations, gas-saving techniques, and math steps
Emit GenericSwapCompleted after successful same-chain swap completion

Files:

  • src/Interfaces/IERC20PermitExtended.sol
  • src/Periphery/Permit2Proxy.sol
**/*.sol

📄 CodeRabbit inference engine (conventions.md)

Follow solhint rules as configured in .solhint.json

Files:

  • src/Interfaces/IERC20PermitExtended.sol
  • src/Periphery/Permit2Proxy.sol
  • test/solidity/Periphery/Permit2Proxy.t.sol
test/solidity/**/*.t.sol

📄 CodeRabbit inference engine (conventions.md)

test/solidity/**/*.t.sol: Solidity test files must use .t.sol extension; organize imports with system libraries first, then project files
Test naming: successful tests prefixed test_, failure tests prefixed testRevert_, base/inherited tests prefixed testBase_; negative tests must check specific revert reasons
Each test contract must implement setUp() that configures block numbers, initializes base contracts, sets up facets, and uses vm.label; contracts inheriting TestBase.sol must call initTestBase() in setUp()
Use vm.startPrank()/vm.stopPrank() for user simulation; maintain blank-line formatting rules for tests (expectRevert separation, assertion grouping, spacing)
Use assertEq for equality, custom assertion modifiers for balance changes, vm.expectRevert with precise reasons, and vm.expectEmit(true,true,true,true,) to verify events and parameters

Files:

  • test/solidity/Periphery/Permit2Proxy.t.sol
🧠 Learnings (12)
📚 Learning: 2025-08-26T12:11:21.073Z
Learnt from: mirooon
PR: lifinance/contracts#1328
File: src/Periphery/Lda/Facets/CoreRouteFacet.sol:4-6
Timestamp: 2025-08-26T12:11:21.073Z
Learning: OpenZeppelin's SafeERC20 library provides safePermit functionality for IERC20Permit tokens. The pattern "using SafeERC20 for IERC20Permit;" enables calling safePermit on IERC20Permit tokens safely. This functionality is available in recent versions of OpenZeppelin contracts.

Applied to files:

  • src/Interfaces/IERC20PermitExtended.sol
  • src/Periphery/Permit2Proxy.sol
📚 Learning: 2025-08-26T12:11:21.073Z
Learnt from: mirooon
PR: lifinance/contracts#1328
File: src/Periphery/Lda/Facets/CoreRouteFacet.sol:4-6
Timestamp: 2025-08-26T12:11:21.073Z
Learning: OpenZeppelin's SafeERC20 library provides safePermit functionality for IERC20Permit tokens. The using SafeERC20 for IERC20Permit; directive enables calling safePermit on IERC20Permit tokens safely.

Applied to files:

  • src/Interfaces/IERC20PermitExtended.sol
  • src/Periphery/Permit2Proxy.sol
📚 Learning: 2025-08-26T12:11:21.073Z
Learnt from: mirooon
PR: lifinance/contracts#1328
File: src/Periphery/Lda/Facets/CoreRouteFacet.sol:4-6
Timestamp: 2025-08-26T12:11:21.073Z
Learning: OpenZeppelin's SafeERC20 library provides safePermit functionality for IERC20Permit tokens in modern versions. The usage pattern "using SafeERC20 for IERC20Permit;" is valid and enables safe permit operations. This is confirmed to be working in the lifinance/contracts codebase.

Applied to files:

  • src/Interfaces/IERC20PermitExtended.sol
  • src/Periphery/Permit2Proxy.sol
📚 Learning: 2025-08-26T12:11:21.073Z
Learnt from: mirooon
PR: lifinance/contracts#1328
File: src/Periphery/Lda/Facets/CoreRouteFacet.sol:4-6
Timestamp: 2025-08-26T12:11:21.073Z
Learning: In the lifinance/contracts repository, OpenZeppelin's SafeERC20 library provides safePermit functionality for IERC20Permit tokens. This is confirmed by working implementations in both CoreRouteFacet.sol and LiFiDEXAggregator.sol that use the pattern "using SafeERC20 for IERC20Permit;" and successfully call safePermit with permit parameters.

Applied to files:

  • src/Interfaces/IERC20PermitExtended.sol
  • src/Periphery/Permit2Proxy.sol
📚 Learning: 2025-08-26T12:11:21.073Z
Learnt from: mirooon
PR: lifinance/contracts#1328
File: src/Periphery/Lda/Facets/CoreRouteFacet.sol:4-6
Timestamp: 2025-08-26T12:11:21.073Z
Learning: OpenZeppelin's SafeERC20 library provides safePermit functionality for IERC20Permit tokens. This is confirmed by actual usage in the lifinance/contracts codebase where safePermit is used in multiple files including CoreRouteFacet.sol and LiFiDEXAggregator.sol. The pattern "using SafeERC20 for IERC20Permit;" is valid and working.

Applied to files:

  • src/Interfaces/IERC20PermitExtended.sol
  • src/Periphery/Permit2Proxy.sol
📚 Learning: 2024-10-09T03:47:21.269Z
Learnt from: 0xDEnYO
PR: lifinance/contracts#782
File: src/Periphery/Permit2Proxy.sol:75-108
Timestamp: 2024-10-09T03:47:21.269Z
Learning: In the `Permit2Proxy` contract (`src/Periphery/Permit2Proxy.sol`), reentrancy protection is not necessary for functions like `callDiamondWithEIP2612Signature` when calling our own trusted diamond contract (`LIFI_DIAMOND`).

Applied to files:

  • src/Periphery/Permit2Proxy.sol
  • test/solidity/Periphery/Permit2Proxy.t.sol
📚 Learning: 2025-07-16T01:03:08.106Z
Learnt from: 0xDEnYO
PR: lifinance/contracts#1275
File: src/Facets/AllBridgeFacet.sol:164-164
Timestamp: 2025-07-16T01:03:08.106Z
Learning: In src/Facets/AllBridgeFacet.sol, the team has decided that explicit validation for address downcasting from `_bridgeData.sendingAssetId` to `bytes32(uint256(uint160(_bridgeData.sendingAssetId)))` is not required, accepting the potential risk of silent overflow from unsafe downcasting.

Applied to files:

  • src/Periphery/Permit2Proxy.sol
📚 Learning: 2025-07-15T05:05:28.134Z
Learnt from: 0xDEnYO
PR: lifinance/contracts#1140
File: config/permit2Proxy.json:3-3
Timestamp: 2025-07-15T05:05:28.134Z
Learning: In config/permit2Proxy.json, the addresses represent the Permit2 contract addresses on each chain, NOT the Permit2Proxy contract addresses. These Permit2 addresses are used as constructor arguments when deploying the Permit2Proxy contract. The Permit2Proxy acts as a wrapper/proxy that interacts with the underlying Permit2 contract on each network.

Applied to files:

  • src/Periphery/Permit2Proxy.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
PR: lifinance/contracts#0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to test/solidity/**/*.t.sol : Use vm.startPrank()/vm.stopPrank() for user simulation; maintain blank-line formatting rules for tests (expectRevert separation, assertion grouping, spacing)

Applied to files:

  • test/solidity/Periphery/Permit2Proxy.t.sol
📚 Learning: 2024-10-10T03:26:23.793Z
Learnt from: 0xDEnYO
PR: lifinance/contracts#782
File: test/solidity/Helpers/WithdrawablePeriphery.t.sol:79-80
Timestamp: 2024-10-10T03:26:23.793Z
Learning: In Solidity tests, it's acceptable to use `UnAuthorized.selector` in `vm.expectRevert()`; avoid suggesting changing it to string messages.

Applied to files:

  • test/solidity/Periphery/Permit2Proxy.t.sol
📚 Learning: 2025-08-29T11:53:38.549Z
Learnt from: mirooon
PR: lifinance/contracts#1328
File: test/solidity/Periphery/LDA/BaseCoreRouteTest.t.sol:379-388
Timestamp: 2025-08-29T11:53:38.549Z
Learning: In test/solidity/Periphery/LDA/BaseCoreRouteTest.t.sol, for the revert-testing helper function _executeAndVerifySwap, only the aggregator branch (CommandType.DistributeSelfERC20) should use amountIn-1 to underfund and trigger insufficient balance errors, while user-funded branches should use the full amountIn to test other error conditions.

Applied to files:

  • test/solidity/Periphery/Permit2Proxy.t.sol
📚 Learning: 2024-11-05T17:16:19.946Z
Learnt from: maxklenk
PR: lifinance/contracts#782
File: script/demoScripts/demoPermit2.ts:0-0
Timestamp: 2024-11-05T17:16:19.946Z
Learning: In `script/demoScripts/demoPermit2.ts`, the `nextNonce` function should be called on the `PERMIT2_PROXY_ADDRESS` because the nonce is stored in the proxy contract, not in the `Permit2` contract (`PERMIT2_ADDRESS`).

Applied to files:

  • test/solidity/Periphery/Permit2Proxy.t.sol
🪛 GitHub Actions: VersionControlAndAuditVerification
src/Interfaces/IERC20PermitExtended.sol

[error] 1-1: The following files are missing a custom:version tag in their code: src/Interfaces/IERC20PermitExtended.sol

src/Periphery/Permit2Proxy.sol

[error] 1-1: The file has relevant changes but the version (v1.0.4) was not updated. This Github action cannot complete until these issues are solved.

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: run-unit-tests
  • GitHub Check: enforce-min-test-coverage
🔇 Additional comments (15)
src/Interfaces/IERC20PermitExtended.sol (2)

7-13: LGTM: Packed-signature permit overload.

The permit function accepting bytes calldata signature provides a convenient alternative to the standard (v, r, s) overload, allowing callers to pass packed signatures directly.


15-35: LGTM: EIP-3009 transferWithAuthorization overloads.

Both overloads correctly implement the EIP-3009 transferWithAuthorization function signature, providing both (v, r, s) and packed-signature variants for flexibility.

src/Periphery/Permit2Proxy.sol (4)

8-8: LGTM: Updated import for extended interface.

The import of IERC20PermitExtended correctly replaces the previous ERC20Permit import, enabling support for packed signatures and EIP-3009 flows.


64-116: LGTM: Bytes-signature EIP-2612 overload.

The new overload correctly implements the same logic as the existing (v, r, s) variant, calling IERC20PermitExtended.permit with a packed signature. Error handling, token transfer, approval, and calldata execution are consistent with the existing implementation.


131-176: LGTM: Updated to use IERC20PermitExtended interface.

The cast to IERC20PermitExtended correctly aligns with the new interface, maintaining the same permit logic with (v, r, s) parameters.


178-250: LGTM: EIP-3009 transferWithAuthorization flows.

Both overloads correctly implement EIP-3009 transferWithAuthorization, which atomically transfers tokens from the user to the proxy (unlike permit, which only grants approval). The absence of try-catch error handling is appropriate here, as there's no allowance fallback scenario. The subsequent maxApprove to the diamond and calldata execution are consistent with the EIP-2612 flows.

test/solidity/Periphery/Permit2Proxy.t.sol (9)

113-123: LGTM: TestDataEIP3009 struct.

The struct correctly captures EIP-3009 parameters, including bytes32 nonce (as required by EIP-3009, unlike EIP-2612's uint256 nonce).


133-133: LGTM: Fork block updated.

The fork block number update ensures tests run against the latest on-chain state, which is necessary for testing EIP-2612 and EIP-3009 flows with real contracts like USDC.


162-200: LGTM: Test renamed to CamelCase convention.

The test name follows the test_ prefix convention and uses CamelCase for readability, consistent with modern Solidity test naming practices.


202-238: LGTM: Bytes-signature EIP-2612 test.

The new test correctly verifies the bytes-signature overload by packing (r, s, v) into bytes memory signature and calling callDiamondWithEIP2612Signature with the packed signature.


240-383: LGTM: Test renames to CamelCase convention.

All test renames follow the test_ or testRevert_ prefix convention with CamelCase, improving readability and consistency.


387-424: LGTM: EIP-3009 signature test with (v, r, s).

The test correctly verifies the EIP-3009 flow using transferWithAuthorization with separated signature components (v, r, s), ensuring tokens are transferred and the diamond call succeeds.


426-465: LGTM: EIP-3009 signature test with bytes.

The test correctly verifies the EIP-3009 flow using a packed signature, covering both overloads of callDiamondWithEIP3009Signature.


467-907: LGTM: Test renames and existing test coverage.

All test renames follow the established convention. The existing test coverage for Permit2 flows, error handling, and edge cases remains comprehensive.


1142-1199: LGTM: EIP-3009 test helper functions.

Both helper functions correctly implement EIP-3009 signature generation:

  • _getTestDataEIP3009SignedBypermit2User generates test data with a unique nonce (hashed from user and timestamp) and signs the EIP-3009 digest.
  • _generateEIP3009MsgHash constructs the EIP-712 digest matching the EIP-3009 TransferWithAuthorization type hash.

@@ -0,0 +1,36 @@
// SPDX-License-Identifier: MIT
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix the license identifier to LGPL-3.0-only.

The coding guidelines require all first-party Solidity files to use LGPL-3.0-only, not MIT.

Apply this diff:

-// SPDX-License-Identifier: MIT
+// SPDX-License-Identifier: LGPL-3.0-only

Based on coding guidelines.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// SPDX-License-Identifier: MIT
// SPDX-License-Identifier: LGPL-3.0-only
🧰 Tools
🪛 GitHub Actions: VersionControlAndAuditVerification

[error] 1-1: The following files are missing a custom:version tag in their code: src/Interfaces/IERC20PermitExtended.sol

🤖 Prompt for AI Agents
In src/Interfaces/IERC20PermitExtended.sol around line 1 the SPDX license
identifier is MIT but project guidelines require LGPL-3.0-only; update the
file's first line to use "SPDX-License-Identifier: LGPL-3.0-only" (replace the
existing identifier exactly) and save the file.


import { IERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";

interface IERC20PermitExtended is IERC20Permit {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Add required NatSpec documentation for the interface.

All interfaces must include NatSpec with @title, @notice, @author LI.FI (https://li.fi), and @custom:version X.Y.Z (in that order) before the interface declaration.

Apply this diff:

 import { IERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";
 
+/// @title IERC20 Permit Extended Interface
+/// @notice Extends IERC20Permit with packed signature support and EIP-3009 transferWithAuthorization
+/// @author LI.FI (https://li.fi)
+/// @custom:version 1.0.0
 interface IERC20PermitExtended is IERC20Permit {

Based on coding guidelines and pipeline failures.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
interface IERC20PermitExtended is IERC20Permit {
import { IERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";
/// @title IERC20 Permit Extended Interface
/// @notice Extends IERC20Permit with packed signature support and EIP-3009 transferWithAuthorization
/// @author LI.FI (https://li.fi)
/// @custom:version 1.0.0
interface IERC20PermitExtended is IERC20Permit {
🤖 Prompt for AI Agents
In src/Interfaces/IERC20PermitExtended.sol around line 6, the interface
declaration lacks the required NatSpec block; add a NatSpec comment immediately
above the `interface IERC20PermitExtended is IERC20Permit {` line containing
@title, @notice, @author LI.FI (https://li.fi), and @custom:version X.Y.Z in
that exact order, providing brief descriptive text for @title and @notice and
keeping the author and version tags as specified.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants