Skip to content

Conversation

@RnkSngh
Copy link

@RnkSngh RnkSngh commented Oct 17, 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 Oct 17, 2025

Walkthrough

Adds Polymer CCTP support: new PolymerCCTPFacet and ITokenMessenger interface, deployment and demo Forge scripts, Bash orchestration, TypeScript end‑to‑end demo, testnet addresses JSON, and comprehensive Solidity tests with mocks.

Changes

Cohort / File(s) Summary
Core Facet & Interface
src/Facets/PolymerCCTPFacet.sol, src/Interfaces/ITokenMessenger.sol
New PolymerCCTPFacet enabling USDC bridging via Polymer CCTP (fee handling, approvals, EVM vs non‑EVM receiver handling, validations, events/errors) and new ITokenMessenger interface with depositForBurn(...) and NatSpec.
Deployment Solidity Scripts
script/deploy/facets/DeployDiamondWithPolymerCCTPFacet.s.sol
New Forge deployment script to deploy DiamondCutFacet, LiFiDiamond, PolymerCCTPFacet and register the facet via diamondCut.
Demo Solidity Script
script/demoScripts/PolymerCCTP.s.sol
New Forge script contract CallPolymerCCTPFacet that reads env vars and calls startBridgeTokensViaPolymerCCTP.
Bash Orchestration & Deploy
script/deploy/DeployPolymerContracts.sh, script/demoScripts/CallPolymerCCTP.sh
New Bash deployer that validates inputs, reads chain ID and addresses JSON, runs Forge deployments, updates addresses file, and a caller script to export env and invoke demo Forge script.
TypeScript Demo & Utilities
script/demoScripts/demoPolymer.ts
New end‑to‑end demo: discovers Polymer routes, fetches calldata, handles Solana/EVM wallets, approves tokens, submits txs, polls transfer status, logs balances and receipts with retries.
Tests & Mocks
test/solidity/Facets/PolymerCCTPFacet.t.sol
New Forge tests and mocks: TestPolymerCCTPFacet, MockTokenMessenger, MockUSDC, MockToken, MockDEX, and PolymerCCTPFacetTest covering success, swap+bridge, and multiple revert scenarios.
Addresses
testnet-addresses.json
New testnet addresses JSON with entries for multiple chain IDs including 11155111, 11155420, 421614, 84532, 43113, 80002, 1301.
Config cleanup
foundry.toml
Removes zksync-specific profile and many etherscan network entries from Foundry configuration.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Areas requiring extra attention:

  • src/Facets/PolymerCCTPFacet.sol: fee arithmetic, approval and transfer flows, EVM vs non‑EVM branches, and emitted events/errors.
  • src/Interfaces/ITokenMessenger.sol: signature, parameter ordering and NatSpec alignment with external TokenMessenger expectations.
  • script/deploy/DeployPolymerContracts.sh: extraction/parsing of forge output and safe update of addresses JSON.
  • script/demoScripts/demoPolymer.ts: route/calldata validation, Solana vs EVM handling, approval/nonce logic, and polling/retry behavior.
  • test/solidity/Facets/PolymerCCTPFacet.t.sol: mock fidelity for depositForBurn and coverage of edge cases.

Possibly related PRs

Suggested labels

WIP

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Description check ⚠️ Warning The pull request description is essentially a blank template with all checklist items unchecked and Jira/implementation details missing. Complete the PR description by providing the Jira task reference, explaining the implementation rationale, and verifying all checklist items. Include audit details if applicable.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The pull request title clearly and concisely summarizes the main change: adding a Polymer CCTP facet implementation along with supporting scripts.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

@RnkSngh RnkSngh force-pushed the rs/cctp-facet branch 2 times, most recently from 64b3128 to cf7a083 Compare October 17, 2025 04: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: 11

🧹 Nitpick comments (6)
script/demoScripts/CallPolymerCCTP.sh (2)

3-11: Shell robustness and unused var

  • Consider set -euo pipefail for stricter error handling
  • YELLOW is defined but unused (ShellCheck SC2034)
-# Exit on error
-set -e
+# Exit on error; undefined vars; fail on pipe
+set -euo pipefail
@@
-YELLOW='\033[1;33m'
+# YELLOW='\033[1;33m' # unused

49-68: Addresses lookup defaults are fine; add explicit check for jq/cast presence

Fail early if dependencies are missing.

+command -v jq >/dev/null 2>&1 || { echo -e "${RED}jq is required${NC}"; exit 1; }
+command -v cast >/dev/null 2>&1 || { echo -e "${RED}foundry 'cast' is required${NC}"; exit 1; }
script/deploy/DeployPolymerContracts.sh (2)

3-11: Strengthen error handling and remove unused var

  • Use set -euo pipefail
  • YELLOW is unused (SC2034)
-# Exit on error
-set -e
+# Exit on error; undefined vars; fail on pipe
+set -euo pipefail
@@
-YELLOW='\033[1;33m'
+# YELLOW='\033[1;33m' # unused

47-53: Dependency checks and final instruction path

  • Add jq/cast presence checks
  • Final hint references wrong path; use script/demoScripts/CallPolymerCCTP.sh
+command -v jq >/dev/null 2>&1 || { echo -e "${RED}jq is required${NC}"; exit 1; }
+command -v cast >/dev/null 2>&1 || { echo -e "${RED}foundry 'cast' is required${NC}"; exit 1; }
@@
-echo -e "${BLUE}To call the facet, run: ./script/deploy/CallPolymerCCTP.sh <RPC_URL> <PRIVATE_KEY> <DESTINATION_DOMAIN> $ADDRESSES_FILE${NC}"
+echo -e "${BLUE}To call the facet, run: ./script/demoScripts/CallPolymerCCTP.sh <RPC_URL> <PRIVATE_KEY> <DESTINATION_DOMAIN> $ADDRESSES_FILE${NC}"

Also applies to: 131-133

src/Facets/PolymerCCTPFacet.sol (2)

20-35: Constructor arg validation OK; add NatSpec for params and ensure fee collector rationale is resolved

Add NatSpec @param for constructor args and remove TODO before release.

Add:

/// @param _tokenMessenger Polymer/CCTP token messenger address
/// @param _usdc USDC token address
/// @param _polymerFeeReceiver Address that receives polymerTokenFee in USDC

68-70: Unsafe approve; use LibAsset approval helper

Prefer LibAsset.approveERC20 or a safe pattern to set allowance if insufficient.

-        IERC20(usdc).approve(address(tokenMessenger), _bridgeData.minAmount);
+        LibAsset.approveERC20(IERC20(usdc), address(tokenMessenger), _bridgeData.minAmount);
📜 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 98bde03 and cf7a083.

📒 Files selected for processing (8)
  • script/demoScripts/CallPolymerCCTP.sh (1 hunks)
  • script/demoScripts/PolymerCCTP.s.sol (1 hunks)
  • script/deploy/DeployPolymerContracts.sh (1 hunks)
  • script/deploy/facets/DeployDiamondWithPolymerCCTPFacet.s.sol (1 hunks)
  • src/Facets/PolymerCCTPFacet.sol (1 hunks)
  • src/Interfaces/IMessageTransmitter.sol (1 hunks)
  • src/Interfaces/IPolymerCCTP.sol (1 hunks)
  • src/Interfaces/ITokenMessenger.sol (1 hunks)
🧰 Additional context used
📓 Path-based instructions (8)
{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/IMessageTransmitter.sol
  • src/Interfaces/IPolymerCCTP.sol
  • src/Interfaces/ITokenMessenger.sol
  • script/deploy/facets/DeployDiamondWithPolymerCCTPFacet.s.sol
  • src/Facets/PolymerCCTPFacet.sol
  • script/demoScripts/PolymerCCTP.s.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/IMessageTransmitter.sol
  • src/Interfaces/IPolymerCCTP.sol
  • src/Interfaces/ITokenMessenger.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/IMessageTransmitter.sol
  • src/Interfaces/IPolymerCCTP.sol
  • src/Interfaces/ITokenMessenger.sol
  • src/Facets/PolymerCCTPFacet.sol
**/*.sol

📄 CodeRabbit inference engine (conventions.md)

Follow solhint rules as configured in .solhint.json

Files:

  • src/Interfaces/IMessageTransmitter.sol
  • src/Interfaces/IPolymerCCTP.sol
  • src/Interfaces/ITokenMessenger.sol
  • script/deploy/facets/DeployDiamondWithPolymerCCTPFacet.s.sol
  • src/Facets/PolymerCCTPFacet.sol
  • script/demoScripts/PolymerCCTP.s.sol
script/**/*.sh

📄 CodeRabbit inference engine (conventions.md)

script/**/*.sh: Bash scripts must start with #!/bin/bash, be modular with sections (Logging, Error handling and logging, Deployment functions), follow DRY via helpers, and organize core operations into functions
Bash style: consistent indentation/naming, comments, usage instructions, and TODO/limitations documented
Bash error handling and logging: use helper logging functions, validate inputs early, check exit status with checkFailure, and use set -e where appropriate

Files:

  • script/deploy/DeployPolymerContracts.sh
  • script/demoScripts/CallPolymerCCTP.sh
{script/**/*.sh,preinstall.sh}

📄 CodeRabbit inference engine (conventions.md)

Environment: load from .env or config.sh, declare globals in config, update .env.example, validate env vars early; add system packages to preinstall.sh

Files:

  • script/deploy/DeployPolymerContracts.sh
  • script/demoScripts/CallPolymerCCTP.sh
script/deploy/facets/Deploy*.s.sol

📄 CodeRabbit inference engine (conventions.md)

script/deploy/facets/Deploy*.s.sol: Deployment scripts for facets must be named Deploy{ContractName}.s.sol and inherit DeployScriptBase; deploy using deploy(type(ContractName).creationCode); omit getConstructorArgs() when not needed
When constructor args are required, implement getConstructorArgs() using stdJson, then call deploy(type(FacetName).creationCode) and return (deployed, constructorArgs)

Files:

  • script/deploy/facets/DeployDiamondWithPolymerCCTPFacet.s.sol
src/Facets/**/*Facet.sol

📄 CodeRabbit inference engine (conventions.md)

src/Facets/**/*Facet.sol: Facet contracts must be located in src/Facets and include "Facet" in the contract name
Facet contracts must implement: internal _startBridge, swapAndStartBridgeTokensVia{FacetName}, and startBridgeTokensVia{FacetName}
Facet contracts must import/use: ILiFi, LibAsset, LibSwap, LibAllowList; and use security mixins ReentrancyGuard, SwapperV2, Validatable; ECDSA optional
Do not use msg.sender as refund address; require an explicit parameter for refund address
In {facetName}Data, receiverAddress must be the first field and be validated against bridgeData.receiver; verify targetChainId equals bridgeData.destinationChain for EVM-to-EVM
Apply required modifiers where appropriate: nonReentrant, refundExcessNative, validateBridgeData, doesNotContainSourceSwaps/doesContainSourceSwaps, doesNotContainDestinationCalls/doesContainDestinationCalls
Emit LiFiTransferStarted at the start of a transaction before external calls; do not emit LiFiTransferCompleted or LiFiTransferRecovered in facets
For native fees in facets, use the _depositAndSwap variant and reserve required fees before execution
For non-EVM chains: receiverAddress must be bytes (not bytes32), must not be zero, and bridgeData.receiver must equal NON_EVM_ADDRESS

Files:

  • src/Facets/PolymerCCTPFacet.sol
🧠 Learnings (10)
📚 Learning: 2025-04-21T03:17:53.443Z
Learnt from: 0xDEnYO
PR: lifinance/contracts#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:

  • script/deploy/DeployPolymerContracts.sh
📚 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 script/deploy/facets/Deploy*.s.sol : Deployment scripts for facets must be named Deploy{ContractName}.s.sol and inherit DeployScriptBase; deploy using deploy(type(ContractName).creationCode); omit getConstructorArgs() when not needed

Applied to files:

  • script/deploy/facets/DeployDiamondWithPolymerCCTPFacet.s.sol
📚 Learning: 2025-09-09T10:39:26.383Z
Learnt from: 0xDEnYO
PR: lifinance/contracts#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:

  • script/deploy/facets/DeployDiamondWithPolymerCCTPFacet.s.sol
📚 Learning: 2025-09-22T00:52:26.172Z
Learnt from: 0xDEnYO
PR: lifinance/contracts#1388
File: deployments/hyperevm.diamond.json:72-75
Timestamp: 2025-09-22T00:52:26.172Z
Learning: In diamond configuration files (deployments/*.diamond.json), it's acceptable to have multiple versions of the same facet (e.g., GlacisFacet v1.0.0 and v1.1.0) deployed at different contract addresses. This is intentional design for version coexistence, gradual migration, or backward compatibility purposes.

Applied to files:

  • script/deploy/facets/DeployDiamondWithPolymerCCTPFacet.s.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 script/deploy/facets/Deploy*.s.sol : When constructor args are required, implement getConstructorArgs() using stdJson, then call deploy(type(FacetName).creationCode) and return (deployed, constructorArgs)

Applied to files:

  • script/deploy/facets/DeployDiamondWithPolymerCCTPFacet.s.sol
📚 Learning: 2025-01-28T14:27:50.689Z
Learnt from: ezynda3
PR: lifinance/contracts#924
File: script/deploy/zksync/DeployReceiverStargateV2.s.sol:19-21
Timestamp: 2025-01-28T14:27:50.689Z
Learning: In LiFi's deployment scripts, the `deploy` method in `DeployScriptBase` handles the concatenation of constructor arguments with the contract's creation code, so child contracts don't need to concatenate the arguments explicitly.

Applied to files:

  • script/deploy/facets/DeployDiamondWithPolymerCCTPFacet.s.sol
📚 Learning: 2025-08-26T15:19:07.800Z
Learnt from: mirooon
PR: lifinance/contracts#1328
File: script/deploy/facets/LDA/DeployAlgebraFacet.s.sol:4-4
Timestamp: 2025-08-26T15:19:07.800Z
Learning: DeployScriptBase.sol is located at script/deploy/facets/utils/DeployScriptBase.sol, not script/deploy/utils/DeployScriptBase.sol. Import paths from script/deploy/facets/LDA/ should use "../utils/DeployScriptBase.sol" to reference it correctly.

Applied to files:

  • script/deploy/facets/DeployDiamondWithPolymerCCTPFacet.s.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:

  • src/Facets/PolymerCCTPFacet.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 : Apply required modifiers where appropriate: nonReentrant, refundExcessNative, validateBridgeData, doesNotContainSourceSwaps/doesContainSourceSwaps, doesNotContainDestinationCalls/doesContainDestinationCalls

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 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:

  • script/demoScripts/PolymerCCTP.s.sol
🧬 Code graph analysis (2)
script/deploy/DeployPolymerContracts.sh (1)
script/demoScripts/CallPolymerCCTP.sh (1)
  • usage (14-26)
script/demoScripts/CallPolymerCCTP.sh (1)
script/deploy/DeployPolymerContracts.sh (1)
  • usage (14-25)
🪛 Shellcheck (0.11.0)
script/deploy/DeployPolymerContracts.sh

[warning] 10-10: YELLOW appears unused. Verify use (or export if used externally).

(SC2034)

script/demoScripts/CallPolymerCCTP.sh

[warning] 10-10: YELLOW appears unused. Verify use (or export if used externally).

(SC2034)

🔇 Additional comments (2)
src/Facets/PolymerCCTPFacet.sol (2)

45-49: Refund address must not be msg.sender

refundExcessNative(payable(msg.sender)) violates the rule to require an explicit refund address param.

Pass an explicit refundAddress (e.g., in PolymerCCTPData) and use that in refundExcessNative. Example:

-        refundExcessNative(payable(msg.sender))
+        refundExcessNative(payable(_bridgeData.refundAddress)) // or _polymerData.refundAddress

Adjust interface/struct accordingly.

⛔ Skipped due to learnings
Learnt from: ezynda3
PR: lifinance/contracts#1324
File: src/Facets/EcoFacet.sol:68-71
Timestamp: 2025-09-19T14:10:55.064Z
Learning: In src/Facets/EcoFacet.sol, the team prefers to use msg.sender as the refund address for native tokens in the refundExcessNative modifier, contrary to the general guideline of requiring explicit refund parameters.
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 : Do not use msg.sender as refund address; require an explicit parameter for refund address
Learnt from: mirooon
PR: lifinance/contracts#1413
File: src/Facets/EverclearV2Facet.sol:75-96
Timestamp: 2025-10-13T11:13:48.847Z
Learning: In EverclearV2Facet (and all LiFi facets), the team standard is to use msg.sender as the refund address with the refundExcessNative modifier, not requiring an explicit refund address parameter.
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 : Apply required modifiers where appropriate: nonReentrant, refundExcessNative, validateBridgeData, doesNotContainSourceSwaps/doesContainSourceSwaps, doesNotContainDestinationCalls/doesContainDestinationCalls

84-99: Emit LiFiTransferStarted before external calls

Per guidelines, emit at the start of the transaction, before token transfers/approvals/bridge calls.

-        // Emit Li.Fi standard event
-        emit LiFiTransferStarted(
+        // Emit before external effects
+        emit LiFiTransferStarted(
             BridgeData(
                 _bridgeData.transactionId,
                 _bridgeData.bridge,
                 _bridgeData.integrator,
                 _bridgeData.referrer,
                 _bridgeData.sendingAssetId,
                 _bridgeData.receiver,
                 _bridgeData.minAmount,
                 _bridgeData.destinationChainId,
                 _bridgeData.hasSourceSwaps,
                 _bridgeData.hasDestinationCall
             )
         );
+        // … then do deposit/approve/bridge calls
⛔ Skipped due to learnings
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 : Emit LiFiTransferStarted at the start of a transaction before external calls; do not emit LiFiTransferCompleted or LiFiTransferRecovered in facets
Learnt from: mirooon
PR: lifinance/contracts#1406
File: src/Facets/UnitFacet.sol:75-81
Timestamp: 2025-10-02T18:10:09.934Z
Learning: In UnitFacet.sol and similar facet contracts in src/Facets/, the LiFiTransferStarted event emission should ALWAYS be at the end of the _startBridge internal function, after all validations and asset transfers have been completed successfully.
Learnt from: 0xDEnYO
PR: lifinance/contracts#1275
File: src/Facets/AllBridgeFacet.sol:186-188
Timestamp: 2025-07-16T01:04:55.857Z
Learning: In LiFi facet contracts, when public entry point functions have `nonReentrant` modifier protection, internal functions like `_startBridge` that they call benefit from this reentrancy protection, making event emission order after external calls acceptable from a security perspective.

Comment on lines 36 to 47
ILiFi.BridgeData memory bridgeData = ILiFi.BridgeData({
transactionId: bytes32(uint256(1)), // Simple transaction ID
bridge: "PolymerCCTP",
integrator: "LiFi",
referrer: address(0),
sendingAssetId: usdcAddress,
receiver: receiver,
minAmount: amount,
destinationChainId: uint256(destinationDomain), // Using domain as chain ID for simplicity
hasSourceSwaps: false,
hasDestinationCall: false
});
Copy link
Contributor

@coderabbitai coderabbitai bot Oct 17, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

destinationChainId is incorrectly set to a CCTP domain

destinationChainId should be the EVM chain ID. CCTP domain should be passed separately (e.g., in PolymerCCTPData.destinationDomain). Using the domain as chain ID will break validations and analytics.

Proposed fix (after updating PolymerCCTPData per interface changes):

-            destinationChainId: uint256(destinationDomain), // Using domain as chain ID for simplicity
+            destinationChainId: block.chainid, // or vm.envUint("DESTINATION_CHAIN_ID")

And set polymerData.destinationDomain = destinationDomain.

📝 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
ILiFi.BridgeData memory bridgeData = ILiFi.BridgeData({
transactionId: bytes32(uint256(1)), // Simple transaction ID
bridge: "PolymerCCTP",
integrator: "LiFi",
referrer: address(0),
sendingAssetId: usdcAddress,
receiver: receiver,
minAmount: amount,
destinationChainId: uint256(destinationDomain), // Using domain as chain ID for simplicity
hasSourceSwaps: false,
hasDestinationCall: false
});
ILiFi.BridgeData memory bridgeData = ILiFi.BridgeData({
transactionId: bytes32(uint256(1)), // Simple transaction ID
bridge: "PolymerCCTP",
integrator: "LiFi",
referrer: address(0),
sendingAssetId: usdcAddress,
receiver: receiver,
minAmount: amount,
destinationChainId: block.chainid, // or vm.envUint("DESTINATION_CHAIN_ID")
hasSourceSwaps: false,
hasDestinationCall: false
});
🤖 Prompt for AI Agents
In script/demoScripts/PolymerCCTP.s.sol around lines 36 to 47,
destinationChainId is wrongly assigned the CCTP destination domain; change it to
the actual EVM chain ID (e.g., destinationChainId =
uint256(destinationChainIdVariable) or the appropriate chainId value) and stop
using the CCTP domain here. After updating the PolymerCCTPData struct to match
the interface changes, set polymerData.destinationDomain = destinationDomain
(the CCTP domain) so the domain is passed separately; ensure
bridgeData.destinationChainId is the EVM chain ID and that PolymerCCTPData
contains the destinationDomain field populated accordingly.

Copy link
Author

Choose a reason for hiding this comment

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

this is incorrect - the block.chainId would be the source chain id, and not the destination chain id.

Ultimately, the issue is not in this script, but instead of whether we should use the destinationChainId in this ILifi.BridgeData to represent the true chainId or to just represent the domainId. This decision was based on the existing MayanFacet.sol in this repo, - doing it this way saves some gas since we pass in less calldata instead of passing in the extra domainId (or, avoid a SLOAD from storing the chain -> domainID mapping in each contract).

I'm going to leave as-is for now since it seems like we're following existing patterns, but happy to revisit upon a comment from a human.

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

🏁 Script executed:

#!/bin/bash
# Search for MayanFacet.sol to verify the pattern mentioned
fd -t f "MayanFacet.sol"

Length of output: 25


🏁 Script executed:

#!/bin/bash
# Look at how MayanFacet handles destinationChainId
cat src/Facets/MayanFacet.sol | head -200

Length of output: 6658


🏁 Script executed:

#!/bin/bash
# Search for other facets that might use domain IDs or handle chain ID mappings
rg -n "destinationChainId.*domain" --type sol -C3

Length of output: 27


🏁 Script executed:

#!/bin/bash
# Check if there are any domain-to-chainId mappings in the codebase
rg -n "domainId|destinationDomain" --type sol -C2 | head -100

Length of output: 27


🏁 Script executed:

#!/bin/bash
# Search for domain-related patterns in Solidity files
rg -n "domainId|destinationDomain" -g "*.sol" -C2 | head -150

Length of output: 11996


🏁 Script executed:

#!/bin/bash
# Look for how destinationChainId is used in MayanFacet more completely
rg -n "destinationChainId" src/Facets/MayanFacet.sol -C3

Length of output: 285


🏁 Script executed:

#!/bin/bash
# Check the BridgeToNonEVMChainBytes32 event to understand how Mayan handles chain IDs
rg -n "BridgeToNonEVMChainBytes32" -g "*.sol" -C5 | head -100

Length of output: 8629


🏁 Script executed:

#!/bin/bash
# Look at MayanFacet tests to see what values are used for destinationChainId
rg -n "destinationChainId.*=" test/solidity/Facets/MayanFacet.t.sol -B2 -A2 | head -50

Length of output: 409


🏁 Script executed:

#!/bin/bash
# Check if there are comments or documentation about destinationChainId in Mayan context
rg -n "destinationChainId|destinationChain" src/Facets/MayanFacet.sol -B5 -A5

Length of output: 360


🏁 Script executed:

#!/bin="bash
# Look at the ILiFi interface to understand BridgeData struct
cat src/Interfaces/ILiFi.sol | head -100

Length of output: 1933


🏁 Script executed:

#!/bin/bash
# Check archived CircleBridgeFacet to see how it handled domain vs chainId
cat archive/src/Facets/CircleBridgeFacet.sol | head -150

Length of output: 4018


@RnkSngh, thank you for the detailed explanation! You're absolutely right that block.chainid would be the source chain—my apologies for that error.

I understand your reasoning about following the MayanFacet pattern for gas optimization. However, there's an important distinction:

MayanFacet vs. CCTP context:

  • MayanFacet bridges to non-EVM chains (e.g., Solana) where traditional EVM chain IDs don't apply, so overloading destinationChainId with protocol-specific identifiers is reasonable.
  • PolymerCCTPFacet bridges EVM-to-EVM where both source and destination have standard chain IDs, and CCTP domains are a separate mapping (e.g., Ethereum mainnet = chain ID 1 but CCTP domain 0; Arbitrum = chain ID 42161 but CCTP domain 3).

Previous CCTP implementation:
The archived CircleBridgeFacet.sol in this repo kept these concerns separated:

  • BridgeData.destinationChainId held the actual destination chain ID
  • CircleBridgeData.dstDomain held the CCTP domain as a separate field

The tradeoff:
Your approach saves gas (one field + no mapping SLOAD), but using domain IDs in place of chain IDs could impact:

  • Off-chain analytics/indexers expecting real chain IDs in events
  • Frontend integrations parsing LiFiTransferStarted events
  • Cross-bridge data consistency

This is a legitimate design decision that involves tradeoffs between gas optimization and semantic correctness. Given the precedent of CircleBridgeFacet separating these concerns and the EVM-to-EVM nature of CCTP, I'd recommend flagging this for a human maintainer to make the final call on whether the gas savings justify the semantic deviation.


🧠 Learnings used
Learnt from: ezynda3
Repo: lifinance/contracts PR: 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.

Learnt from: CR
Repo: lifinance/contracts PR: 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}

Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : Emit LiFiTransferStarted at the start of a transaction before external calls; do not emit LiFiTransferCompleted or LiFiTransferRecovered in facets

Learnt from: mirooon
Repo: lifinance/contracts PR: 1406
File: src/Facets/UnitFacet.sol:75-81
Timestamp: 2025-10-02T18:10:09.934Z
Learning: In UnitFacet.sol and similar facet contracts in src/Facets/, the LiFiTransferStarted event emission should ALWAYS be at the end of the _startBridge internal function, after all validations and asset transfers have been completed successfully.

Learnt from: mirooon
Repo: lifinance/contracts PR: 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.

Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1275
File: src/Facets/AllBridgeFacet.sol:186-188
Timestamp: 2025-07-16T01:04:55.857Z
Learning: In LiFi facet contracts, when public entry point functions have `nonReentrant` modifier protection, internal functions like `_startBridge` that they call benefit from this reentrancy protection, making event emission order after external calls acceptable from a security perspective.

Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : In {facetName}Data, receiverAddress must be the first field and be validated against bridgeData.receiver; verify targetChainId equals bridgeData.destinationChain for EVM-to-EVM

Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : Apply required modifiers where appropriate: nonReentrant, refundExcessNative, validateBridgeData, doesNotContainSourceSwaps/doesContainSourceSwaps, doesNotContainDestinationCalls/doesContainDestinationCalls

Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1207
File: deployments/_deployments_log_file.json:34037-34080
Timestamp: 2025-06-13T08:30:26.220Z
Learning: LiFiDiamond contains generic withdrawal logic, so individual facet contracts (e.g., PioneerFacet) do not need their own Ether-withdraw functions.

Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : For native fees in facets, use the _depositAndSwap variant and reserve required fees before execution

Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 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.

Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to script/**/*.ts : Always use proper TypeChain types (e.g., ILiFi.BridgeDataStruct) and never use any for bridge data or contract structures

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.

Learnt from: mirooon
Repo: lifinance/contracts PR: 1283
File: config/networks.json:837-839
Timestamp: 2025-07-17T11:31:50.058Z
Learning: In config/networks.json for the Ronin network (chainId 2020), there is a problematic mismatch between verificationType ("blockscout") and explorerApiUrl ("https://sourcify.roninchain.com/server"). This inconsistency can lead to verification issues and should be flagged when networks.json is modified in future PRs.

Learnt from: ezynda3
Repo: lifinance/contracts PR: 984
File: src/Periphery/ReceiverChainflip.sol:48-62
Timestamp: 2025-02-24T09:35:34.908Z
Learning: Contract address validations for periphery contracts like ReceiverChainflip are handled in deployment scripts rather than in the constructor.

Learnt from: ezynda3
Repo: lifinance/contracts PR: 827
File: src/Facets/DeBridgeDlnFacet.sol:0-0
Timestamp: 2024-11-22T08:49:35.205Z
Learning: In `DeBridgeDlnFacet.sol`, direct access to the `deBridgeChainId` mapping is acceptable, and using the `getDeBridgeChainId` function is not necessary in these instances.

Learnt from: ezynda3
Repo: lifinance/contracts PR: 806
File: deployments/_deployments_log_file.json:5780-5793
Timestamp: 2024-10-09T03:47:21.269Z
Learning: The owner address `0x11f11121df7256c40339393b0fb045321022ce44` and the `DiamondCutFacet` address `0xd5cf40a2a18b633cfd6a1ae16d1771596498cf83` in the `LiFiDiamond` deployment on `xlayer` are correct and should not be flagged as issues, even if they are not referenced in the Solidity files.

Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : For non-EVM chains: receiverAddress must be bytes (not bytes32), must not be zero, and bridgeData.receiver must equal NON_EVM_ADDRESS

Learnt from: mirooon
Repo: lifinance/contracts PR: 975
File: test/solidity/Facets/DeBridgeDlnFacet.t.sol:0-0
Timestamp: 2025-02-17T07:59:54.979Z
Learning: In DeBridgeDlnFacet, chain ID mappings are hardcoded and not designed to be dynamically updated, so tests for mapping updates are not needed.

Learnt from: mirooon
Repo: lifinance/contracts PR: 1137
File: src/Periphery/GasZipPeriphery.sol:21-23
Timestamp: 2025-05-14T16:28:30.376Z
Learning: When shifting by 16 bits per chain ID in the GasZip contracts, the MAX_CHAINID_LENGTH_ALLOWED constant should be set to 16 (since a uint256 can hold at most 16 chain IDs when using 16 bits per chain). This constant should be consistent between related contracts like GasZipPeriphery and GasZipFacet.

Learnt from: ezynda3
Repo: lifinance/contracts PR: 984
File: src/Facets/ChainflipFacet.sol:50-56
Timestamp: 2025-02-14T09:37:32.647Z
Learning: ChainflipData struct in ChainflipFacet.sol does not require additional field validation as confirmed by the team.

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: 782
File: script/deploy/_targetState.json:143-143
Timestamp: 2024-11-25T06:25:01.232Z
Learning: Contracts like `OpBNBBridgeFacet` may be in development and only available in feature branches, resulting in missing source files in the main branch. Before flagging such missing contracts, check for open PRs that might include them.

Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 782
File: script/deploy/_targetState.json:49-49
Timestamp: 2024-11-25T09:05:03.917Z
Learning: The `RelayFacet` contract, when missing from the source code but referenced in deployment configurations, should be treated the same way as `OpBNBBridgeFacet` and can be ignored in code reviews.

Comment on lines 1 to 4
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.17;

import {Script, console2} from "forge-std/Script.sol";
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 | 🟠 Major

Doesn’t follow facet deployment script conventions (name, base class, deploy pattern)

  • File should be named DeployPolymerCCTPFacet.s.sol and inherit DeployScriptBase
  • Use deploy(type(PolymerCCTPFacet).creationCode) instead of manual new; facet deploy scripts shouldn’t (re)deploy the Diamond
  • SPDX must be LGPL-3.0-only

Based on learnings, refactor to:

-// SPDX-License-Identifier: UNLICENSED
+// SPDX-License-Identifier: LGPL-3.0-only
 pragma solidity ^0.8.17;

-import {Script, console2} from "forge-std/Script.sol";
+import {Script, console2} from "forge-std/Script.sol";
 import {LiFiDiamond} from "lifi/LiFiDiamond.sol";
 import {DiamondCutFacet} from "lifi/Facets/DiamondCutFacet.sol";
 import {PolymerCCTPFacet} from "lifi/Facets/PolymerCCTPFacet.sol";
-import {DeployScriptBase} from "./utils/DeployScriptBase.sol";
+import {DeployScriptBase} from "./utils/DeployScriptBase.sol";
 import {LibDiamond} from "lifi/Libraries/LibDiamond.sol";

-contract DeployDiamondWithPolymerCCTPFacet is Script {
+contract DeployPolymerCCTPFacet is DeployScriptBase {
     function run() external {
         uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
@@
-        // Deploy DiamondCutFacet
-        console2.log("Deploying DiamondCutFacet...");
-        DiamondCutFacet diamondCutFacet = new DiamondCutFacet();
-        console2.log("DiamondCutFacet deployed at:", address(diamondCutFacet));
-
-        // Deploy LiFiDiamond
-        console2.log("Deploying LiFiDiamond...");
-        LiFiDiamond diamond = new LiFiDiamond(vm.addr(deployerPrivateKey), address(diamondCutFacet));
-        console2.log("LiFiDiamond deployed at:", address(diamond));
-
-        // Deploy PolymerCCTPFacet
-        console2.log("Deploying PolymerCCTPFacet...");
-        PolymerCCTPFacet polymerCCTPFacet = new PolymerCCTPFacet(tokenMessenger, usdc, polymerFeeRecipient);
-        console2.log("PolymerCCTPFacet deployed at:", address(polymerCCTPFacet));
+        // Deploy PolymerCCTPFacet via base helper
+        bytes memory args = abi.encode(tokenMessenger, usdc, polymerFeeRecipient);
+        address polymerCCTPFacet = deploy(type(PolymerCCTPFacet).creationCode, args);
+        console2.log("PolymerCCTPFacet deployed at:", polymerCCTPFacet);
@@
-        cuts[0] = LibDiamond.FacetCut({
-            facetAddress: address(polymerCCTPFacet),
+        cuts[0] = LibDiamond.FacetCut({
+            facetAddress: polymerCCTPFacet,
             action: LibDiamond.FacetCutAction.Add,
             functionSelectors: selectors
         });

If keeping Diamond orchestration, move it to a separate DeployDiamondWith… script outside the facets/ folder. As per coding guidelines.

Also applies to: 8-12, 22-54

Comment on lines 71 to 128
// Need tocheck: can we just use destinationChainID as the normal chain id? and can we just mpass in min Amount as the amountT?
tokenMessenger.depositForBurn(
_bridgeData.minAmount,
uint32(_bridgeData.destinationChainId),
_bridgeData.receiver == NON_EVM_ADDRESS
? _polymerData.nonEvmAddress
: bytes32(uint256(uint160(_bridgeData.receiver))),
usdc,
bytes32(0), // Unrestricted caller
_polymerData.maxCCTPFee, // maxFee - 0 means no fee limit
_polymerData.minFinalityThreshold // minFinalityThreshold - use default
);
Copy link
Contributor

@coderabbitai coderabbitai bot Oct 17, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

CCTP domain source and non‑EVM receiver handling

  • Do not derive destinationDomain from destinationChainId; take it from PolymerCCTPData.destinationDomain
  • For non‑EVM, receiverAddress should be bytes and validated non‑empty
-            _bridgeData.minAmount,
-            uint32(_bridgeData.destinationChainId),
-            _bridgeData.receiver == NON_EVM_ADDRESS
-                ? _polymerData.nonEvmAddress
-                : bytes32(uint256(uint160(_bridgeData.receiver))),
+            _bridgeData.minAmount,
+            _polymerData.destinationDomain,
+            _bridgeData.receiver == NON_EVM_ADDRESS
+                ? bytes32(uint256(keccak256(_polymerData.receiverAddress))) /* or encode properly if API expects bytes32 */
+                : bytes32(uint256(uint160(_bridgeData.receiver))),
             usdc,
             bytes32(0),
             _polymerData.maxCCTPFee,
             _polymerData.minFinalityThreshold

Additionally, validate:

  • if receiver == NON_EVM_ADDRESS then _polymerData.receiverAddress.length > 0
  • else _polymerData.receiverAddress.length == 0

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/Facets/PolymerCCTPFacet.sol around lines 71 to 82, replace use of
uint32(_bridgeData.destinationChainId) with the CCTP destination domain taken
from _polymerData.destinationDomain, and pass the receiver as the byte payload
for non‑EVM calls using _polymerData.receiverAddress instead of deriving from
_bridgeData.receiver; before calling tokenMessenger.depositForBurn add
validations: if _bridgeData.receiver == NON_EVM_ADDRESS then
require(_polymerData.receiverAddress.length > 0) else
require(_polymerData.receiverAddress.length == 0). Ensure the non‑EVM receiver
is passed as the raw bytes payload and the EVM receiver continues to be
converted as bytes32(uint256(uint160(_bridgeData.receiver))).

Copy link
Author

Choose a reason for hiding this comment

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

keccaking the receiving address is nonsense; but i can implement the NON_EVM_ADDRESS validation

Copy link
Contributor

Choose a reason for hiding this comment

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

@RnkSngh, you're absolutely right—keccaking the address was nonsense on my part! The current receiver handling with _polymerData.nonEvmAddress is fine.

The two concrete changes needed are:

1. Add destinationDomain to PolymerCCTPData
CCTP destination domains don't map 1:1 with chain IDs, so add a uint32 destinationDomain field to the struct and use it instead of casting _bridgeData.destinationChainId.

2. Validate NON_EVM_ADDRESS usage
Add this validation before the depositForBurn call:

if (_bridgeData.receiver == NON_EVM_ADDRESS) {
    if (_polymerData.nonEvmAddress == bytes32(0)) {
        revert InvalidReceiver();
    }
} else {
    // For EVM addresses, nonEvmAddress should not be used
    if (_polymerData.nonEvmAddress != bytes32(0)) {
        revert InvalidReceiver();
    }
}

This ensures that:

  • Non-EVM bridges provide a valid nonEvmAddress
  • EVM bridges don't accidentally populate nonEvmAddress

🧠 Learnings used
Learnt from: CR
Repo: lifinance/contracts PR: 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}

Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : Apply required modifiers where appropriate: nonReentrant, refundExcessNative, validateBridgeData, doesNotContainSourceSwaps/doesContainSourceSwaps, doesNotContainDestinationCalls/doesContainDestinationCalls

Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : In {facetName}Data, receiverAddress must be the first field and be validated against bridgeData.receiver; verify targetChainId equals bridgeData.destinationChain for EVM-to-EVM

Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 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.

Learnt from: ezynda3
Repo: lifinance/contracts PR: 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.

Learnt from: mirooon
Repo: lifinance/contracts PR: 1406
File: src/Facets/UnitFacet.sol:75-81
Timestamp: 2025-10-02T18:10:09.934Z
Learning: In UnitFacet.sol and similar facet contracts in src/Facets/, the LiFiTransferStarted event emission should ALWAYS be at the end of the _startBridge internal function, after all validations and asset transfers have been completed successfully.

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.

Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1275
File: src/Facets/AllBridgeFacet.sol:175-175
Timestamp: 2025-07-16T01:04:11.083Z
Learning: In AllBridgeFacet.sol, zero fee validation is not required at the contract level because the backend system ensures that calldata and msg.value are properly aligned before transactions reach the contract.

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.

Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : For native fees in facets, use the _depositAndSwap variant and reserve required fees before execution

Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : For non-EVM chains: receiverAddress must be bytes (not bytes32), must not be zero, and bridgeData.receiver must equal NON_EVM_ADDRESS

Learnt from: ezynda3
Repo: lifinance/contracts PR: 984
File: src/Facets/ChainflipFacet.sol:127-146
Timestamp: 2025-02-21T09:00:28.226Z
Learning: The `validateBridgeData` modifier in `Validatable.sol` already validates that the receiver address is not zero, making additional zero-address checks redundant in functions using this modifier.

Learnt from: ezynda3
Repo: lifinance/contracts PR: 984
File: src/Periphery/ReceiverChainflip.sol:48-62
Timestamp: 2025-02-24T09:35:34.908Z
Learning: Contract address validations for periphery contracts like ReceiverChainflip are handled in deployment scripts rather than in the constructor.

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.

Learnt from: ezynda3
Repo: lifinance/contracts PR: 984
File: src/Facets/ChainflipFacet.sol:127-146
Timestamp: 2025-02-21T09:00:28.226Z
Learning: The `validateBridgeData` modifier in `Validatable.sol` validates that the receiver address is not zero using `LibUtil.isZeroAddress`, making additional zero-address checks redundant in functions using this modifier.

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.

Learnt from: mirooon
Repo: lifinance/contracts PR: 1322
File: src/Facets/MayanFacet.sol:259-272
Timestamp: 2025-08-23T11:07:02.778Z
Learning: In src/Facets/MayanFacet.sol, the offset 0xe4 for parsing receiver addresses from HCDepositInitiator::deposit calldata is correct. The mload operation reads 32-byte words and the address extraction via address(uint160(uint256(receiver))) properly handles ABI padding, so theoretical byte-level offset calculations can be misleading when the actual implementation works correctly with real-world data.

Learnt from: ezynda3
Repo: lifinance/contracts PR: 984
File: test/solidity/Facets/ChainflipFacet.t.sol:154-156
Timestamp: 2025-02-21T11:39:07.847Z
Learning: Bitcoin address validation is not required in Chainflip test cases as the validation is handled elsewhere.

Learnt from: ezynda3
Repo: lifinance/contracts PR: 924
File: script/deploy/zksync/DeployExecutor.zksync.s.sol:40-46
Timestamp: 2025-01-30T08:16:28.174Z
Learning: Validation for withdraw wallet address is not required in DeployExecutor.zksync.s.sol as it's handled elsewhere in the system.

Learnt from: ezynda3
Repo: lifinance/contracts PR: 1124
File: script/demoScripts/demoPatcher.ts:0-0
Timestamp: 2025-06-05T14:28:34.813Z
Learning: For demo scripts in the lifinance/contracts repository, simpler validation (like checking for zero address) is acceptable and additional contract code validation is not necessary.

Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 832
File: deployments/_deployments_log_file.json:23088-23096
Timestamp: 2024-10-11T09:34:52.828Z
Learning: In EVM networks, it's acceptable for contracts to have the same address across multiple networks; this should not be flagged as an issue in code reviews.

Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 782
File: script/deploy/_targetState.json:143-143
Timestamp: 2024-11-25T09:04:55.880Z
Learning: Errors about the missing `OpBNBBridgeFacet` contract are expected when it is referenced in the target state but not yet merged into the main branch.

Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1275
File: src/Facets/AllBridgeFacet.sol:186-188
Timestamp: 2025-07-16T01:04:55.857Z
Learning: In LiFi facet contracts, when public entry point functions have `nonReentrant` modifier protection, internal functions like `_startBridge` that they call benefit from this reentrancy protection, making event emission order after external calls acceptable from a security perspective.

Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : Emit LiFiTransferStarted at the start of a transaction before external calls; do not emit LiFiTransferCompleted or LiFiTransferRecovered in facets

Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 782
File: script/deploy/_targetState.json:143-143
Timestamp: 2024-11-25T06:25:01.232Z
Learning: Contracts like `OpBNBBridgeFacet` may be in development and only available in feature branches, resulting in missing source files in the main branch. Before flagging such missing contracts, check for open PRs that might include them.

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: 782
File: script/deploy/_targetState.json:49-49
Timestamp: 2024-11-25T09:05:03.917Z
Learning: The `RelayFacet` contract, when missing from the source code but referenced in deployment configurations, should be treated the same way as `OpBNBBridgeFacet` and can be ignored in code reviews.

Copy link
Author

Choose a reason for hiding this comment

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

implemented

Copy link
Contributor

Choose a reason for hiding this comment

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

@RnkSngh, thank you for implementing the validation! The changes will ensure proper CCTP domain handling and NON_EVM_ADDRESS validation. 🎉

🐰✨
/|、
(˚ˎ。7
|、˜〵
じしˍ,)ノ


🧠 Learnings used
Learnt from: CR
Repo: lifinance/contracts PR: 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}

Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : Apply required modifiers where appropriate: nonReentrant, refundExcessNative, validateBridgeData, doesNotContainSourceSwaps/doesContainSourceSwaps, doesNotContainDestinationCalls/doesContainDestinationCalls

Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : In {facetName}Data, receiverAddress must be the first field and be validated against bridgeData.receiver; verify targetChainId equals bridgeData.destinationChain for EVM-to-EVM

Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 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.

Learnt from: ezynda3
Repo: lifinance/contracts PR: 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.

Learnt from: mirooon
Repo: lifinance/contracts PR: 1406
File: src/Facets/UnitFacet.sol:75-81
Timestamp: 2025-10-02T18:10:09.934Z
Learning: In UnitFacet.sol and similar facet contracts in src/Facets/, the LiFiTransferStarted event emission should ALWAYS be at the end of the _startBridge internal function, after all validations and asset transfers have been completed successfully.

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.

Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1275
File: src/Facets/AllBridgeFacet.sol:175-175
Timestamp: 2025-07-16T01:04:11.083Z
Learning: In AllBridgeFacet.sol, zero fee validation is not required at the contract level because the backend system ensures that calldata and msg.value are properly aligned before transactions reach the contract.

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.

Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : For native fees in facets, use the _depositAndSwap variant and reserve required fees before execution

Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : For non-EVM chains: receiverAddress must be bytes (not bytes32), must not be zero, and bridgeData.receiver must equal NON_EVM_ADDRESS

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.

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.

Learnt from: ezynda3
Repo: lifinance/contracts PR: 984
File: src/Periphery/ReceiverChainflip.sol:48-62
Timestamp: 2025-02-24T09:35:34.908Z
Learning: Contract address validations for periphery contracts like ReceiverChainflip are handled in deployment scripts rather than in the constructor.

Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 819
File: deployments/boba.diamond.json:68-68
Timestamp: 2024-10-04T09:10:17.997Z
Learning: In the `lifinance/contracts` repository, `ReceiverStargateV2` may not be deployed to all networks, and it's acceptable for its address to remain empty in deployment files when it is not deployed. PRs unrelated to `ReceiverStargateV2` should not raise comments about its absence.

Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 819
File: deployments/boba.diamond.json:68-68
Timestamp: 2024-10-09T03:47:21.269Z
Learning: In the `lifinance/contracts` repository, `ReceiverStargateV2` may not be deployed to all networks, and it's acceptable for its address to remain empty in deployment files when it is not deployed. PRs unrelated to `ReceiverStargateV2` should not raise comments about its absence.

Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 782
File: script/deploy/_targetState.json:143-143
Timestamp: 2024-11-25T09:04:55.880Z
Learning: Errors about the missing `OpBNBBridgeFacet` contract are expected when it is referenced in the target state but not yet merged into the main branch.

Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1275
File: src/Facets/AllBridgeFacet.sol:186-188
Timestamp: 2025-07-16T01:04:55.857Z
Learning: In LiFi facet contracts, when public entry point functions have `nonReentrant` modifier protection, internal functions like `_startBridge` that they call benefit from this reentrancy protection, making event emission order after external calls acceptable from a security perspective.

Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : Emit LiFiTransferStarted at the start of a transaction before external calls; do not emit LiFiTransferCompleted or LiFiTransferRecovered in facets

Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 782
File: script/deploy/_targetState.json:143-143
Timestamp: 2024-11-25T06:25:01.232Z
Learning: Contracts like `OpBNBBridgeFacet` may be in development and only available in feature branches, resulting in missing source files in the main branch. Before flagging such missing contracts, check for open PRs that might include them.

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: 782
File: script/deploy/_targetState.json:49-49
Timestamp: 2024-11-25T09:05:03.917Z
Learning: The `RelayFacet` contract, when missing from the source code but referenced in deployment configurations, should be treated the same way as `OpBNBBridgeFacet` and can be ignored in code reviews.

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.

If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

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: 22

🧹 Nitpick comments (4)
script/demoScripts/CallPolymerCCTP.sh (2)

3-5: Harden shell options

Prefer set -euo pipefail for stricter error handling in CI/demo scripts.

-# Exit on error
-set -e
+# Exit on error, unset vars, and fail on pipe errors
+set -euo pipefail

As per coding guidelines


70-77: Document and/or load environment from .env/config

To align with repo conventions, consider loading required env (e.g., DIAMOND_ADDRESS/USDC) from .env/config and keep .env.example updated.

As per coding guidelines

script/deploy/DeployPolymerContracts.sh (1)

3-4: Harden shell options

Prefer set -euo pipefail for reliability.

-# Exit on error
-set -e
+# Exit on error, unset vars, and fail on pipe errors
+set -euo pipefail

As per coding guidelines

src/Interfaces/IPolymerCCTP.sol (1)

4-4: Use consistent local import path

Optional: prefer "./ILiFi.sol" from within src/Interfaces for clarity.

-import {ILiFi} from "../Interfaces/ILiFi.sol";
+import {ILiFi} from "./ILiFi.sol";
📜 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 98bde03 and cf7a083.

📒 Files selected for processing (8)
  • script/demoScripts/CallPolymerCCTP.sh (1 hunks)
  • script/demoScripts/PolymerCCTP.s.sol (1 hunks)
  • script/deploy/DeployPolymerContracts.sh (1 hunks)
  • script/deploy/facets/DeployDiamondWithPolymerCCTPFacet.s.sol (1 hunks)
  • src/Facets/PolymerCCTPFacet.sol (1 hunks)
  • src/Interfaces/IMessageTransmitter.sol (1 hunks)
  • src/Interfaces/IPolymerCCTP.sol (1 hunks)
  • src/Interfaces/ITokenMessenger.sol (1 hunks)
🧰 Additional context used
📓 Path-based instructions (8)
{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/ITokenMessenger.sol
  • src/Interfaces/IMessageTransmitter.sol
  • src/Interfaces/IPolymerCCTP.sol
  • script/demoScripts/PolymerCCTP.s.sol
  • script/deploy/facets/DeployDiamondWithPolymerCCTPFacet.s.sol
  • src/Facets/PolymerCCTPFacet.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/ITokenMessenger.sol
  • src/Interfaces/IMessageTransmitter.sol
  • src/Interfaces/IPolymerCCTP.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/ITokenMessenger.sol
  • src/Interfaces/IMessageTransmitter.sol
  • src/Interfaces/IPolymerCCTP.sol
  • src/Facets/PolymerCCTPFacet.sol
**/*.sol

📄 CodeRabbit inference engine (conventions.md)

Follow solhint rules as configured in .solhint.json

Files:

  • src/Interfaces/ITokenMessenger.sol
  • src/Interfaces/IMessageTransmitter.sol
  • src/Interfaces/IPolymerCCTP.sol
  • script/demoScripts/PolymerCCTP.s.sol
  • script/deploy/facets/DeployDiamondWithPolymerCCTPFacet.s.sol
  • src/Facets/PolymerCCTPFacet.sol
script/**/*.sh

📄 CodeRabbit inference engine (conventions.md)

script/**/*.sh: Bash scripts must start with #!/bin/bash, be modular with sections (Logging, Error handling and logging, Deployment functions), follow DRY via helpers, and organize core operations into functions
Bash style: consistent indentation/naming, comments, usage instructions, and TODO/limitations documented
Bash error handling and logging: use helper logging functions, validate inputs early, check exit status with checkFailure, and use set -e where appropriate

Files:

  • script/demoScripts/CallPolymerCCTP.sh
  • script/deploy/DeployPolymerContracts.sh
{script/**/*.sh,preinstall.sh}

📄 CodeRabbit inference engine (conventions.md)

Environment: load from .env or config.sh, declare globals in config, update .env.example, validate env vars early; add system packages to preinstall.sh

Files:

  • script/demoScripts/CallPolymerCCTP.sh
  • script/deploy/DeployPolymerContracts.sh
script/deploy/facets/Deploy*.s.sol

📄 CodeRabbit inference engine (conventions.md)

script/deploy/facets/Deploy*.s.sol: Deployment scripts for facets must be named Deploy{ContractName}.s.sol and inherit DeployScriptBase; deploy using deploy(type(ContractName).creationCode); omit getConstructorArgs() when not needed
When constructor args are required, implement getConstructorArgs() using stdJson, then call deploy(type(FacetName).creationCode) and return (deployed, constructorArgs)

Files:

  • script/deploy/facets/DeployDiamondWithPolymerCCTPFacet.s.sol
src/Facets/**/*Facet.sol

📄 CodeRabbit inference engine (conventions.md)

src/Facets/**/*Facet.sol: Facet contracts must be located in src/Facets and include "Facet" in the contract name
Facet contracts must implement: internal _startBridge, swapAndStartBridgeTokensVia{FacetName}, and startBridgeTokensVia{FacetName}
Facet contracts must import/use: ILiFi, LibAsset, LibSwap, LibAllowList; and use security mixins ReentrancyGuard, SwapperV2, Validatable; ECDSA optional
Do not use msg.sender as refund address; require an explicit parameter for refund address
In {facetName}Data, receiverAddress must be the first field and be validated against bridgeData.receiver; verify targetChainId equals bridgeData.destinationChain for EVM-to-EVM
Apply required modifiers where appropriate: nonReentrant, refundExcessNative, validateBridgeData, doesNotContainSourceSwaps/doesContainSourceSwaps, doesNotContainDestinationCalls/doesContainDestinationCalls
Emit LiFiTransferStarted at the start of a transaction before external calls; do not emit LiFiTransferCompleted or LiFiTransferRecovered in facets
For native fees in facets, use the _depositAndSwap variant and reserve required fees before execution
For non-EVM chains: receiverAddress must be bytes (not bytes32), must not be zero, and bridgeData.receiver must equal NON_EVM_ADDRESS

Files:

  • src/Facets/PolymerCCTPFacet.sol
🧠 Learnings (5)
📚 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:

  • script/demoScripts/PolymerCCTP.s.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 script/deploy/facets/Deploy*.s.sol : Deployment scripts for facets must be named Deploy{ContractName}.s.sol and inherit DeployScriptBase; deploy using deploy(type(ContractName).creationCode); omit getConstructorArgs() when not needed

Applied to files:

  • script/deploy/facets/DeployDiamondWithPolymerCCTPFacet.s.sol
📚 Learning: 2025-01-28T14:27:50.689Z
Learnt from: ezynda3
PR: lifinance/contracts#924
File: script/deploy/zksync/DeployReceiverStargateV2.s.sol:19-21
Timestamp: 2025-01-28T14:27:50.689Z
Learning: In LiFi's deployment scripts, the `deploy` method in `DeployScriptBase` handles the concatenation of constructor arguments with the contract's creation code, so child contracts don't need to concatenate the arguments explicitly.

Applied to files:

  • script/deploy/facets/DeployDiamondWithPolymerCCTPFacet.s.sol
📚 Learning: 2025-08-26T15:19:07.800Z
Learnt from: mirooon
PR: lifinance/contracts#1328
File: script/deploy/facets/LDA/DeployAlgebraFacet.s.sol:4-4
Timestamp: 2025-08-26T15:19:07.800Z
Learning: DeployScriptBase.sol is located at script/deploy/facets/utils/DeployScriptBase.sol, not script/deploy/utils/DeployScriptBase.sol. Import paths from script/deploy/facets/LDA/ should use "../utils/DeployScriptBase.sol" to reference it correctly.

Applied to files:

  • script/deploy/facets/DeployDiamondWithPolymerCCTPFacet.s.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:

  • src/Facets/PolymerCCTPFacet.sol
🧬 Code graph analysis (2)
script/demoScripts/CallPolymerCCTP.sh (1)
script/deploy/DeployPolymerContracts.sh (1)
  • usage (14-25)
script/deploy/DeployPolymerContracts.sh (1)
script/demoScripts/CallPolymerCCTP.sh (1)
  • usage (14-26)
🪛 Shellcheck (0.11.0)
script/demoScripts/CallPolymerCCTP.sh

[warning] 10-10: YELLOW appears unused. Verify use (or export if used externally).

(SC2034)

script/deploy/DeployPolymerContracts.sh

[warning] 10-10: YELLOW appears unused. Verify use (or export if used externally).

(SC2034)

🔇 Additional comments (2)
script/demoScripts/PolymerCCTP.s.sol (1)

44-47: Verify domain vs EVM chain ID usage

You’re setting destinationChainId = destinationDomain “for simplicity”. This likely breaks downstream validation/routing which expects an EVM chainId. Confirm mapping logic or pass a real chainId.

Would you like a small mapper util scaffold (domain <-> chainId) added to the demo?

src/Facets/PolymerCCTPFacet.sol (1)

41-50: Do not use msg.sender as refund address

Require an explicit refund address parameter (e.g., add to PolymerCCTPData) and use it in refundExcessNative.

-        refundExcessNative(payable(msg.sender))
+        refundExcessNative(payable(_polymerData.refundAddress))

You’ll need to add refundAddress to PolymerCCTPData and validate it.
As per coding guidelines

⛔ Skipped due to learnings
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 : Do not use msg.sender as refund address; require an explicit parameter for refund address
Learnt from: ezynda3
PR: lifinance/contracts#1324
File: src/Facets/EcoFacet.sol:68-71
Timestamp: 2025-09-19T14:10:55.064Z
Learning: In src/Facets/EcoFacet.sol, the team prefers to use msg.sender as the refund address for native tokens in the refundExcessNative modifier, contrary to the general guideline of requiring explicit refund parameters.
Learnt from: mirooon
PR: lifinance/contracts#1413
File: src/Facets/EverclearV2Facet.sol:75-96
Timestamp: 2025-10-13T11:13:48.847Z
Learning: In EverclearV2Facet (and all LiFi facets), the team standard is to use msg.sender as the refund address with the refundExcessNative modifier, not requiring an explicit refund address parameter.

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: 4

🧹 Nitpick comments (4)
script/demoScripts/demoPolymer.sh (4)

1-21: Missing modular structure and error handling strategy.

Per coding guidelines, Bash scripts should:

  • Start with #!/bin/bash
  • Use set -e or systematic error checking with $?
  • Organize into modular sections (Logging, Error handling, Deployment functions)
  • Define helper logging functions instead of inline echo statements
  • Load configuration from .env or config.sh

This script currently lacks a systematic error-handling strategy. Multiple critical operations (cast commands, API calls) proceed without checking exit status, so failures are silently ignored. Add set -e near the top (after the shebang) and refactor helper functions (e.g., log_error(), validate_file(), query_chain_id()) into reusable utilities.

Consider creating a shared config/utility file (e.g., script/lib/common.sh) with:

  • Logging functions: log_info(), log_error(), log_success()
  • Validation helpers: validate_file(), validate_rpc(), validate_address()
  • API/cast wrappers that handle errors consistently

Then source it at the top:

#!/bin/bash
set -e
source "$(dirname "$0")/../lib/common.sh"

67-73: Add error handling to balance queries for better user feedback.

Lines 68–69 query USDC balances without error checking. If the cast calls fail, users see no output for the balance section, which may cause confusion or apparent script failure.

Wrap balance queries with fallback values:

 echo "==================== Balances Before Transaction ===================="
-FROM_BALANCE_BEFORE=$(cast call "$FROM_TOKEN" "balanceOf(address)(uint256)" "$USER_ADDRESS" -r "$FROM_RPC_URL")
-TO_BALANCE_BEFORE=$(cast call "$TO_TOKEN" "balanceOf(address)(uint256)" "$USER_ADDRESS" -r "$TO_RPC_URL")
+FROM_BALANCE_BEFORE=$(cast call "$FROM_TOKEN" "balanceOf(address)(uint256)" "$USER_ADDRESS" -r "$FROM_RPC_URL" 2>&1) || FROM_BALANCE_BEFORE="(query failed)"
+TO_BALANCE_BEFORE=$(cast call "$TO_TOKEN" "balanceOf(address)(uint256)" "$USER_ADDRESS" -r "$TO_RPC_URL" 2>&1) || TO_BALANCE_BEFORE="(query failed)"

Alternatively, create a helper function to centralize this pattern.


95-102: Remove or conditionally enable debug output.

Line 101 (echo response gotten: $ROUTES_RESPONSE) appears to be debug output left in the code. This should either be removed or made conditional:

-echo response gotten: $ROUTES_RESPONSE
+[ "$DEBUG" = "1" ] && echo "DEBUG: Routes response: $ROUTES_RESPONSE"

For a cleaner approach, wrap detailed debug output in a debug helper function sourced from a shared utilities file.


142-151: Simplify transaction hash extraction using --async flag.

Line 146 uses grep | awk parsing, which is fragile and breaks if output format changes. The cast send command supports --async which prints just the tx hash directly, eliminating parsing complexity entirely.

Additionally, add hex validation to ensure the extracted hash is valid:

-TX_HASH=$(cast send "$TX_TO" \
+TX_HASH=$(cast send --async "$TX_TO" \
   --private-key "$PRIVATE_KEY" \
   --gas-limit "$TX_GAS_LIMIT" \
   --rpc-url "$FROM_RPC_URL" \
-  "$TX_DATA" | grep "transactionHash" | awk '{print $2}')
+  "$TX_DATA")
 
-if [ -z "$TX_HASH" ]; then
+if [ -z "$TX_HASH" ] || ! [[ "$TX_HASH" =~ ^0x[a-fA-F0-9]{64}$ ]]; then
   echo "Error: Transaction submission failed"
   exit 1
 fi

If you need the full transaction receipt data, use cast send -j instead and parse with jq: cast send -j ... | jq -r '.transactionHash'.

📜 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 c243c9a and 61959f4.

📒 Files selected for processing (1)
  • script/demoScripts/demoPolymer.sh (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
script/**/*.sh

📄 CodeRabbit inference engine (conventions.md)

script/**/*.sh: Bash scripts must start with #!/bin/bash, be modular with sections (Logging, Error handling and logging, Deployment functions), follow DRY via helpers, and organize core operations into functions
Bash style: consistent indentation/naming, comments, usage instructions, and TODO/limitations documented
Bash error handling and logging: use helper logging functions, validate inputs early, check exit status with checkFailure, and use set -e where appropriate

Files:

  • script/demoScripts/demoPolymer.sh
{script/**/*.sh,preinstall.sh}

📄 CodeRabbit inference engine (conventions.md)

Environment: load from .env or config.sh, declare globals in config, update .env.example, validate env vars early; add system packages to preinstall.sh

Files:

  • script/demoScripts/demoPolymer.sh
🪛 Shellcheck (0.11.0)
script/demoScripts/demoPolymer.sh

[warning] 133-133: TX_CHAIN_ID appears unused. Verify use (or export if used externally).

(SC2034)

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

♻️ Duplicate comments (4)
src/Interfaces/IPolymerCCTP.sol (3)

1-2: Duplicate: Wrong SPDX license identifier

This issue was already flagged in previous reviews. The license must be LGPL-3.0-only per project conventions.


6-11: Duplicate: PolymerCCTPData struct layout violates facet conventions

This issue was comprehensively addressed in previous reviews. The struct must have receiverAddress (type bytes) as the first field per facet data guidelines, and should include CCTP-specific fields like destinationDomain.


13-25: Duplicate: Missing interface NatSpec and formatting issues

Previous reviews comprehensively flagged the missing NatSpec header for the interface, the extra space in error signatures (line 17), and the extra space in the event signature (line 20). Additionally, the function at lines 22-24 requires NatSpec documentation per coding guidelines.

src/Facets/PolymerCCTPFacet.sol (1)

67-86: CRITICAL: Approve and burn amounts must use net amount after fee deduction.

The deposit on line 68 correctly pulls only (minAmount - polymerTokenFee), but:

  • Line 73 approves minAmount to tokenMessenger (should approve minAmount - polymerTokenFee)
  • Line 77 attempts to burn minAmount (should burn minAmount - polymerTokenFee)

This mismatch will cause the transaction to fail because the contract only holds the net amount after the fee was transferred out.

Additionally:

  • Line 78: Should use _polymerData.destinationDomain instead of casting _bridgeData.destinationChainId to uint32, as CCTP domains don't map 1:1 to EVM chain IDs.
  • Lines 79-81: Should validate that _polymerData.nonEvmAddress is not empty when _bridgeData.receiver == NON_EVM_ADDRESS.

Apply this diff:

         LibAsset.depositAsset(_bridgeData.sendingAssetId, _bridgeData.minAmount -  _polymerData.polymerTokenFee );
         LibAsset.transferFromERC20( usdc,  msg.sender, polymerFeeReceiver, _polymerData.polymerTokenFee );

+        uint256 amountToBurn = _bridgeData.minAmount - _polymerData.polymerTokenFee;

-        // TODO we don't need to use safe approve here?
-        IERC20(usdc).approve(address(tokenMessenger), _bridgeData.minAmount);
+        IERC20(usdc).approve(address(tokenMessenger), amountToBurn);

+        if (_bridgeData.receiver == NON_EVM_ADDRESS && _polymerData.nonEvmAddress.length == 0) {
+            revert InvalidBridgeReceiver();
+        }
+
-        // Need tocheck: can we just use destinationChainID as the normal chain id? and can we just mpass in min Amount as the amountT?
         tokenMessenger.depositForBurn(
-            _bridgeData.minAmount,
-            uint32(_bridgeData.destinationChainId),
+            amountToBurn,
+            _polymerData.destinationDomain,
             _bridgeData.receiver == NON_EVM_ADDRESS
                 ? _polymerData.nonEvmAddress
                 : bytes32(uint256(uint160(_bridgeData.receiver))),
📜 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 61959f4 and 5d28394.

📒 Files selected for processing (4)
  • script/demoScripts/demoPolymer.sh (1 hunks)
  • src/Facets/PolymerCCTPFacet.sol (1 hunks)
  • src/Interfaces/IPolymerCCTP.sol (1 hunks)
  • testnet-addresses.json (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • testnet-addresses.json
  • script/demoScripts/demoPolymer.sh
🧰 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/Facets/PolymerCCTPFacet.sol
  • src/Interfaces/IPolymerCCTP.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/Facets/PolymerCCTPFacet.sol
  • src/Interfaces/IPolymerCCTP.sol
src/Facets/**/*Facet.sol

📄 CodeRabbit inference engine (conventions.md)

src/Facets/**/*Facet.sol: Facet contracts must be located in src/Facets and include "Facet" in the contract name
Facet contracts must implement: internal _startBridge, swapAndStartBridgeTokensVia{FacetName}, and startBridgeTokensVia{FacetName}
Facet contracts must import/use: ILiFi, LibAsset, LibSwap, LibAllowList; and use security mixins ReentrancyGuard, SwapperV2, Validatable; ECDSA optional
Do not use msg.sender as refund address; require an explicit parameter for refund address
In {facetName}Data, receiverAddress must be the first field and be validated against bridgeData.receiver; verify targetChainId equals bridgeData.destinationChain for EVM-to-EVM
Apply required modifiers where appropriate: nonReentrant, refundExcessNative, validateBridgeData, doesNotContainSourceSwaps/doesContainSourceSwaps, doesNotContainDestinationCalls/doesContainDestinationCalls
Emit LiFiTransferStarted at the start of a transaction before external calls; do not emit LiFiTransferCompleted or LiFiTransferRecovered in facets
For native fees in facets, use the _depositAndSwap variant and reserve required fees before execution
For non-EVM chains: receiverAddress must be bytes (not bytes32), must not be zero, and bridgeData.receiver must equal NON_EVM_ADDRESS

Files:

  • src/Facets/PolymerCCTPFacet.sol
**/*.sol

📄 CodeRabbit inference engine (conventions.md)

Follow solhint rules as configured in .solhint.json

Files:

  • src/Facets/PolymerCCTPFacet.sol
  • src/Interfaces/IPolymerCCTP.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/IPolymerCCTP.sol
🧠 Learnings (69)
📓 Common learnings
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 782
File: script/deploy/_targetState.json:143-143
Timestamp: 2024-11-25T06:25:01.232Z
Learning: Contracts like `OpBNBBridgeFacet` may be in development and only available in feature branches, resulting in missing source files in the main branch. Before flagging such missing contracts, check for open PRs that might include them.
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : Apply required modifiers where appropriate: nonReentrant, refundExcessNative, validateBridgeData, doesNotContainSourceSwaps/doesContainSourceSwaps, doesNotContainDestinationCalls/doesContainDestinationCalls
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 782
File: script/deploy/_targetState.json:49-49
Timestamp: 2024-11-25T09:05:03.917Z
Learning: The `RelayFacet` contract, when missing from the source code but referenced in deployment configurations, should be treated the same way as `OpBNBBridgeFacet` and can be ignored in code reviews.
Learnt from: ezynda3
Repo: lifinance/contracts PR: 861
File: script/deploy/_targetState.json:1364-1390
Timestamp: 2024-11-21T08:25:26.214Z
Learning: For the Cronos network configuration in `script/deploy/_targetState.json`, the absence of bridge facets such as `StargateFacet`, `AcrossFacet`, `HopFacet`, and `SymbiosisFacet` is acceptable and expected.
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1388
File: deployments/hyperevm.diamond.json:72-75
Timestamp: 2025-09-22T00:52:26.172Z
Learning: In diamond configuration files (deployments/*.diamond.json), it's acceptable to have multiple versions of the same facet (e.g., GlacisFacet v1.0.0 and v1.1.0) deployed at different contract addresses. This is intentional design for version coexistence, gradual migration, or backward compatibility purposes.
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 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:

  • src/Facets/PolymerCCTPFacet.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : Apply required modifiers where appropriate: nonReentrant, refundExcessNative, validateBridgeData, doesNotContainSourceSwaps/doesContainSourceSwaps, doesNotContainDestinationCalls/doesContainDestinationCalls

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : In {facetName}Data, receiverAddress must be the first field and be validated against bridgeData.receiver; verify targetChainId equals bridgeData.destinationChain for EVM-to-EVM

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-07-16T01:03:08.106Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 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/Facets/PolymerCCTPFacet.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2024-10-31T09:09:38.568Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 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:

  • src/Facets/PolymerCCTPFacet.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-10-02T18:10:09.934Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1406
File: src/Facets/UnitFacet.sol:75-81
Timestamp: 2025-10-02T18:10:09.934Z
Learning: In UnitFacet.sol and similar facet contracts in src/Facets/, the LiFiTransferStarted event emission should ALWAYS be at the end of the _startBridge internal function, after all validations and asset transfers have been completed successfully.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 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:

  • src/Facets/PolymerCCTPFacet.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : Emit LiFiTransferStarted at the start of a transaction before external calls; do not emit LiFiTransferCompleted or LiFiTransferRecovered in facets

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-07-16T01:04:55.857Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1275
File: src/Facets/AllBridgeFacet.sol:186-188
Timestamp: 2025-07-16T01:04:55.857Z
Learning: In LiFi facet contracts, when public entry point functions have `nonReentrant` modifier protection, internal functions like `_startBridge` that they call benefit from this reentrancy protection, making event emission order after external calls acceptable from a security perspective.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2024-11-25T09:04:55.880Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 782
File: script/deploy/_targetState.json:143-143
Timestamp: 2024-11-25T09:04:55.880Z
Learning: Errors about the missing `OpBNBBridgeFacet` contract are expected when it is referenced in the target state but not yet merged into the main branch.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-29T11:18:56.656Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Periphery/LDA/Facets/NativeWrapperFacet.sol:1-2
Timestamp: 2025-08-29T11:18:56.656Z
Learning: For src/Periphery/LDA/Facets/NativeWrapperFacet.sol, the SPDX license identifier should be immediately followed by the pragma statement without a blank line in between, as confirmed by mirooon.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-10-02T18:14:51.385Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1406
File: src/Facets/UnitFacet.sol:1-2
Timestamp: 2025-10-02T18:14:51.385Z
Learning: For facet contracts in src/Facets/, the SPDX license identifier should be immediately followed by the pragma statement without a blank line in between, as confirmed by mirooon for src/Facets/UnitFacet.sol.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-10-02T18:14:45.047Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1406
File: script/deploy/facets/UpdateUnitFacet.s.sol:1-3
Timestamp: 2025-10-02T18:14:45.047Z
Learning: For update scripts in script/deploy/facets/, the SPDX license identifier should be immediately followed by the pragma statement without a blank line in between, as confirmed by mirooon for script/deploy/facets/UpdateUnitFacet.s.sol.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 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:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-27T23:01:41.042Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Libraries/LibUniV3Logic.sol:4-5
Timestamp: 2025-08-27T23:01:41.042Z
Learning: In the lifinance/contracts repository, the pattern `import { IERC20, SafeERC20 } from "openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";` is correctly used throughout the codebase and works as expected. SafeERC20.sol re-exports IERC20 in modern OpenZeppelin versions, making this combined import valid. Do not suggest splitting these imports.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : Facet contracts must be located in src/Facets and include "Facet" in the contract name

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-29T14:06:33.384Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: script/deploy/facets/LDA/UpdateUniV2StyleFacet.s.sol:1-2
Timestamp: 2025-08-29T14:06:33.384Z
Learning: For LDA deployment and update scripts in script/deploy/facets/LDA/, the SPDX license identifier should be immediately followed by the pragma statement without a blank line in between, as confirmed by mirooon for script/deploy/facets/LDA/UpdateUniV2StyleFacet.s.sol.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-08-29T14:05:37.002Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: script/deploy/facets/LDA/DeployLDAOwnershipFacet.s.sol:1-3
Timestamp: 2025-08-29T14:05:37.002Z
Learning: For deployment script files in script/deploy/facets/LDA/, the SPDX license identifier should be immediately followed by the pragma statement without a blank line in between, as confirmed by mirooon for script/deploy/facets/LDA/DeployLDAOwnershipFacet.s.sol.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2024-11-25T06:25:01.232Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 782
File: script/deploy/_targetState.json:143-143
Timestamp: 2024-11-25T06:25:01.232Z
Learning: Contracts like `OpBNBBridgeFacet` may be in development and only available in feature branches, resulting in missing source files in the main branch. Before flagging such missing contracts, check for open PRs that might include them.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2024-11-25T09:05:03.917Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 782
File: script/deploy/_targetState.json:49-49
Timestamp: 2024-11-25T09:05:03.917Z
Learning: The `RelayFacet` contract, when missing from the source code but referenced in deployment configurations, should be treated the same way as `OpBNBBridgeFacet` and can be ignored in code reviews.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : For native fees in facets, use the _depositAndSwap variant and reserve required fees before execution

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-27T23:36:40.773Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 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/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-07-16T01:04:11.083Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1275
File: src/Facets/AllBridgeFacet.sol:175-175
Timestamp: 2025-07-16T01:04:11.083Z
Learning: In AllBridgeFacet.sol, zero fee validation is not required at the contract level because the backend system ensures that calldata and msg.value are properly aligned before transactions reach the contract.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2024-10-22T03:14:52.578Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 807
File: src/Facets/GasZipFacet.sol:53-53
Timestamp: 2024-10-22T03:14:52.578Z
Learning: In `src/Facets/GasZipFacet.sol`, when checking `msg.value`, the team prefers to use strict equality (`msg.value == _bridgeData.minAmount`) to save gas and avoid refunding excess Ether.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-29T11:53:38.549Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 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:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : For non-EVM chains: receiverAddress must be bytes (not bytes32), must not be zero, and bridgeData.receiver must equal NON_EVM_ADDRESS

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-02-24T09:35:34.908Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 984
File: src/Periphery/ReceiverChainflip.sol:48-62
Timestamp: 2025-02-24T09:35:34.908Z
Learning: Contract address validations for periphery contracts like ReceiverChainflip are handled in deployment scripts rather than in the constructor.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 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:

  • src/Facets/PolymerCCTPFacet.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-02-21T09:00:28.226Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 984
File: src/Facets/ChainflipFacet.sol:127-146
Timestamp: 2025-02-21T09:00:28.226Z
Learning: The `validateBridgeData` modifier in `Validatable.sol` already validates that the receiver address is not zero, making additional zero-address checks redundant in functions using this modifier.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 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:

  • src/Facets/PolymerCCTPFacet.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 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:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-27T22:29:51.839Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: test/solidity/Periphery/Lda/Facets/SyncSwapV2Facet.t.sol:1-2
Timestamp: 2025-08-27T22:29:51.839Z
Learning: Test files in test/solidity/**/*.t.sol should use the LGPL-3.0-only SPDX license identifier, not Unlicense.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to {src,test/solidity,script}/**/*.sol : Use SPDX license identifier // SPDX-License-Identifier: LGPL-3.0-only for all first-party Solidity files

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-08-29T11:33:37.368Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: test/solidity/Periphery/LDA/BaseCoreRouteTest.t.sol:1-3
Timestamp: 2025-08-29T11:33:37.368Z
Learning: For test files in test/solidity/Periphery/LDA/**, the SPDX license identifier should be immediately followed by the pragma statement without a blank line in between, as confirmed by mirooon for test/solidity/Periphery/LDA/BaseCoreRouteTest.t.sol.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-08-29T14:06:01.072Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: script/deploy/facets/LDA/UpdateCurveFacet.s.sol:1-2
Timestamp: 2025-08-29T14:06:01.072Z
Learning: For script/deploy/facets/LDA/UpdateCurveFacet.s.sol and similar LDA update scripts, the SPDX license identifier should be immediately followed by the pragma statement without requiring a blank line in between, as confirmed by mirooon.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-29T11:11:35.958Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Periphery/LDA/Facets/SyncSwapV2Facet.sol:1-3
Timestamp: 2025-08-29T11:11:35.958Z
Learning: For LDA facet files in src/Periphery/LDA/Facets/, the SPDX license identifier can be immediately followed by the pragma statement without requiring a blank line in between, as confirmed by mirooon.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-27T23:01:41.042Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Libraries/LibUniV3Logic.sol:4-5
Timestamp: 2025-08-27T23:01:41.042Z
Learning: In OpenZeppelin v4.0+, SafeERC20.sol re-exports IERC20, so importing both IERC20 and SafeERC20 from "openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol" is valid and works correctly. Do not suggest splitting this import into separate files.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-27T23:01:41.042Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Libraries/LibUniV3Logic.sol:4-5
Timestamp: 2025-08-27T23:01:41.042Z
Learning: In OpenZeppelin v4.0+, SafeERC20.sol re-exports IERC20, so importing both IERC20 and SafeERC20 from "openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol" is valid and works correctly. Do not suggest splitting this import.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-27T23:01:41.042Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Libraries/LibUniV3Logic.sol:4-5
Timestamp: 2025-08-27T23:01:41.042Z
Learning: In modern OpenZeppelin versions (v4.0+), SafeERC20.sol re-exports IERC20, making the combined import `import { IERC20, SafeERC20 } from "openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";` completely valid and working. Do not suggest splitting these imports as they work correctly as a combined import.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-19T05:07:19.448Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 1303
File: script/deploy/tron/utils.ts:3-5
Timestamp: 2025-08-19T05:07:19.448Z
Learning: In the lifinance/contracts repository, consola and TronWeb should be imported as named exports: `import { consola } from 'consola'` and `import { TronWeb } from 'tronweb'`. Do not flag these as incorrect default export usage.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-19T05:07:19.448Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 1303
File: script/deploy/tron/utils.ts:3-5
Timestamp: 2025-08-19T05:07:19.448Z
Learning: In the lifinance/contracts repository, consola and TronWeb should be imported as named exports: `import { consola } from 'consola'` and `import { TronWeb } from 'tronweb'`. Do not flag these as incorrect default export usage. Both libraries provide named exports, not default exports.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-27T13:07:58.254Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Periphery/Lda/Facets/CurveFacet.sol:0-0
Timestamp: 2025-08-27T13:07:58.254Z
Learning: In the LiFi contracts codebase, ICurveLegacy interface is defined inline in src/Periphery/LiFiDEXAggregator.sol with a non-payable exchange function. The CurveFacet diff shows importing it from a separate file that doesn't exist, creating a compilation issue.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2024-09-23T02:04:16.323Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 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/Facets/PolymerCCTPFacet.sol
📚 Learning: 2024-10-09T03:47:21.269Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 807
File: src/Periphery/GasZipPeriphery.sol:4-14
Timestamp: 2024-10-09T03:47:21.269Z
Learning: In `GasZipPeriphery.sol`, `LibUtil` and `Validatable` are used, so ensure not to suggest their removal in future reviews.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to {src,test/solidity,script}/**/*.sol : 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

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-09-19T14:10:55.064Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 1324
File: src/Facets/EcoFacet.sol:68-71
Timestamp: 2025-09-19T14:10:55.064Z
Learning: In src/Facets/EcoFacet.sol, the team prefers to use msg.sender as the refund address for native tokens in the refundExcessNative modifier, contrary to the general guideline of requiring explicit refund parameters.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to {src,test/solidity,script}/**/*.sol : Error names must be descriptive PascalCase, use custom errors instead of revert() strings, and omit error messages for gas optimization

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-19T05:10:12.922Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 1303
File: script/utils/network.ts:27-32
Timestamp: 2025-08-19T05:10:12.922Z
Learning: In the lifinance/contracts repository, the team decided to standardize RPC environment variable naming by converting all hyphens to underscores in network names. For example, "tron-shasta" becomes "ETH_NODE_URI_TRON_SHASTA". The getRPCEnvVarName() helper in script/utils/network.ts implements this standard and should be used consistently across the codebase instead of direct string manipulation.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : Do not use msg.sender as refund address; require an explicit parameter for refund address

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 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:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-09-25T00:12:58.536Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1395
File: src/Periphery/FeeForwarder.sol:78-120
Timestamp: 2025-09-25T00:12:58.536Z
Learning: In src/Periphery/FeeForwarder.sol, the team intentionally uses address(this).balance for refunding remaining native tokens because the contract is designed to never hold funds and not collect dust, making it safe to return the entire balance to the caller.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-10-13T11:13:48.847Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1413
File: src/Facets/EverclearV2Facet.sol:75-96
Timestamp: 2025-10-13T11:13:48.847Z
Learning: In EverclearV2Facet (and all LiFi facets), the team standard is to use msg.sender as the refund address with the refundExcessNative modifier, not requiring an explicit refund address parameter.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-01-28T14:29:00.823Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 924
File: script/deploy/zksync/utils/UpdateScriptBase.sol:112-178
Timestamp: 2025-01-28T14:29:00.823Z
Learning: The suggestion to modify `buildDiamondCut` function in `UpdateScriptBase.sol` to handle selectors from multiple old facets differently was deemed unnecessary by the maintainer.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-29T11:07:57.743Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Periphery/LDA/Facets/CurveFacet.sol:87-91
Timestamp: 2025-08-29T11:07:57.743Z
Learning: In src/Periphery/LDA/Facets/CurveFacet.sol, modern Curve pools (isV2=true, representing V2/NG pools) should reject native tokenIn by adding an early revert check when LibAsset.isNativeAsset(tokenIn) is true, since ICurveV2 exchange functions are non-payable and cannot accept native ETH.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2024-12-02T06:33:33.309Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 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/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-02-21T09:00:28.226Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 984
File: src/Facets/ChainflipFacet.sol:127-146
Timestamp: 2025-02-21T09:00:28.226Z
Learning: The `validateBridgeData` modifier in `Validatable.sol` validates that the receiver address is not zero using `LibUtil.isZeroAddress`, making additional zero-address checks redundant in functions using this modifier.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-20T09:56:24.259Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1327
File: docs/OutputValidator.md:23-29
Timestamp: 2025-08-20T09:56:24.259Z
Learning: In src/Periphery/OutputValidator.sol, the ERC20 token flow that uses balanceOf(msg.sender) to compute actualAmount and transfers the excess via safeTransferFrom is intentional behavior, as confirmed by 0xDEnYO. The design choice to potentially include pre-existing ERC20 balances in the excess calculation is by design and should not be flagged as a security concern in future reviews.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2024-10-14T08:23:38.076Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 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/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-27T13:07:58.254Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Periphery/Lda/Facets/CurveFacet.sol:0-0
Timestamp: 2025-08-27T13:07:58.254Z
Learning: The CurveFacet in src/Periphery/Lda/Facets/CurveFacet.sol has fundamental issues: it imports a non-existent ICurveLegacy interface, and attempts to send ETH to non-payable exchange functions in both ICurve and ICurveV2 interfaces. All current Curve interfaces are non-payable and cannot accept native ETH.

Applied to files:

  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-08-29T11:04:14.413Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Periphery/LDA/Errors/Errors.sol:1-7
Timestamp: 2025-08-29T11:04:14.413Z
Learning: For error files in src/Periphery/LDA/Errors/Errors.sol and similar error definition files, the file header format should keep NatSpec comments before the pragma statement due to solhint compiler requirements, rather than following the standard convention of SPDX → blank line → pragma → NatSpec.

Applied to files:

  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Interfaces/**/*.sol : Every interface must include NatSpec in this order: title, notice, author LI.FI (https://li.fi), custom:version X.Y.Z

Applied to files:

  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/**/*.sol : Every contract must include NatSpec header in this order: title, author LI.FI (https://li.fi), notice, custom:version X.Y.Z

Applied to files:

  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-09-24T23:55:07.082Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1395
File: script/deploy/facets/DeployFeeForwarder.s.sol:1-1
Timestamp: 2025-09-24T23:55:07.082Z
Learning: All Solidity files in the lifinance/contracts repository, including deployment scripts in script/deploy/, must use the SPDX license identifier "// SPDX-License-Identifier: LGPL-3.0-only" as specified in conventions.md. This applies to all .sol files in src/, test/solidity/, and script/ directories.

Applied to files:

  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-09-25T00:05:33.977Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1395
File: src/Periphery/FeeForwarder.sol:1-2
Timestamp: 2025-09-25T00:05:33.977Z
Learning: In the lifinance/contracts repository, the established convention is that SPDX license identifiers should be immediately followed by the pragma statement without a blank line in between. This applies to all Solidity files across src/, test/, and script/ directories, as confirmed by 0xDEnYO and verified by codebase analysis.

Applied to files:

  • src/Interfaces/IPolymerCCTP.sol
📚 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:

  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-02-14T09:37:32.647Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 984
File: src/Facets/ChainflipFacet.sol:50-56
Timestamp: 2025-02-14T09:37:32.647Z
Learning: ChainflipData struct in ChainflipFacet.sol does not require additional field validation as confirmed by the team.

Applied to files:

  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/**/*.sol : All public/external functions must have NatSpec comments documenting purpose, params, and returns

Applied to files:

  • src/Interfaces/IPolymerCCTP.sol
📚 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:

  • src/Interfaces/IPolymerCCTP.sol
🔇 Additional comments (2)
src/Facets/PolymerCCTPFacet.sol (2)

41-50: LGTM: Function modifiers are correctly applied.

The function signature and modifiers follow facet conventions properly: nonReentrant, refundExcessNative, validateBridgeData, onlyAllowSourceToken, doesNotContainSourceSwaps, and doesNotContainDestinationCalls are all appropriately applied.


51-66: Fix inverted fee validation logic and remove redundant checks.

Lines 63-65: The comparison logic is inverted. Currently it reverts when polymerTokenFee >= minAmount, but it should revert when polymerTokenFee > minAmount (fee should be allowed to equal minAmount for edge cases). Additionally, the error name FeeCannotBeLessThanAmount is confusing given the check.

Lines 59-61: This validation is redundant because the onlyAllowSourceToken(_bridgeData, usdc) modifier on line 47 already enforces this constraint.

Apply this diff:

-        if(_bridgeData.sendingAssetId != usdc){
-            revert InvalidSendingAsset(_bridgeData.sendingAssetId , usdc);
-        }
-
-        if(_polymerData.polymerTokenFee >= _bridgeData.minAmount){
-            revert FeeCannotBeLessThanAmount();
+        if(_polymerData.polymerTokenFee > _bridgeData.minAmount){
+            revert FeeTooHigh();
         }
⛔ Skipped due to learnings
Learnt from: ezynda3
Repo: lifinance/contracts PR: 984
File: src/Facets/ChainflipFacet.sol:127-146
Timestamp: 2025-02-21T09:00:28.226Z
Learning: The `validateBridgeData` modifier in `Validatable.sol` already validates that the receiver address is not zero, making additional zero-address checks redundant in functions using this modifier.
Learnt from: ezynda3
Repo: lifinance/contracts PR: 984
File: src/Facets/ChainflipFacet.sol:127-146
Timestamp: 2025-02-21T09:00:28.226Z
Learning: The `validateBridgeData` modifier in `Validatable.sol` validates that the receiver address is not zero using `LibUtil.isZeroAddress`, making additional zero-address checks redundant in functions using this modifier.
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1275
File: src/Facets/AllBridgeFacet.sol:175-175
Timestamp: 2025-07-16T01:04:11.083Z
Learning: In AllBridgeFacet.sol, zero fee validation is not required at the contract level because the backend system ensures that calldata and msg.value are properly aligned before transactions reach the contract.
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : Apply required modifiers where appropriate: nonReentrant, refundExcessNative, validateBridgeData, doesNotContainSourceSwaps/doesContainSourceSwaps, doesNotContainDestinationCalls/doesContainDestinationCalls
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 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.
Learnt from: ezynda3
Repo: lifinance/contracts PR: 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`.
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 807
File: src/Facets/GasZipFacet.sol:53-53
Timestamp: 2024-10-22T03:14:52.578Z
Learning: In `src/Facets/GasZipFacet.sol`, when checking `msg.value`, the team prefers to use strict equality (`msg.value == _bridgeData.minAmount`) to save gas and avoid refunding excess Ether.
Learnt from: mirooon
Repo: lifinance/contracts PR: 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.
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1327
File: docs/OutputValidator.md:23-29
Timestamp: 2025-08-20T09:56:24.259Z
Learning: In src/Periphery/OutputValidator.sol, the ERC20 token flow that uses balanceOf(msg.sender) to compute actualAmount and transfers the excess via safeTransferFrom is intentional behavior, as confirmed by 0xDEnYO. The design choice to potentially include pre-existing ERC20 balances in the excess calculation is by design and should not be flagged as a security concern in future reviews.
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 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.

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: 3

♻️ Duplicate comments (9)
src/Facets/PolymerCCTPFacet.sol (7)

1-2: Use LGPL-3.0-only license identifier.

The SPDX license identifier must be LGPL-3.0-only for all first-party Solidity files.

As per coding guidelines

Apply this diff:

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

4-14: Remove duplicate imports.

IERC20 is imported twice (lines 4 and 11) and PolymerCCTPData is imported twice (lines 6 and 9).

As per coding guidelines

Apply this diff:

-import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
 import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
-import {PolymerCCTPData} from "../Interfaces/IPolymerCCTP.sol";
 import {ILiFi} from "../Interfaces/ILiFi.sol";
 import {ITokenMessenger} from "../Interfaces/ITokenMessenger.sol";
 import {IPolymerCCTPFacet, PolymerCCTPData} from "../Interfaces/IPolymerCCTP.sol";
 import {LiFiData} from "../Helpers/LiFiData.sol";
-import {LibAsset, IERC20} from "../Libraries/LibAsset.sol";
+import {LibAsset} from "../Libraries/LibAsset.sol";
+import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
 import {LibSwap} from "../Libraries/LibSwap.sol";

21-23: Rename immutables to CONSTANT_CASE.

Immutable state variables must use CONSTANT_CASE naming convention.

As per coding guidelines

Apply this diff:

-    ITokenMessenger public immutable tokenMessenger;
-    address public immutable usdc;
-    address payable public immutable polymerFeeReceiver;
+    ITokenMessenger public immutable TOKEN_MESSENGER;
+    address public immutable USDC;
+    address payable public immutable POLYMER_FEE_RECEIVER;

Then update all references throughout the contract (constructor, function body, etc.).


25-35: Update constructor to use renamed immutables and remove TODO.

Constructor assignments must use the renamed CONSTANT_CASE immutable variables.

As per coding guidelines

Apply this diff:

     constructor(address _tokenMessenger, address _usdc, address _polymerFeeReceiver) {
-        // TODO: Do we want to have fee collector here?
-
         if (_tokenMessenger == address(0) || _usdc == address(0) || _polymerFeeReceiver == address(0)) {
             revert InvalidAddress();
         }
 
-        tokenMessenger = ITokenMessenger(_tokenMessenger);
-        usdc = _usdc;
-        polymerFeeReceiver = payable(_polymerFeeReceiver);
+        TOKEN_MESSENGER = ITokenMessenger(_tokenMessenger);
+        USDC = _usdc;
+        POLYMER_FEE_RECEIVER = payable(_polymerFeeReceiver);
     }

41-50: Implement required facet functions.

Facet contracts must implement an internal _startBridge function and a public swapAndStartBridgeTokensViaPolymerCCTP function per coding guidelines.

As per coding guidelines

The _startBridge function should contain the core bridging logic (lines 51-106), and startBridgeTokensViaPolymerCCTP should call it. The swapAndStartBridgeTokensViaPolymerCCTP function should handle source swaps before calling _startBridge.


76-86: Use destinationDomain from polymerData instead of casting destinationChainId.

Line 78 casts destinationChainId to uint32 for the CCTP domain, but CCTP domain IDs are not the same as chain IDs. The destination domain should come from _polymerData.destinationDomain.

Apply this diff:

         tokenMessenger.depositForBurn(
             _bridgeData.minAmount -  _polymerData.polymerTokenFee ,
-            uint32(_bridgeData.destinationChainId),
+            _polymerData.destinationDomain,
             _bridgeData.receiver == NON_EVM_ADDRESS
                 ? _polymerData.nonEvmAddress
                 : bytes32(uint256(uint160(_bridgeData.receiver))),

Also validate non-EVM receiver address before the external call:

+        if (_bridgeData.receiver == NON_EVM_ADDRESS && _polymerData.nonEvmAddress.length == 0) {
+            revert InvalidBridgeReceiver();
+        }
+
         tokenMessenger.depositForBurn(

88-105: Emit events before external calls.

Events are emitted after the tokenMessenger.depositForBurn external call (lines 76-86). Per coding guidelines, LiFiTransferStarted must be emitted before external calls.

As per coding guidelines

Move both event emissions to before the depositForBurn call:

+        emit PolymerCCTPFeeSent(_bridgeData.minAmount, _polymerData.polymerTokenFee, _polymerData.minFinalityThreshold);
+        emit LiFiTransferStarted(
+            BridgeData(
+                _bridgeData.transactionId,
+                _bridgeData.bridge,
+                _bridgeData.integrator,
+                _bridgeData.referrer,
+                _bridgeData.sendingAssetId,
+                _bridgeData.receiver,
+                _bridgeData.minAmount - _polymerData.polymerTokenFee,
+                _bridgeData.destinationChainId,
+                _bridgeData.hasSourceSwaps,
+                _bridgeData.hasDestinationCall
+            )
+        );
+
         tokenMessenger.depositForBurn(
             _bridgeData.minAmount -  _polymerData.polymerTokenFee ,
             uint32(_bridgeData.destinationChainId),
             _bridgeData.receiver == NON_EVM_ADDRESS
                 ? _polymerData.nonEvmAddress
                 : bytes32(uint256(uint160(_bridgeData.receiver))),
             usdc,
             bytes32(0),
             _polymerData.maxCCTPFee,
             _polymerData.minFinalityThreshold
         );
-
-        emit PolymerCCTPFeeSent( _bridgeData.minAmount, _polymerData.polymerTokenFee, _polymerData.minFinalityThreshold);
-
-        // Emit Li.Fi standard event
-        // TODO: Check - do we need to emit this event? 
-        emit LiFiTransferStarted(
-            BridgeData(
-                _bridgeData.transactionId,
-                _bridgeData.bridge,
-                _bridgeData.integrator,
-                _bridgeData.referrer,
-                _bridgeData.sendingAssetId,
-                _bridgeData.receiver,
-                _bridgeData.minAmount - _polymerData.polymerTokenFee,
-                _bridgeData.destinationChainId,
-                _bridgeData.hasSourceSwaps,
-                _bridgeData.hasDestinationCall
-            )
-        );
script/demoScripts/demoPolymer.sh (2)

25-40: Add error handling for cast chain-id commands.

Lines 25-26 execute cast chain-id without checking exit status. If cast fails, the variables may contain error messages that pass the non-empty check at lines 29-37, causing confusing errors later when jq tries to parse them.

Apply this pattern:

 echo "Querying chain IDs from RPC URLs..."
-FROM_CHAIN_ID=$(cast chain-id -r "$FROM_RPC_URL")
-TO_CHAIN_ID=$(cast chain-id -r "$TO_RPC_URL")
+FROM_CHAIN_ID=$(cast chain-id -r "$FROM_RPC_URL" 2>&1) || {
+  echo "Error: Failed to query chain ID from $FROM_RPC_URL"
+  exit 1
+}
+TO_CHAIN_ID=$(cast chain-id -r "$TO_RPC_URL" 2>&1) || {
+  echo "Error: Failed to query chain ID from $TO_RPC_URL"
+  exit 1
+}

58-61: Add error handling for wallet address derivation.

Line 59 derives the wallet address without checking if the command succeeded. An invalid private key will silently produce an empty USER_ADDRESS, causing downstream failures.

Apply this pattern:

-USER_ADDRESS=$(cast wallet address "$PRIVATE_KEY")
+USER_ADDRESS=$(cast wallet address "$PRIVATE_KEY" 2>&1) || {
+  echo "Error: Failed to derive wallet address from private key"
+  exit 1
+}
+[ -z "$USER_ADDRESS" ] && {
+  echo "Error: Wallet address is empty"
+  exit 1
+}
🧹 Nitpick comments (2)
src/Facets/PolymerCCTPFacet.sol (1)

52-61: Remove redundant validations covered by modifiers.

Lines 52-61 perform validations that are already enforced by the function modifiers:

  • Line 52-54: minAmount == 0 check is redundant with validateBridgeData modifier
  • Line 55-57: Zero receiver check is redundant with validateBridgeData modifier
  • Line 59-61: sendingAssetId != usdc check is redundant with onlyAllowSourceToken modifier

These explicit checks provide clearer error messages but add gas cost. Consider removing them if you prefer to rely on the modifiers, or keep them for better error reporting.

script/demoScripts/demoPolymer.sh (1)

138-145: Remove or validate unused variables.

Lines 141 and 145 extract TX_CHAIN_ID and POLYMER_TX_DATA but never use them. Static analysis flags these as unused.

Either:

  1. Validate TX_CHAIN_ID matches FROM_CHAIN_ID for safety:
    if [ "$TX_CHAIN_ID" != "$FROM_CHAIN_ID" ]; then
      echo "Error: API returned chainId $TX_CHAIN_ID, expected $FROM_CHAIN_ID"
      exit 1
    fi
  2. Remove both lines if they're truly not needed.
📜 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 5d28394 and 7872e29.

📒 Files selected for processing (3)
  • script/demoScripts/demoPolymer.sh (1 hunks)
  • src/Facets/PolymerCCTPFacet.sol (1 hunks)
  • testnet-addresses.json (1 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
script/**/*.sh

📄 CodeRabbit inference engine (conventions.md)

script/**/*.sh: Bash scripts must start with #!/bin/bash, be modular with sections (Logging, Error handling and logging, Deployment functions), follow DRY via helpers, and organize core operations into functions
Bash style: consistent indentation/naming, comments, usage instructions, and TODO/limitations documented
Bash error handling and logging: use helper logging functions, validate inputs early, check exit status with checkFailure, and use set -e where appropriate

Files:

  • script/demoScripts/demoPolymer.sh
{script/**/*.sh,preinstall.sh}

📄 CodeRabbit inference engine (conventions.md)

Environment: load from .env or config.sh, declare globals in config, update .env.example, validate env vars early; add system packages to preinstall.sh

Files:

  • script/demoScripts/demoPolymer.sh
{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/Facets/PolymerCCTPFacet.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/Facets/PolymerCCTPFacet.sol
src/Facets/**/*Facet.sol

📄 CodeRabbit inference engine (conventions.md)

src/Facets/**/*Facet.sol: Facet contracts must be located in src/Facets and include "Facet" in the contract name
Facet contracts must implement: internal _startBridge, swapAndStartBridgeTokensVia{FacetName}, and startBridgeTokensVia{FacetName}
Facet contracts must import/use: ILiFi, LibAsset, LibSwap, LibAllowList; and use security mixins ReentrancyGuard, SwapperV2, Validatable; ECDSA optional
Do not use msg.sender as refund address; require an explicit parameter for refund address
In {facetName}Data, receiverAddress must be the first field and be validated against bridgeData.receiver; verify targetChainId equals bridgeData.destinationChain for EVM-to-EVM
Apply required modifiers where appropriate: nonReentrant, refundExcessNative, validateBridgeData, doesNotContainSourceSwaps/doesContainSourceSwaps, doesNotContainDestinationCalls/doesContainDestinationCalls
Emit LiFiTransferStarted at the start of a transaction before external calls; do not emit LiFiTransferCompleted or LiFiTransferRecovered in facets
For native fees in facets, use the _depositAndSwap variant and reserve required fees before execution
For non-EVM chains: receiverAddress must be bytes (not bytes32), must not be zero, and bridgeData.receiver must equal NON_EVM_ADDRESS

Files:

  • src/Facets/PolymerCCTPFacet.sol
**/*.sol

📄 CodeRabbit inference engine (conventions.md)

Follow solhint rules as configured in .solhint.json

Files:

  • src/Facets/PolymerCCTPFacet.sol
🧠 Learnings (75)
📓 Common learnings
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 782
File: script/deploy/_targetState.json:143-143
Timestamp: 2024-11-25T06:25:01.232Z
Learning: Contracts like `OpBNBBridgeFacet` may be in development and only available in feature branches, resulting in missing source files in the main branch. Before flagging such missing contracts, check for open PRs that might include them.
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : Apply required modifiers where appropriate: nonReentrant, refundExcessNative, validateBridgeData, doesNotContainSourceSwaps/doesContainSourceSwaps, doesNotContainDestinationCalls/doesContainDestinationCalls
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 782
File: script/deploy/_targetState.json:49-49
Timestamp: 2024-11-25T09:05:03.917Z
Learning: The `RelayFacet` contract, when missing from the source code but referenced in deployment configurations, should be treated the same way as `OpBNBBridgeFacet` and can be ignored in code reviews.
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1388
File: deployments/hyperevm.diamond.json:72-75
Timestamp: 2025-09-22T00:52:26.172Z
Learning: In diamond configuration files (deployments/*.diamond.json), it's acceptable to have multiple versions of the same facet (e.g., GlacisFacet v1.0.0 and v1.1.0) deployed at different contract addresses. This is intentional design for version coexistence, gradual migration, or backward compatibility purposes.
📚 Learning: 2024-11-05T17:15:33.833Z
Learnt from: maxklenk
Repo: lifinance/contracts PR: 782
File: script/demoScripts/demoPermit2.ts:100-107
Timestamp: 2024-11-05T17:15:33.833Z
Learning: In demo scripts using testing wallets, passing private keys via command-line arguments is acceptable.

Applied to files:

  • script/demoScripts/demoPolymer.sh
📚 Learning: 2024-11-05T17:14:24.059Z
Learnt from: maxklenk
Repo: lifinance/contracts PR: 782
File: script/demoScripts/demoPermit2.ts:119-124
Timestamp: 2024-11-05T17:14:24.059Z
Learning: In the file `script/demoScripts/demoPermit2.ts`, adding error handling for external API calls is not preferred as it may overload the demo.

Applied to files:

  • script/demoScripts/demoPolymer.sh
📚 Learning: 2024-10-04T09:21:59.708Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 819
File: deployments/polygon.json:0-0
Timestamp: 2024-10-04T09:21:59.708Z
Learning: Ensure that scripts used for codebase verification produce valid and accurate results before reporting issues, especially when checking Ethereum address checksums in `deployments/polygon.json`.

Applied to files:

  • script/demoScripts/demoPolymer.sh
📚 Learning: 2025-08-07T06:34:07.709Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1316
File: script/deploy/deployAllContracts.sh:346-350
Timestamp: 2025-08-07T06:34:07.709Z
Learning: In script/deploy/deployAllContracts.sh, 0xDEnYO prefers not to add automated retry mechanisms for cast send commands in deployment scripts, preferring manual re-execution if network issues occur. This prioritizes simplicity over automated error handling.

Applied to files:

  • script/demoScripts/demoPolymer.sh
📚 Learning: 2025-09-12T11:48:46.333Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1377
File: deployments/mainnet.json:7-7
Timestamp: 2025-09-12T11:48:46.333Z
Learning: When running verification scripts for contract deployments, ensure the scripts are syntactically correct and thoroughly tested before making claims about contract validity. False negatives from faulty scripts can incorrectly flag valid deployments as problematic.

Applied to files:

  • script/demoScripts/demoPolymer.sh
📚 Learning: 2025-06-06T16:09:58.692Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1193
File: script/tasks/diamondSyncWhitelistedAddresses.sh:128-128
Timestamp: 2025-06-06T16:09:58.692Z
Learning: In script/tasks/diamondSyncWhitelistedAddresses.sh at line 128, there is an unquoted command substitution `$(getPrivateKey "$NETWORK" "$ENVIRONMENT")` that should be quoted as `"$(getPrivateKey "$NETWORK" "$ENVIRONMENT")"` to prevent word splitting issues. The user mirooon wants to be reminded about this issue in future PRs when this file is touched.

Applied to files:

  • script/demoScripts/demoPolymer.sh
📚 Learning: 2025-02-20T04:50:33.488Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1015
File: script/deploy/safe/add-safe-owners-and-threshold.ts:117-123
Timestamp: 2025-02-20T04:50:33.488Z
Learning: The `submitAndExecuteTransaction` function in Safe management scripts includes comprehensive error handling that covers transaction proposal, execution, and receipt verification. It uses try-catch blocks, verifies transaction receipt status, logs errors, and properly propagates them up the call stack, making additional error handling around its calls unnecessary.

Applied to files:

  • script/demoScripts/demoPolymer.sh
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 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:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : Apply required modifiers where appropriate: nonReentrant, refundExcessNative, validateBridgeData, doesNotContainSourceSwaps/doesContainSourceSwaps, doesNotContainDestinationCalls/doesContainDestinationCalls

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-10-02T18:10:09.934Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1406
File: src/Facets/UnitFacet.sol:75-81
Timestamp: 2025-10-02T18:10:09.934Z
Learning: In UnitFacet.sol and similar facet contracts in src/Facets/, the LiFiTransferStarted event emission should ALWAYS be at the end of the _startBridge internal function, after all validations and asset transfers have been completed successfully.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : In {facetName}Data, receiverAddress must be the first field and be validated against bridgeData.receiver; verify targetChainId equals bridgeData.destinationChain for EVM-to-EVM

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-07-16T01:03:08.106Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 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/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : Emit LiFiTransferStarted at the start of a transaction before external calls; do not emit LiFiTransferCompleted or LiFiTransferRecovered in facets

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2024-10-31T09:09:38.568Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 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:

  • src/Facets/PolymerCCTPFacet.sol
📚 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:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-07-16T01:04:55.857Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1275
File: src/Facets/AllBridgeFacet.sol:186-188
Timestamp: 2025-07-16T01:04:55.857Z
Learning: In LiFi facet contracts, when public entry point functions have `nonReentrant` modifier protection, internal functions like `_startBridge` that they call benefit from this reentrancy protection, making event emission order after external calls acceptable from a security perspective.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : For non-EVM chains: receiverAddress must be bytes (not bytes32), must not be zero, and bridgeData.receiver must equal NON_EVM_ADDRESS

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-29T11:18:56.656Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Periphery/LDA/Facets/NativeWrapperFacet.sol:1-2
Timestamp: 2025-08-29T11:18:56.656Z
Learning: For src/Periphery/LDA/Facets/NativeWrapperFacet.sol, the SPDX license identifier should be immediately followed by the pragma statement without a blank line in between, as confirmed by mirooon.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-10-02T18:14:51.385Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1406
File: src/Facets/UnitFacet.sol:1-2
Timestamp: 2025-10-02T18:14:51.385Z
Learning: For facet contracts in src/Facets/, the SPDX license identifier should be immediately followed by the pragma statement without a blank line in between, as confirmed by mirooon for src/Facets/UnitFacet.sol.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-10-02T18:14:45.047Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1406
File: script/deploy/facets/UpdateUnitFacet.s.sol:1-3
Timestamp: 2025-10-02T18:14:45.047Z
Learning: For update scripts in script/deploy/facets/, the SPDX license identifier should be immediately followed by the pragma statement without a blank line in between, as confirmed by mirooon for script/deploy/facets/UpdateUnitFacet.s.sol.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 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:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-27T23:01:41.042Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Libraries/LibUniV3Logic.sol:4-5
Timestamp: 2025-08-27T23:01:41.042Z
Learning: In the lifinance/contracts repository, the pattern `import { IERC20, SafeERC20 } from "openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";` is correctly used throughout the codebase and works as expected. SafeERC20.sol re-exports IERC20 in modern OpenZeppelin versions, making this combined import valid. Do not suggest splitting these imports.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : Facet contracts must be located in src/Facets and include "Facet" in the contract name

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-29T14:06:33.384Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: script/deploy/facets/LDA/UpdateUniV2StyleFacet.s.sol:1-2
Timestamp: 2025-08-29T14:06:33.384Z
Learning: For LDA deployment and update scripts in script/deploy/facets/LDA/, the SPDX license identifier should be immediately followed by the pragma statement without a blank line in between, as confirmed by mirooon for script/deploy/facets/LDA/UpdateUniV2StyleFacet.s.sol.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-29T14:05:37.002Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: script/deploy/facets/LDA/DeployLDAOwnershipFacet.s.sol:1-3
Timestamp: 2025-08-29T14:05:37.002Z
Learning: For deployment script files in script/deploy/facets/LDA/, the SPDX license identifier should be immediately followed by the pragma statement without a blank line in between, as confirmed by mirooon for script/deploy/facets/LDA/DeployLDAOwnershipFacet.s.sol.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2024-11-25T06:25:01.232Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 782
File: script/deploy/_targetState.json:143-143
Timestamp: 2024-11-25T06:25:01.232Z
Learning: Contracts like `OpBNBBridgeFacet` may be in development and only available in feature branches, resulting in missing source files in the main branch. Before flagging such missing contracts, check for open PRs that might include them.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2024-11-25T09:05:03.917Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 782
File: script/deploy/_targetState.json:49-49
Timestamp: 2024-11-25T09:05:03.917Z
Learning: The `RelayFacet` contract, when missing from the source code but referenced in deployment configurations, should be treated the same way as `OpBNBBridgeFacet` and can be ignored in code reviews.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : For native fees in facets, use the _depositAndSwap variant and reserve required fees before execution

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2024-11-25T09:04:55.880Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 782
File: script/deploy/_targetState.json:143-143
Timestamp: 2024-11-25T09:04:55.880Z
Learning: Errors about the missing `OpBNBBridgeFacet` contract are expected when it is referenced in the target state but not yet merged into the main branch.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-27T23:36:40.773Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 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/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-07-16T01:04:11.083Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1275
File: src/Facets/AllBridgeFacet.sol:175-175
Timestamp: 2025-07-16T01:04:11.083Z
Learning: In AllBridgeFacet.sol, zero fee validation is not required at the contract level because the backend system ensures that calldata and msg.value are properly aligned before transactions reach the contract.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2024-10-22T03:14:52.578Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 807
File: src/Facets/GasZipFacet.sol:53-53
Timestamp: 2024-10-22T03:14:52.578Z
Learning: In `src/Facets/GasZipFacet.sol`, when checking `msg.value`, the team prefers to use strict equality (`msg.value == _bridgeData.minAmount`) to save gas and avoid refunding excess Ether.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-29T11:53:38.549Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 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:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-02-24T09:35:34.908Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 984
File: src/Periphery/ReceiverChainflip.sol:48-62
Timestamp: 2025-02-24T09:35:34.908Z
Learning: Contract address validations for periphery contracts like ReceiverChainflip are handled in deployment scripts rather than in the constructor.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 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:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-02-21T09:00:28.226Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 984
File: src/Facets/ChainflipFacet.sol:127-146
Timestamp: 2025-02-21T09:00:28.226Z
Learning: The `validateBridgeData` modifier in `Validatable.sol` already validates that the receiver address is not zero, making additional zero-address checks redundant in functions using this modifier.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 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:

  • src/Facets/PolymerCCTPFacet.sol
📚 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:

  • src/Facets/PolymerCCTPFacet.sol
  • testnet-addresses.json
📚 Learning: 2025-08-27T22:29:51.839Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: test/solidity/Periphery/Lda/Facets/SyncSwapV2Facet.t.sol:1-2
Timestamp: 2025-08-27T22:29:51.839Z
Learning: Test files in test/solidity/**/*.t.sol should use the LGPL-3.0-only SPDX license identifier, not Unlicense.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to {src,test/solidity,script}/**/*.sol : Use SPDX license identifier // SPDX-License-Identifier: LGPL-3.0-only for all first-party Solidity files

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-29T11:33:37.368Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: test/solidity/Periphery/LDA/BaseCoreRouteTest.t.sol:1-3
Timestamp: 2025-08-29T11:33:37.368Z
Learning: For test files in test/solidity/Periphery/LDA/**, the SPDX license identifier should be immediately followed by the pragma statement without a blank line in between, as confirmed by mirooon for test/solidity/Periphery/LDA/BaseCoreRouteTest.t.sol.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-29T14:06:01.072Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: script/deploy/facets/LDA/UpdateCurveFacet.s.sol:1-2
Timestamp: 2025-08-29T14:06:01.072Z
Learning: For script/deploy/facets/LDA/UpdateCurveFacet.s.sol and similar LDA update scripts, the SPDX license identifier should be immediately followed by the pragma statement without requiring a blank line in between, as confirmed by mirooon.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-29T11:11:35.958Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Periphery/LDA/Facets/SyncSwapV2Facet.sol:1-3
Timestamp: 2025-08-29T11:11:35.958Z
Learning: For LDA facet files in src/Periphery/LDA/Facets/, the SPDX license identifier can be immediately followed by the pragma statement without requiring a blank line in between, as confirmed by mirooon.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-27T23:01:41.042Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Libraries/LibUniV3Logic.sol:4-5
Timestamp: 2025-08-27T23:01:41.042Z
Learning: In OpenZeppelin v4.0+, SafeERC20.sol re-exports IERC20, so importing both IERC20 and SafeERC20 from "openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol" is valid and works correctly. Do not suggest splitting this import into separate files.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-27T23:01:41.042Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Libraries/LibUniV3Logic.sol:4-5
Timestamp: 2025-08-27T23:01:41.042Z
Learning: In OpenZeppelin v4.0+, SafeERC20.sol re-exports IERC20, so importing both IERC20 and SafeERC20 from "openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol" is valid and works correctly. Do not suggest splitting this import.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-27T23:01:41.042Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Libraries/LibUniV3Logic.sol:4-5
Timestamp: 2025-08-27T23:01:41.042Z
Learning: In modern OpenZeppelin versions (v4.0+), SafeERC20.sol re-exports IERC20, making the combined import `import { IERC20, SafeERC20 } from "openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";` completely valid and working. Do not suggest splitting these imports as they work correctly as a combined import.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-19T05:07:19.448Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 1303
File: script/deploy/tron/utils.ts:3-5
Timestamp: 2025-08-19T05:07:19.448Z
Learning: In the lifinance/contracts repository, consola and TronWeb should be imported as named exports: `import { consola } from 'consola'` and `import { TronWeb } from 'tronweb'`. Do not flag these as incorrect default export usage.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-19T05:07:19.448Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 1303
File: script/deploy/tron/utils.ts:3-5
Timestamp: 2025-08-19T05:07:19.448Z
Learning: In the lifinance/contracts repository, consola and TronWeb should be imported as named exports: `import { consola } from 'consola'` and `import { TronWeb } from 'tronweb'`. Do not flag these as incorrect default export usage. Both libraries provide named exports, not default exports.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-27T13:07:58.254Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Periphery/Lda/Facets/CurveFacet.sol:0-0
Timestamp: 2025-08-27T13:07:58.254Z
Learning: In the LiFi contracts codebase, ICurveLegacy interface is defined inline in src/Periphery/LiFiDEXAggregator.sol with a non-payable exchange function. The CurveFacet diff shows importing it from a separate file that doesn't exist, creating a compilation issue.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2024-09-23T02:04:16.323Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 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/Facets/PolymerCCTPFacet.sol
📚 Learning: 2024-10-09T03:47:21.269Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 807
File: src/Periphery/GasZipPeriphery.sol:4-14
Timestamp: 2024-10-09T03:47:21.269Z
Learning: In `GasZipPeriphery.sol`, `LibUtil` and `Validatable` are used, so ensure not to suggest their removal in future reviews.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to {src,test/solidity,script}/**/*.sol : 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

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-09-19T14:10:55.064Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 1324
File: src/Facets/EcoFacet.sol:68-71
Timestamp: 2025-09-19T14:10:55.064Z
Learning: In src/Facets/EcoFacet.sol, the team prefers to use msg.sender as the refund address for native tokens in the refundExcessNative modifier, contrary to the general guideline of requiring explicit refund parameters.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to {src,test/solidity,script}/**/*.sol : Error names must be descriptive PascalCase, use custom errors instead of revert() strings, and omit error messages for gas optimization

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : Do not use msg.sender as refund address; require an explicit parameter for refund address

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 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:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-09-25T00:12:58.536Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1395
File: src/Periphery/FeeForwarder.sol:78-120
Timestamp: 2025-09-25T00:12:58.536Z
Learning: In src/Periphery/FeeForwarder.sol, the team intentionally uses address(this).balance for refunding remaining native tokens because the contract is designed to never hold funds and not collect dust, making it safe to return the entire balance to the caller.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-10-13T11:13:48.847Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1413
File: src/Facets/EverclearV2Facet.sol:75-96
Timestamp: 2025-10-13T11:13:48.847Z
Learning: In EverclearV2Facet (and all LiFi facets), the team standard is to use msg.sender as the refund address with the refundExcessNative modifier, not requiring an explicit refund address parameter.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-01-28T14:29:00.823Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 924
File: script/deploy/zksync/utils/UpdateScriptBase.sol:112-178
Timestamp: 2025-01-28T14:29:00.823Z
Learning: The suggestion to modify `buildDiamondCut` function in `UpdateScriptBase.sol` to handle selectors from multiple old facets differently was deemed unnecessary by the maintainer.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-29T11:07:57.743Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Periphery/LDA/Facets/CurveFacet.sol:87-91
Timestamp: 2025-08-29T11:07:57.743Z
Learning: In src/Periphery/LDA/Facets/CurveFacet.sol, modern Curve pools (isV2=true, representing V2/NG pools) should reject native tokenIn by adding an early revert check when LibAsset.isNativeAsset(tokenIn) is true, since ICurveV2 exchange functions are non-payable and cannot accept native ETH.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2024-12-02T06:33:33.309Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 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/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-02-21T09:00:28.226Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 984
File: src/Facets/ChainflipFacet.sol:127-146
Timestamp: 2025-02-21T09:00:28.226Z
Learning: The `validateBridgeData` modifier in `Validatable.sol` validates that the receiver address is not zero using `LibUtil.isZeroAddress`, making additional zero-address checks redundant in functions using this modifier.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-20T09:56:24.259Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1327
File: docs/OutputValidator.md:23-29
Timestamp: 2025-08-20T09:56:24.259Z
Learning: In src/Periphery/OutputValidator.sol, the ERC20 token flow that uses balanceOf(msg.sender) to compute actualAmount and transfers the excess via safeTransferFrom is intentional behavior, as confirmed by 0xDEnYO. The design choice to potentially include pre-existing ERC20 balances in the excess calculation is by design and should not be flagged as a security concern in future reviews.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2024-10-14T08:23:38.076Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 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/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-04-28T07:46:24.084Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 1116
File: src/Libraries/LibAsset.sol:102-108
Timestamp: 2025-04-28T07:46:24.084Z
Learning: In the LiFi contracts, the `LibAsset.transferFromERC20` function is intentionally designed to accept arbitrary `from` parameters, with security ensured through a whitelist mechanism that controls which contracts can call this function.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2024-11-21T08:39:29.530Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 861
File: config/dexs.json:748-752
Timestamp: 2024-11-21T08:39:29.530Z
Learning: In the 'worldchain' network, the addresses `0x50D5a8aCFAe13Dceb217E9a071F6c6Bd5bDB4155`, `0x8f023b4193a6b18C227B4a755f8e28B3D30Ef9a1`, and `0x603a538477d44064eA5A5d8C345b4Ff6fca1142a` are used as DEXs and should be included in `config/dexs.json`.

Applied to files:

  • testnet-addresses.json
📚 Learning: 2025-08-28T02:02:56.965Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1136
File: config/dexs.json:0-0
Timestamp: 2025-08-28T02:02:56.965Z
Learning: Different networks in config/dexs.json naturally and appropriately have different DEX addresses. Do not flag different addresses across different networks as issues - only flag if the same network's addresses appear to have changed unintentionally between branches.

Applied to files:

  • testnet-addresses.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:

  • testnet-addresses.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:

  • testnet-addresses.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:

  • testnet-addresses.json
📚 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:

  • testnet-addresses.json
📚 Learning: 2024-12-04T01:59:34.045Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 832
File: deployments/_deployments_log_file.json:23712-23720
Timestamp: 2024-12-04T01:59:34.045Z
Learning: In `deployments/_deployments_log_file.json`, duplicate deployment entries for the same version and address may occur because they correspond to deployments on different networks. These entries are acceptable and should not be flagged as duplicates in future reviews.

Applied to files:

  • testnet-addresses.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:

  • testnet-addresses.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:

  • testnet-addresses.json
🪛 Gitleaks (8.28.0)
testnet-addresses.json

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

(generic-api-key)


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

(generic-api-key)


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

(generic-api-key)

🪛 Shellcheck (0.11.0)
script/demoScripts/demoPolymer.sh

[warning] 141-141: TX_CHAIN_ID appears unused. Verify use (or export if used externally).

(SC2034)


[warning] 145-145: POLYMER_TX_DATA appears unused. Verify use (or export if used externally).

(SC2034)

🔇 Additional comments (1)
testnet-addresses.json (1)

1-20: LGTM! JSON structure is valid.

The testnet addresses file is well-structured with consistent chain-specific addresses for USDC, tokenMessenger, polymerFeeRecipient, and diamondProxy. The static analysis warnings about "Generic API Keys" on lines 5, 11, and 17 are false positives—these are legitimate Ethereum contract addresses (tokenMessenger), not API keys.

Note: The same tokenMessenger and polymerFeeRecipient addresses across all three testnets is likely intentional for testnet deployments.

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

♻️ Duplicate comments (9)
src/Facets/PolymerCCTPFacet.sol (6)

1-2: SPDX license identifier must be LGPL-3.0-only.

The SPDX identifier is still MIT. Per coding guidelines, all first-party Solidity files must use LGPL-3.0-only.

As per coding guidelines


4-14: Duplicate imports cause compilation failure.

IERC20 is imported twice (lines 4 and 11) and PolymerCCTPData is imported twice (lines 6 and 9). Remove the redundant imports.

As per coding guidelines

Based on learnings, LibAllowList is only required for facets that make arbitrary external calls to DEX aggregators, not for bridge facets like this one.


21-23: Immutable variables must use CONSTANT_CASE.

Per coding guidelines, constants and immutables should use CONSTANT_CASE naming convention.

As per coding guidelines


41-50: Missing required facet functions.

Per coding guidelines, facets must implement:

  • internal _startBridge
  • swapAndStartBridgeTokensVia{FacetName} (public entry point)
  • startBridgeTokensVia{FacetName} (already present)

As per coding guidelines


67-78: Critical: CCTP domain IDs don't match EVM chain IDs.

Line 70 uses uint32(_bridgeData.destinationChainId), but CCTP uses its own domain ID system that doesn't correspond to EVM chain IDs. For example, Ethereum mainnet is chain ID 1 but CCTP domain 0; Avalanche is chain ID 43114 but CCTP domain 1.

This will cause all cross-chain transfers to fail. The PolymerCCTPData struct should include a destinationDomain field, and that should be used here instead.

This issue was flagged in previous reviews but remains unresolved.


80-95: Events must be emitted before external calls.

Per coding guidelines, "Emit LiFiTransferStarted at the start of a transaction before external calls." Both PolymerCCTPFeeSent (line 80) and LiFiTransferStarted (lines 84-95) are emitted after the external tokenMessenger.depositForBurn call (lines 68-78).

Move both event emissions to occur before line 68.

As per coding guidelines

Regarding the TODO on line 83: Yes, LiFiTransferStarted emission is required per coding guidelines for all facets.

src/Interfaces/IPolymerCCTP.sol (3)

1-2: SPDX license identifier must be LGPL-3.0-only.

As per coding guidelines


6-11: PolymerCCTPData struct doesn't follow facet data conventions.

Per coding guidelines:

  1. receiverAddress must be the first field (currently missing)
  2. For non-EVM chains, the receiver should be bytes (not bytes32)
  3. Missing destinationDomain field (CCTP uses domain IDs, not chain IDs)

The struct should include a destinationDomain field since CCTP domain IDs don't correspond to EVM chain IDs.

As per coding guidelines


13-23: Interface missing required NatSpec header.

Per coding guidelines, every interface must include NatSpec in this order: @title, @notice, @author LI.FI (https://li.fi), @Custom:version X.Y.Z

As per coding guidelines

🧹 Nitpick comments (3)
src/Facets/PolymerCCTPFacet.sol (2)

26-26: Remove or address TODO comment.

This TODO about the fee collector should be resolved before merging.

Do you want me to help clarify the fee collector design decision, or should this TODO be removed if the current design is final?


52-54: Redundant validation - onlyAllowSourceToken modifier already enforces this.

The onlyAllowSourceToken(_bridgeData, usdc) modifier on line 47 already validates that sendingAssetId equals usdc. This explicit check is redundant.

Consider removing this redundant check to reduce code complexity, or keep it as an explicit sanity check if preferred for clarity.

test/solidity/Facets/PolymerCCTPFacet.t.sol (1)

103-131: Test name doesn't match test condition.

The test is named test_Revert_FeeGreaterThanAmount but on line 105 it sets polymerFee = 100e6 which equals bridgeAmount, not greater than.

The facet code checks polymerTokenFee >= minAmount (line 56 in the facet), so this test is valid, but consider either:

  1. Renaming to test_Revert_FeeEqualsOrGreaterThanAmount, or
  2. Changing line 105 to uint256 polymerFee = 101e6; to truly test greater-than
📜 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 7872e29 and 3cf3af9.

📒 Files selected for processing (3)
  • src/Facets/PolymerCCTPFacet.sol (1 hunks)
  • src/Interfaces/IPolymerCCTP.sol (1 hunks)
  • test/solidity/Facets/PolymerCCTPFacet.t.sol (1 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/Facets/PolymerCCTPFacet.sol
  • test/solidity/Facets/PolymerCCTPFacet.t.sol
  • src/Interfaces/IPolymerCCTP.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/Facets/PolymerCCTPFacet.sol
  • src/Interfaces/IPolymerCCTP.sol
src/Facets/**/*Facet.sol

📄 CodeRabbit inference engine (conventions.md)

src/Facets/**/*Facet.sol: Facet contracts must be located in src/Facets and include "Facet" in the contract name
Facet contracts must implement: internal _startBridge, swapAndStartBridgeTokensVia{FacetName}, and startBridgeTokensVia{FacetName}
Facet contracts must import/use: ILiFi, LibAsset, LibSwap, LibAllowList; and use security mixins ReentrancyGuard, SwapperV2, Validatable; ECDSA optional
Do not use msg.sender as refund address; require an explicit parameter for refund address
In {facetName}Data, receiverAddress must be the first field and be validated against bridgeData.receiver; verify targetChainId equals bridgeData.destinationChain for EVM-to-EVM
Apply required modifiers where appropriate: nonReentrant, refundExcessNative, validateBridgeData, doesNotContainSourceSwaps/doesContainSourceSwaps, doesNotContainDestinationCalls/doesContainDestinationCalls
Emit LiFiTransferStarted at the start of a transaction before external calls; do not emit LiFiTransferCompleted or LiFiTransferRecovered in facets
For native fees in facets, use the _depositAndSwap variant and reserve required fees before execution
For non-EVM chains: receiverAddress must be bytes (not bytes32), must not be zero, and bridgeData.receiver must equal NON_EVM_ADDRESS

Files:

  • src/Facets/PolymerCCTPFacet.sol
**/*.sol

📄 CodeRabbit inference engine (conventions.md)

Follow solhint rules as configured in .solhint.json

Files:

  • src/Facets/PolymerCCTPFacet.sol
  • test/solidity/Facets/PolymerCCTPFacet.t.sol
  • src/Interfaces/IPolymerCCTP.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/Facets/PolymerCCTPFacet.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/IPolymerCCTP.sol
🧠 Learnings (74)
📓 Common learnings
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: 782
File: script/deploy/_targetState.json:143-143
Timestamp: 2024-11-25T06:25:01.232Z
Learning: Contracts like `OpBNBBridgeFacet` may be in development and only available in feature branches, resulting in missing source files in the main branch. Before flagging such missing contracts, check for open PRs that might include them.
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : Apply required modifiers where appropriate: nonReentrant, refundExcessNative, validateBridgeData, doesNotContainSourceSwaps/doesContainSourceSwaps, doesNotContainDestinationCalls/doesContainDestinationCalls
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 782
File: script/deploy/_targetState.json:49-49
Timestamp: 2024-11-25T09:05:03.917Z
Learning: The `RelayFacet` contract, when missing from the source code but referenced in deployment configurations, should be treated the same way as `OpBNBBridgeFacet` and can be ignored in code reviews.
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 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:

  • src/Facets/PolymerCCTPFacet.sol
  • test/solidity/Facets/PolymerCCTPFacet.t.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : Apply required modifiers where appropriate: nonReentrant, refundExcessNative, validateBridgeData, doesNotContainSourceSwaps/doesContainSourceSwaps, doesNotContainDestinationCalls/doesContainDestinationCalls

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
  • test/solidity/Facets/PolymerCCTPFacet.t.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-07-16T01:03:08.106Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 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/Facets/PolymerCCTPFacet.sol
  • test/solidity/Facets/PolymerCCTPFacet.t.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : In {facetName}Data, receiverAddress must be the first field and be validated against bridgeData.receiver; verify targetChainId equals bridgeData.destinationChain for EVM-to-EVM

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
  • test/solidity/Facets/PolymerCCTPFacet.t.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 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:

  • src/Facets/PolymerCCTPFacet.sol
  • test/solidity/Facets/PolymerCCTPFacet.t.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-10-02T18:10:09.934Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1406
File: src/Facets/UnitFacet.sol:75-81
Timestamp: 2025-10-02T18:10:09.934Z
Learning: In UnitFacet.sol and similar facet contracts in src/Facets/, the LiFiTransferStarted event emission should ALWAYS be at the end of the _startBridge internal function, after all validations and asset transfers have been completed successfully.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
  • test/solidity/Facets/PolymerCCTPFacet.t.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2024-10-31T09:09:38.568Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 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:

  • src/Facets/PolymerCCTPFacet.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : Emit LiFiTransferStarted at the start of a transaction before external calls; do not emit LiFiTransferCompleted or LiFiTransferRecovered in facets

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2024-11-25T09:04:55.880Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 782
File: script/deploy/_targetState.json:143-143
Timestamp: 2024-11-25T09:04:55.880Z
Learning: Errors about the missing `OpBNBBridgeFacet` contract are expected when it is referenced in the target state but not yet merged into the main branch.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : For non-EVM chains: receiverAddress must be bytes (not bytes32), must not be zero, and bridgeData.receiver must equal NON_EVM_ADDRESS

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-08-29T11:18:56.656Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Periphery/LDA/Facets/NativeWrapperFacet.sol:1-2
Timestamp: 2025-08-29T11:18:56.656Z
Learning: For src/Periphery/LDA/Facets/NativeWrapperFacet.sol, the SPDX license identifier should be immediately followed by the pragma statement without a blank line in between, as confirmed by mirooon.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-10-02T18:14:51.385Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1406
File: src/Facets/UnitFacet.sol:1-2
Timestamp: 2025-10-02T18:14:51.385Z
Learning: For facet contracts in src/Facets/, the SPDX license identifier should be immediately followed by the pragma statement without a blank line in between, as confirmed by mirooon for src/Facets/UnitFacet.sol.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-10-02T18:14:45.047Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1406
File: script/deploy/facets/UpdateUnitFacet.s.sol:1-3
Timestamp: 2025-10-02T18:14:45.047Z
Learning: For update scripts in script/deploy/facets/, the SPDX license identifier should be immediately followed by the pragma statement without a blank line in between, as confirmed by mirooon for script/deploy/facets/UpdateUnitFacet.s.sol.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 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:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-27T23:01:41.042Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Libraries/LibUniV3Logic.sol:4-5
Timestamp: 2025-08-27T23:01:41.042Z
Learning: In the lifinance/contracts repository, the pattern `import { IERC20, SafeERC20 } from "openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";` is correctly used throughout the codebase and works as expected. SafeERC20.sol re-exports IERC20 in modern OpenZeppelin versions, making this combined import valid. Do not suggest splitting these imports.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : Facet contracts must be located in src/Facets and include "Facet" in the contract name

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
  • test/solidity/Facets/PolymerCCTPFacet.t.sol
📚 Learning: 2025-08-29T14:06:33.384Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: script/deploy/facets/LDA/UpdateUniV2StyleFacet.s.sol:1-2
Timestamp: 2025-08-29T14:06:33.384Z
Learning: For LDA deployment and update scripts in script/deploy/facets/LDA/, the SPDX license identifier should be immediately followed by the pragma statement without a blank line in between, as confirmed by mirooon for script/deploy/facets/LDA/UpdateUniV2StyleFacet.s.sol.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-08-29T14:05:37.002Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: script/deploy/facets/LDA/DeployLDAOwnershipFacet.s.sol:1-3
Timestamp: 2025-08-29T14:05:37.002Z
Learning: For deployment script files in script/deploy/facets/LDA/, the SPDX license identifier should be immediately followed by the pragma statement without a blank line in between, as confirmed by mirooon for script/deploy/facets/LDA/DeployLDAOwnershipFacet.s.sol.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2024-11-25T06:25:01.232Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 782
File: script/deploy/_targetState.json:143-143
Timestamp: 2024-11-25T06:25:01.232Z
Learning: Contracts like `OpBNBBridgeFacet` may be in development and only available in feature branches, resulting in missing source files in the main branch. Before flagging such missing contracts, check for open PRs that might include them.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2024-11-25T09:05:03.917Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 782
File: script/deploy/_targetState.json:49-49
Timestamp: 2024-11-25T09:05:03.917Z
Learning: The `RelayFacet` contract, when missing from the source code but referenced in deployment configurations, should be treated the same way as `OpBNBBridgeFacet` and can be ignored in code reviews.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : For native fees in facets, use the _depositAndSwap variant and reserve required fees before execution

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
  • test/solidity/Facets/PolymerCCTPFacet.t.sol
📚 Learning: 2025-08-27T23:36:40.773Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 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/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-07-16T01:04:11.083Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1275
File: src/Facets/AllBridgeFacet.sol:175-175
Timestamp: 2025-07-16T01:04:11.083Z
Learning: In AllBridgeFacet.sol, zero fee validation is not required at the contract level because the backend system ensures that calldata and msg.value are properly aligned before transactions reach the contract.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2024-10-22T03:14:52.578Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 807
File: src/Facets/GasZipFacet.sol:53-53
Timestamp: 2024-10-22T03:14:52.578Z
Learning: In `src/Facets/GasZipFacet.sol`, when checking `msg.value`, the team prefers to use strict equality (`msg.value == _bridgeData.minAmount`) to save gas and avoid refunding excess Ether.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-29T11:53:38.549Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 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:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-02-24T09:35:34.908Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 984
File: src/Periphery/ReceiverChainflip.sol:48-62
Timestamp: 2025-02-24T09:35:34.908Z
Learning: Contract address validations for periphery contracts like ReceiverChainflip are handled in deployment scripts rather than in the constructor.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 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:

  • src/Facets/PolymerCCTPFacet.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-02-21T09:00:28.226Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 984
File: src/Facets/ChainflipFacet.sol:127-146
Timestamp: 2025-02-21T09:00:28.226Z
Learning: The `validateBridgeData` modifier in `Validatable.sol` already validates that the receiver address is not zero, making additional zero-address checks redundant in functions using this modifier.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 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:

  • src/Facets/PolymerCCTPFacet.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 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:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-27T22:29:51.839Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: test/solidity/Periphery/Lda/Facets/SyncSwapV2Facet.t.sol:1-2
Timestamp: 2025-08-27T22:29:51.839Z
Learning: Test files in test/solidity/**/*.t.sol should use the LGPL-3.0-only SPDX license identifier, not Unlicense.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to {src,test/solidity,script}/**/*.sol : Use SPDX license identifier // SPDX-License-Identifier: LGPL-3.0-only for all first-party Solidity files

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-08-29T11:33:37.368Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: test/solidity/Periphery/LDA/BaseCoreRouteTest.t.sol:1-3
Timestamp: 2025-08-29T11:33:37.368Z
Learning: For test files in test/solidity/Periphery/LDA/**, the SPDX license identifier should be immediately followed by the pragma statement without a blank line in between, as confirmed by mirooon for test/solidity/Periphery/LDA/BaseCoreRouteTest.t.sol.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-08-29T14:06:01.072Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: script/deploy/facets/LDA/UpdateCurveFacet.s.sol:1-2
Timestamp: 2025-08-29T14:06:01.072Z
Learning: For script/deploy/facets/LDA/UpdateCurveFacet.s.sol and similar LDA update scripts, the SPDX license identifier should be immediately followed by the pragma statement without requiring a blank line in between, as confirmed by mirooon.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-29T11:11:35.958Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Periphery/LDA/Facets/SyncSwapV2Facet.sol:1-3
Timestamp: 2025-08-29T11:11:35.958Z
Learning: For LDA facet files in src/Periphery/LDA/Facets/, the SPDX license identifier can be immediately followed by the pragma statement without requiring a blank line in between, as confirmed by mirooon.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-27T23:01:41.042Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Libraries/LibUniV3Logic.sol:4-5
Timestamp: 2025-08-27T23:01:41.042Z
Learning: In OpenZeppelin v4.0+, SafeERC20.sol re-exports IERC20, so importing both IERC20 and SafeERC20 from "openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol" is valid and works correctly. Do not suggest splitting this import into separate files.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-27T23:01:41.042Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Libraries/LibUniV3Logic.sol:4-5
Timestamp: 2025-08-27T23:01:41.042Z
Learning: In OpenZeppelin v4.0+, SafeERC20.sol re-exports IERC20, so importing both IERC20 and SafeERC20 from "openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol" is valid and works correctly. Do not suggest splitting this import.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-27T23:01:41.042Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Libraries/LibUniV3Logic.sol:4-5
Timestamp: 2025-08-27T23:01:41.042Z
Learning: In modern OpenZeppelin versions (v4.0+), SafeERC20.sol re-exports IERC20, making the combined import `import { IERC20, SafeERC20 } from "openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";` completely valid and working. Do not suggest splitting these imports as they work correctly as a combined import.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-19T05:07:19.448Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 1303
File: script/deploy/tron/utils.ts:3-5
Timestamp: 2025-08-19T05:07:19.448Z
Learning: In the lifinance/contracts repository, consola and TronWeb should be imported as named exports: `import { consola } from 'consola'` and `import { TronWeb } from 'tronweb'`. Do not flag these as incorrect default export usage.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-19T05:07:19.448Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 1303
File: script/deploy/tron/utils.ts:3-5
Timestamp: 2025-08-19T05:07:19.448Z
Learning: In the lifinance/contracts repository, consola and TronWeb should be imported as named exports: `import { consola } from 'consola'` and `import { TronWeb } from 'tronweb'`. Do not flag these as incorrect default export usage. Both libraries provide named exports, not default exports.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-27T13:07:58.254Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Periphery/Lda/Facets/CurveFacet.sol:0-0
Timestamp: 2025-08-27T13:07:58.254Z
Learning: In the LiFi contracts codebase, ICurveLegacy interface is defined inline in src/Periphery/LiFiDEXAggregator.sol with a non-payable exchange function. The CurveFacet diff shows importing it from a separate file that doesn't exist, creating a compilation issue.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2024-09-23T02:04:16.323Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 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/Facets/PolymerCCTPFacet.sol
📚 Learning: 2024-10-09T03:47:21.269Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 807
File: src/Periphery/GasZipPeriphery.sol:4-14
Timestamp: 2024-10-09T03:47:21.269Z
Learning: In `GasZipPeriphery.sol`, `LibUtil` and `Validatable` are used, so ensure not to suggest their removal in future reviews.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to {src,test/solidity,script}/**/*.sol : 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

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-09-19T14:10:55.064Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 1324
File: src/Facets/EcoFacet.sol:68-71
Timestamp: 2025-09-19T14:10:55.064Z
Learning: In src/Facets/EcoFacet.sol, the team prefers to use msg.sender as the refund address for native tokens in the refundExcessNative modifier, contrary to the general guideline of requiring explicit refund parameters.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to {src,test/solidity,script}/**/*.sol : Error names must be descriptive PascalCase, use custom errors instead of revert() strings, and omit error messages for gas optimization

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-19T05:10:12.922Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 1303
File: script/utils/network.ts:27-32
Timestamp: 2025-08-19T05:10:12.922Z
Learning: In the lifinance/contracts repository, the team decided to standardize RPC environment variable naming by converting all hyphens to underscores in network names. For example, "tron-shasta" becomes "ETH_NODE_URI_TRON_SHASTA". The getRPCEnvVarName() helper in script/utils/network.ts implements this standard and should be used consistently across the codebase instead of direct string manipulation.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : Do not use msg.sender as refund address; require an explicit parameter for refund address

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 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:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-09-25T00:12:58.536Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1395
File: src/Periphery/FeeForwarder.sol:78-120
Timestamp: 2025-09-25T00:12:58.536Z
Learning: In src/Periphery/FeeForwarder.sol, the team intentionally uses address(this).balance for refunding remaining native tokens because the contract is designed to never hold funds and not collect dust, making it safe to return the entire balance to the caller.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-10-13T11:13:48.847Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1413
File: src/Facets/EverclearV2Facet.sol:75-96
Timestamp: 2025-10-13T11:13:48.847Z
Learning: In EverclearV2Facet (and all LiFi facets), the team standard is to use msg.sender as the refund address with the refundExcessNative modifier, not requiring an explicit refund address parameter.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-01-28T14:29:00.823Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 924
File: script/deploy/zksync/utils/UpdateScriptBase.sol:112-178
Timestamp: 2025-01-28T14:29:00.823Z
Learning: The suggestion to modify `buildDiamondCut` function in `UpdateScriptBase.sol` to handle selectors from multiple old facets differently was deemed unnecessary by the maintainer.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-29T11:07:57.743Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Periphery/LDA/Facets/CurveFacet.sol:87-91
Timestamp: 2025-08-29T11:07:57.743Z
Learning: In src/Periphery/LDA/Facets/CurveFacet.sol, modern Curve pools (isV2=true, representing V2/NG pools) should reject native tokenIn by adding an early revert check when LibAsset.isNativeAsset(tokenIn) is true, since ICurveV2 exchange functions are non-payable and cannot accept native ETH.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
  • test/solidity/Facets/PolymerCCTPFacet.t.sol
📚 Learning: 2024-12-02T06:33:33.309Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 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/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-02-21T09:00:28.226Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 984
File: src/Facets/ChainflipFacet.sol:127-146
Timestamp: 2025-02-21T09:00:28.226Z
Learning: The `validateBridgeData` modifier in `Validatable.sol` validates that the receiver address is not zero using `LibUtil.isZeroAddress`, making additional zero-address checks redundant in functions using this modifier.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-20T09:56:24.259Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1327
File: docs/OutputValidator.md:23-29
Timestamp: 2025-08-20T09:56:24.259Z
Learning: In src/Periphery/OutputValidator.sol, the ERC20 token flow that uses balanceOf(msg.sender) to compute actualAmount and transfers the excess via safeTransferFrom is intentional behavior, as confirmed by 0xDEnYO. The design choice to potentially include pre-existing ERC20 balances in the excess calculation is by design and should not be flagged as a security concern in future reviews.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-07-16T01:04:55.857Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1275
File: src/Facets/AllBridgeFacet.sol:186-188
Timestamp: 2025-07-16T01:04:55.857Z
Learning: In LiFi facet contracts, when public entry point functions have `nonReentrant` modifier protection, internal functions like `_startBridge` that they call benefit from this reentrancy protection, making event emission order after external calls acceptable from a security perspective.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2024-10-14T08:23:38.076Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 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/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-04-28T07:46:24.084Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 1116
File: src/Libraries/LibAsset.sol:102-108
Timestamp: 2025-04-28T07:46:24.084Z
Learning: In the LiFi contracts, the `LibAsset.transferFromERC20` function is intentionally designed to accept arbitrary `from` parameters, with security ensured through a whitelist mechanism that controls which contracts can call this function.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-28T02:41:07.505Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1321
File: docs/RelayDepositoryFacet.md:9-10
Timestamp: 2025-08-28T02:41:07.505Z
Learning: Unit tests for RelayDepositoryFacet cannot verify fund forwarding behavior after deposits because the facet delegates to external IRelayDepository contracts. The forwarding logic is implemented in the Relay Protocol V2 Depository contracts, not in the facet itself.

Applied to files:

  • test/solidity/Facets/PolymerCCTPFacet.t.sol
📚 Learning: 2025-01-22T12:36:12.699Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 945
File: test/solidity/Facets/GlacisFacet.t.sol:214-262
Timestamp: 2025-01-22T12:36:12.699Z
Learning: The GlacisFacet test suite inherits from TestBaseFacet which already covers various failure scenarios including invalid receiver address, invalid amounts, same chain bridging, and insufficient funds, making additional failure scenario tests redundant.

Applied to files:

  • test/solidity/Facets/PolymerCCTPFacet.t.sol
📚 Learning: 2025-01-28T11:29:09.566Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 945
File: test/solidity/Facets/GlacisFacet.t.sol:0-0
Timestamp: 2025-01-28T11:29:09.566Z
Learning: Empty test methods with explanatory comments are acceptable in test classes when the feature being tested (e.g., native token bridging) is not supported by the implementation.

Applied to files:

  • test/solidity/Facets/PolymerCCTPFacet.t.sol
📚 Learning: 2024-10-10T03:18:20.721Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 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/Facets/PolymerCCTPFacet.t.sol
📚 Learning: 2025-08-27T13:07:58.254Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Periphery/Lda/Facets/CurveFacet.sol:0-0
Timestamp: 2025-08-27T13:07:58.254Z
Learning: The CurveFacet in src/Periphery/Lda/Facets/CurveFacet.sol has fundamental issues: it imports a non-existent ICurveLegacy interface, and attempts to send ETH to non-payable exchange functions in both ICurve and ICurveV2 interfaces. All current Curve interfaces are non-payable and cannot accept native ETH.

Applied to files:

  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-08-29T11:04:14.413Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Periphery/LDA/Errors/Errors.sol:1-7
Timestamp: 2025-08-29T11:04:14.413Z
Learning: For error files in src/Periphery/LDA/Errors/Errors.sol and similar error definition files, the file header format should keep NatSpec comments before the pragma statement due to solhint compiler requirements, rather than following the standard convention of SPDX → blank line → pragma → NatSpec.

Applied to files:

  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Interfaces/**/*.sol : Every interface must include NatSpec in this order: title, notice, author LI.FI (https://li.fi), custom:version X.Y.Z

Applied to files:

  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/**/*.sol : Every contract must include NatSpec header in this order: title, author LI.FI (https://li.fi), notice, custom:version X.Y.Z

Applied to files:

  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-09-24T23:55:07.082Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1395
File: script/deploy/facets/DeployFeeForwarder.s.sol:1-1
Timestamp: 2025-09-24T23:55:07.082Z
Learning: All Solidity files in the lifinance/contracts repository, including deployment scripts in script/deploy/, must use the SPDX license identifier "// SPDX-License-Identifier: LGPL-3.0-only" as specified in conventions.md. This applies to all .sol files in src/, test/solidity/, and script/ directories.

Applied to files:

  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-09-25T00:05:33.977Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1395
File: src/Periphery/FeeForwarder.sol:1-2
Timestamp: 2025-09-25T00:05:33.977Z
Learning: In the lifinance/contracts repository, the established convention is that SPDX license identifiers should be immediately followed by the pragma statement without a blank line in between. This applies to all Solidity files across src/, test/, and script/ directories, as confirmed by 0xDEnYO and verified by codebase analysis.

Applied to files:

  • src/Interfaces/IPolymerCCTP.sol
📚 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:

  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-02-14T09:37:32.647Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 984
File: src/Facets/ChainflipFacet.sol:50-56
Timestamp: 2025-02-14T09:37:32.647Z
Learning: ChainflipData struct in ChainflipFacet.sol does not require additional field validation as confirmed by the team.

Applied to files:

  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/**/*.sol : All public/external functions must have NatSpec comments documenting purpose, params, and returns

Applied to files:

  • src/Interfaces/IPolymerCCTP.sol
📚 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:

  • src/Interfaces/IPolymerCCTP.sol
🔇 Additional comments (7)
src/Facets/PolymerCCTPFacet.sol (3)

51-51: Remove or address TODO comment.

Is validation of integrator and bridge fields necessary, or can this TODO be removed?


60-60: Remove or address TODO comment.

Should this TODO be removed? The check is appropriate since USDC is the only supported asset.


64-64: Remove or address TODO comment.

Standard ERC20 approve is used here. Is SafeERC20 needed, or can this TODO be removed?

test/solidity/Facets/PolymerCCTPFacet.t.sol (4)

13-29: LGTM - MockTokenMessenger is appropriate for testing.

The mock correctly simulates the token burn flow by transferring tokens from the caller and emitting an event.


32-38: LGTM - MockUSDC is appropriate for testing.

The mock correctly simulates USDC with 6 decimals and provides a mint function for test setup.


61-101: LGTM - Positive test case is well-structured.

The test properly verifies:

  • Fee transfer to the fee receiver
  • Bridge amount transfer to token messenger
  • User balance deduction

Test uses appropriate pranking and assertions.


133-214: LGTM - Negative test cases provide good coverage.

The test suite includes appropriate failure scenarios:

  • Zero amount validation
  • Zero receiver validation
  • Invalid sending asset validation

All tests use correct error selectors and proper setup.

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

♻️ Duplicate comments (8)
script/demoScripts/demoPolymer.sh (3)

25-37: Handle chain-id lookup failures explicitly.

cast chain-id can fail (bad RPC, network hiccup). In that case it prints an error string, you store it in FROM_CHAIN_ID/TO_CHAIN_ID, pass the -z checks, and later jq explodes with an opaque JSON error. Capture stderr and bail out immediately so we fail fast with a clear message, rather than carrying garbage downstream.

-FROM_CHAIN_ID=$(cast chain-id -r "$FROM_RPC_URL")
-TO_CHAIN_ID=$(cast chain-id -r "$TO_RPC_URL")
+if ! FROM_CHAIN_ID=$(cast chain-id -r "$FROM_RPC_URL" 2>&1); then
+  echo "Error: cast chain-id failed for FROM_RPC_URL ($FROM_RPC_URL)"
+  exit 1
+fi
+if ! TO_CHAIN_ID=$(cast chain-id -r "$TO_RPC_URL" 2>&1); then
+  echo "Error: cast chain-id failed for TO_RPC_URL ($TO_RPC_URL)"
+  exit 1
+fi

155-169: Fail fast when approval submission fails.

cast send can error (RPC outage, bad nonce, insufficient funds). Right now you pipe the failure text through jq, end up with an empty hash, and only then shout “Approval transaction failed” without the root cause. Capture the exit status and dump the raw output so we know exactly why the approval failed.

-APPROVE_TX_OUTPUT=$(cast send "$FROM_TOKEN" \
+if ! APPROVE_TX_OUTPUT=$(cast send "$FROM_TOKEN" \
   "approve(address,uint256)(bool)" \
   "$TX_TO" \
   "$APPROVAL_AMOUNT" \
   --private-key "$PRIVATE_KEY" \
   --rpc-url "$FROM_RPC_URL" \
-  --json)
+  --json); then
+  echo "Error: Approval transaction submission failed"
+  echo "$APPROVE_TX_OUTPUT"
+  exit 1
+fi

185-199: Guard against failed bridge transaction submissions.

Same story for the main bridge tx: if cast send blows up, jq returns null and you log a blank hash. Grab the exit status, surface the raw error, and stop before proceeding to receipt polling.

-TX_OUTPUT=$(cast send "$TX_TO" \
+if ! TX_OUTPUT=$(cast send "$TX_TO" \
   --private-key "$PRIVATE_KEY" \
   --gas-limit "$TX_GAS_LIMIT" \
   --rpc-url "$FROM_RPC_URL" \
   --json \
-  "$TX_DATA")
+  "$TX_DATA"); then
+  echo "Error: Bridge transaction submission failed"
+  echo "$TX_OUTPUT"
+  exit 1
+fi
script/demoScripts/PolymerCCTP.s.sol (2)

1-2: Use the mandated SPDX identifier.

Project policy requires // SPDX-License-Identifier: LGPL-3.0-only on every Solidity source (scripts included). Please update the header accordingly.


37-48: Pass an actual destination chain ID, not the CCTP domain.

ILiFi.BridgeData.destinationChainId is consumed by analytics, routing validation, and downstream tooling expecting the real EVM chain ID. Feeding the CCTP domain (e.g., 3/6) will break those checks. Keep the domain in Polymer-specific data (or pull it from env separately), but leave destinationChainId as the target chain’s numeric chain ID.

-            destinationChainId: uint256(destinationDomain), // Using domain as chain ID to avoid SLOAD to read from a mapping - this seems like it can just directly be passed via calldata.
+            destinationChainId: vm.envUint("DESTINATION_CHAIN_ID"),

…and pass destinationDomain through your Polymer-specific struct.

src/Interfaces/IPolymerCCTP.sol (2)

1-2: Align SPDX license with repo policy.

Interfaces must carry // SPDX-License-Identifier: LGPL-3.0-only. Please swap out the MIT identifier to stay compliant.


13-23: Add the required interface NatSpec header.

Coding guidelines mandate @title, @notice, @author LI.FI (https://li.fi), and @custom:version X.Y.Z on every interface. Please add the header above IPolymerCCTPFacet so it conforms. Based on coding guidelines

test/solidity/Facets/PolymerCCTPFacet.t.sol (1)

1-2: Use the LGPL SPDX in tests.

All first-party Solidity (tests included) must use // SPDX-License-Identifier: LGPL-3.0-only. Please update the header.

📜 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 3cf3af9 and ddfd4b5.

📒 Files selected for processing (6)
  • script/demoScripts/CallPolymerCCTP.sh (1 hunks)
  • script/demoScripts/PolymerCCTP.s.sol (1 hunks)
  • script/demoScripts/demoPolymer.sh (1 hunks)
  • src/Facets/PolymerCCTPFacet.sol (1 hunks)
  • src/Interfaces/IPolymerCCTP.sol (1 hunks)
  • test/solidity/Facets/PolymerCCTPFacet.t.sol (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/Facets/PolymerCCTPFacet.sol
🧰 Additional context used
📓 Path-based instructions (7)
script/**/*.sh

📄 CodeRabbit inference engine (conventions.md)

script/**/*.sh: Bash scripts must start with #!/bin/bash, be modular with sections (Logging, Error handling and logging, Deployment functions), follow DRY via helpers, and organize core operations into functions
Bash style: consistent indentation/naming, comments, usage instructions, and TODO/limitations documented
Bash error handling and logging: use helper logging functions, validate inputs early, check exit status with checkFailure, and use set -e where appropriate

Files:

  • script/demoScripts/CallPolymerCCTP.sh
  • script/demoScripts/demoPolymer.sh
{script/**/*.sh,preinstall.sh}

📄 CodeRabbit inference engine (conventions.md)

Environment: load from .env or config.sh, declare globals in config, update .env.example, validate env vars early; add system packages to preinstall.sh

Files:

  • script/demoScripts/CallPolymerCCTP.sh
  • script/demoScripts/demoPolymer.sh
{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:

  • script/demoScripts/PolymerCCTP.s.sol
  • test/solidity/Facets/PolymerCCTPFacet.t.sol
  • src/Interfaces/IPolymerCCTP.sol
**/*.sol

📄 CodeRabbit inference engine (conventions.md)

Follow solhint rules as configured in .solhint.json

Files:

  • script/demoScripts/PolymerCCTP.s.sol
  • test/solidity/Facets/PolymerCCTPFacet.t.sol
  • src/Interfaces/IPolymerCCTP.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/Facets/PolymerCCTPFacet.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/IPolymerCCTP.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/IPolymerCCTP.sol
🧠 Learnings (78)
📓 Common learnings
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : Apply required modifiers where appropriate: nonReentrant, refundExcessNative, validateBridgeData, doesNotContainSourceSwaps/doesContainSourceSwaps, doesNotContainDestinationCalls/doesContainDestinationCalls
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 782
File: script/deploy/_targetState.json:143-143
Timestamp: 2024-11-25T06:25:01.232Z
Learning: Contracts like `OpBNBBridgeFacet` may be in development and only available in feature branches, resulting in missing source files in the main branch. Before flagging such missing contracts, check for open PRs that might include them.
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 782
File: script/deploy/_targetState.json:49-49
Timestamp: 2024-11-25T09:05:03.917Z
Learning: The `RelayFacet` contract, when missing from the source code but referenced in deployment configurations, should be treated the same way as `OpBNBBridgeFacet` and can be ignored in code reviews.
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : Facet contracts must be located in src/Facets and include "Facet" in the contract name
📚 Learning: 2024-10-09T03:47:21.269Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 715
File: script/helperFunctions.sh:3516-3529
Timestamp: 2024-10-09T03:47:21.269Z
Learning: For user 0xDEnYO, avoid reporting SC2155 (Shellcheck warning about declaring and assigning variables in a single line) in future reviews.

Applied to files:

  • script/demoScripts/CallPolymerCCTP.sh
📚 Learning: 2025-04-17T02:39:00.528Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1102
File: script/tasks/diamondSyncSigs_FAST.sh:68-69
Timestamp: 2025-04-17T02:39:00.528Z
Learning: Do not suggest separating variable declaration and assignment in bash scripts (Shellcheck SC2155) in the lifinance/contracts repository. The team has confirmed this pattern is acceptable and preferred in their codebase.

Applied to files:

  • script/demoScripts/CallPolymerCCTP.sh
📚 Learning: 2025-06-05T10:00:01.583Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1145
File: script/tasks/diamondSyncWhitelistedAddresses.sh:208-209
Timestamp: 2025-06-05T10:00:01.583Z
Learning: Task scripts in script/tasks/ directory (like diamondSyncWhitelistedAddresses.sh) define functions that are sourced by deployAllContracts.sh and called from there, rather than executing directly. They don't need to call their own functions at the end of the file.

Applied to files:

  • script/demoScripts/CallPolymerCCTP.sh
📚 Learning: 2024-11-05T17:15:33.833Z
Learnt from: maxklenk
Repo: lifinance/contracts PR: 782
File: script/demoScripts/demoPermit2.ts:100-107
Timestamp: 2024-11-05T17:15:33.833Z
Learning: In demo scripts using testing wallets, passing private keys via command-line arguments is acceptable.

Applied to files:

  • script/demoScripts/demoPolymer.sh
📚 Learning: 2024-11-05T17:14:24.059Z
Learnt from: maxklenk
Repo: lifinance/contracts PR: 782
File: script/demoScripts/demoPermit2.ts:119-124
Timestamp: 2024-11-05T17:14:24.059Z
Learning: In the file `script/demoScripts/demoPermit2.ts`, adding error handling for external API calls is not preferred as it may overload the demo.

Applied to files:

  • script/demoScripts/demoPolymer.sh
📚 Learning: 2024-10-04T09:21:59.708Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 819
File: deployments/polygon.json:0-0
Timestamp: 2024-10-04T09:21:59.708Z
Learning: Ensure that scripts used for codebase verification produce valid and accurate results before reporting issues, especially when checking Ethereum address checksums in `deployments/polygon.json`.

Applied to files:

  • script/demoScripts/demoPolymer.sh
📚 Learning: 2025-08-07T06:34:07.709Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1316
File: script/deploy/deployAllContracts.sh:346-350
Timestamp: 2025-08-07T06:34:07.709Z
Learning: In script/deploy/deployAllContracts.sh, 0xDEnYO prefers not to add automated retry mechanisms for cast send commands in deployment scripts, preferring manual re-execution if network issues occur. This prioritizes simplicity over automated error handling.

Applied to files:

  • script/demoScripts/demoPolymer.sh
📚 Learning: 2025-09-12T11:48:46.333Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1377
File: deployments/mainnet.json:7-7
Timestamp: 2025-09-12T11:48:46.333Z
Learning: When running verification scripts for contract deployments, ensure the scripts are syntactically correct and thoroughly tested before making claims about contract validity. False negatives from faulty scripts can incorrectly flag valid deployments as problematic.

Applied to files:

  • script/demoScripts/demoPolymer.sh
📚 Learning: 2025-06-06T16:09:58.692Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1193
File: script/tasks/diamondSyncWhitelistedAddresses.sh:128-128
Timestamp: 2025-06-06T16:09:58.692Z
Learning: In script/tasks/diamondSyncWhitelistedAddresses.sh at line 128, there is an unquoted command substitution `$(getPrivateKey "$NETWORK" "$ENVIRONMENT")` that should be quoted as `"$(getPrivateKey "$NETWORK" "$ENVIRONMENT")"` to prevent word splitting issues. The user mirooon wants to be reminded about this issue in future PRs when this file is touched.

Applied to files:

  • script/demoScripts/demoPolymer.sh
📚 Learning: 2024-10-14T00:49:45.265Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 782
File: deployments/polygon.json:53-55
Timestamp: 2024-10-14T00:49:45.265Z
Learning: Use 'cast code ADDRESS --rpc-url <RPC-URL>' from the Foundry framework to check if a contract has bytecode deployed, to avoid false negatives.

Applied to files:

  • script/demoScripts/demoPolymer.sh
📚 Learning: 2025-06-06T16:10:04.674Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1193
File: script/tasks/diamondSyncWhitelistedAddresses.sh:97-97
Timestamp: 2025-06-06T16:10:04.674Z
Learning: In script/tasks/diamondSyncWhitelistedAddresses.sh (lines 97 and 133), there are shell array assignments using command substitution that should be fixed using mapfile instead: `WHITELISTED_ADDRESSES=($(getApprovedWhitelistedAddresses))` should be `mapfile -t WHITELISTED_ADDRESSES < <(getApprovedWhitelistedAddresses)` and similarly for `WHITELISTED_ADDRESSES_UPDATED`. User mirooon requested to be reminded of this issue in future PRs when this file is modified.

Applied to files:

  • script/demoScripts/demoPolymer.sh
📚 Learning: 2025-02-20T04:50:33.488Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1015
File: script/deploy/safe/add-safe-owners-and-threshold.ts:117-123
Timestamp: 2025-02-20T04:50:33.488Z
Learning: The `submitAndExecuteTransaction` function in Safe management scripts includes comprehensive error handling that covers transaction proposal, execution, and receipt verification. It uses try-catch blocks, verifies transaction receipt status, logs errors, and properly propagates them up the call stack, making additional error handling around its calls unnecessary.

Applied to files:

  • script/demoScripts/demoPolymer.sh
📚 Learning: 2025-06-24T07:47:41.478Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1232
File: script/deploy/deployAllContracts.sh:0-0
Timestamp: 2025-06-24T07:47:41.478Z
Learning: In script/deploy/deployAllContracts.sh, the string comparison for BALANCE checking (`"$BALANCE" == "0"`) works fine as-is according to the maintainer and doesn't need to be changed to numeric comparison.

Applied to files:

  • script/demoScripts/demoPolymer.sh
📚 Learning: 2025-08-29T11:53:38.549Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 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:

  • script/demoScripts/demoPolymer.sh
📚 Learning: 2024-10-10T03:18:20.721Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 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:

  • script/demoScripts/demoPolymer.sh
📚 Learning: 2025-08-20T09:56:24.259Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1327
File: docs/OutputValidator.md:23-29
Timestamp: 2025-08-20T09:56:24.259Z
Learning: In src/Periphery/OutputValidator.sol, the ERC20 token flow that uses balanceOf(msg.sender) to compute actualAmount and transfers the excess via safeTransferFrom is intentional behavior, as confirmed by 0xDEnYO. The design choice to potentially include pre-existing ERC20 balances in the excess calculation is by design and should not be flagged as a security concern in future reviews.

Applied to files:

  • script/demoScripts/demoPolymer.sh
📚 Learning: 2024-11-25T13:49:40.464Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 782
File: script/deploy/zksync/DeployPermit2Proxy.s.sol:22-61
Timestamp: 2024-11-25T13:49:40.464Z
Learning: In the deploy scripts (e.g., `script/deploy/zksync/DeployPermit2Proxy.s.sol`), complex error handling and address validation are not necessary. The team prefers to keep deploy scripts simple without extensive error handling.

Applied to files:

  • script/demoScripts/demoPolymer.sh
📚 Learning: 2025-02-20T04:50:33.488Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1015
File: script/deploy/safe/add-safe-owners-and-threshold.ts:117-123
Timestamp: 2025-02-20T04:50:33.488Z
Learning: In the Safe owners management scripts, error handling for transaction submission is implemented within the `submitAndExecuteTransaction` function, making additional try-catch blocks around its calls unnecessary.

Applied to files:

  • script/demoScripts/demoPolymer.sh
📚 Learning: 2024-09-23T01:42:03.075Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 807
File: script/deploy/facets/DeployGasZipFacet.s.sol:22-35
Timestamp: 2024-09-23T01:42:03.075Z
Learning: In deployment scripts like `DeployGasZipFacet.s.sol`, do not report issues suggesting adding error handling for missing configuration files or unexpected JSON structures, as the script will fail if the file is missing.

Applied to files:

  • script/demoScripts/demoPolymer.sh
📚 Learning: 2024-10-31T09:09:38.568Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 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:

  • script/demoScripts/PolymerCCTP.s.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 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:

  • script/demoScripts/PolymerCCTP.s.sol
  • test/solidity/Facets/PolymerCCTPFacet.t.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to script/deploy/facets/Deploy*.s.sol : Deployment scripts for facets must be named Deploy{ContractName}.s.sol and inherit DeployScriptBase; deploy using deploy(type(ContractName).creationCode); omit getConstructorArgs() when not needed

Applied to files:

  • script/demoScripts/PolymerCCTP.s.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : Apply required modifiers where appropriate: nonReentrant, refundExcessNative, validateBridgeData, doesNotContainSourceSwaps/doesContainSourceSwaps, doesNotContainDestinationCalls/doesContainDestinationCalls

Applied to files:

  • script/demoScripts/PolymerCCTP.s.sol
  • test/solidity/Facets/PolymerCCTPFacet.t.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-10-02T18:14:45.047Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1406
File: script/deploy/facets/UpdateUnitFacet.s.sol:1-3
Timestamp: 2025-10-02T18:14:45.047Z
Learning: For update scripts in script/deploy/facets/, the SPDX license identifier should be immediately followed by the pragma statement without a blank line in between, as confirmed by mirooon for script/deploy/facets/UpdateUnitFacet.s.sol.

Applied to files:

  • script/demoScripts/PolymerCCTP.s.sol
  • test/solidity/Facets/PolymerCCTPFacet.t.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-08-29T14:06:33.384Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: script/deploy/facets/LDA/UpdateUniV2StyleFacet.s.sol:1-2
Timestamp: 2025-08-29T14:06:33.384Z
Learning: For LDA deployment and update scripts in script/deploy/facets/LDA/, the SPDX license identifier should be immediately followed by the pragma statement without a blank line in between, as confirmed by mirooon for script/deploy/facets/LDA/UpdateUniV2StyleFacet.s.sol.

Applied to files:

  • script/demoScripts/PolymerCCTP.s.sol
  • test/solidity/Facets/PolymerCCTPFacet.t.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-08-29T14:06:01.072Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: script/deploy/facets/LDA/UpdateCurveFacet.s.sol:1-2
Timestamp: 2025-08-29T14:06:01.072Z
Learning: For script/deploy/facets/LDA/UpdateCurveFacet.s.sol and similar LDA update scripts, the SPDX license identifier should be immediately followed by the pragma statement without requiring a blank line in between, as confirmed by mirooon.

Applied to files:

  • script/demoScripts/PolymerCCTP.s.sol
  • test/solidity/Facets/PolymerCCTPFacet.t.sol
📚 Learning: 2025-08-29T14:05:37.002Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: script/deploy/facets/LDA/DeployLDAOwnershipFacet.s.sol:1-3
Timestamp: 2025-08-29T14:05:37.002Z
Learning: For deployment script files in script/deploy/facets/LDA/, the SPDX license identifier should be immediately followed by the pragma statement without a blank line in between, as confirmed by mirooon for script/deploy/facets/LDA/DeployLDAOwnershipFacet.s.sol.

Applied to files:

  • script/demoScripts/PolymerCCTP.s.sol
  • test/solidity/Facets/PolymerCCTPFacet.t.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-09-25T00:05:33.977Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1395
File: src/Periphery/FeeForwarder.sol:1-2
Timestamp: 2025-09-25T00:05:33.977Z
Learning: In the lifinance/contracts repository, the established convention is that SPDX license identifiers should be immediately followed by the pragma statement without a blank line in between. This applies to all Solidity files across src/, test/, and script/ directories, as confirmed by 0xDEnYO and verified by codebase analysis.

Applied to files:

  • script/demoScripts/PolymerCCTP.s.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to {src,test/solidity,script}/**/*.sol : Use SPDX license identifier // SPDX-License-Identifier: LGPL-3.0-only for all first-party Solidity files

Applied to files:

  • script/demoScripts/PolymerCCTP.s.sol
  • test/solidity/Facets/PolymerCCTPFacet.t.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-08-29T14:05:31.360Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: script/deploy/facets/LDA/DeployLDAEmergencyPauseFacet.s.sol:1-2
Timestamp: 2025-08-29T14:05:31.360Z
Learning: For LDA deployment scripts in script/deploy/facets/LDA/, the SPDX license identifier should be immediately followed by the pragma statement without requiring a blank line in between, as confirmed by mirooon.

Applied to files:

  • script/demoScripts/PolymerCCTP.s.sol
📚 Learning: 2025-08-27T22:29:51.839Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: test/solidity/Periphery/Lda/Facets/SyncSwapV2Facet.t.sol:1-2
Timestamp: 2025-08-27T22:29:51.839Z
Learning: Test files in test/solidity/**/*.t.sol should use the LGPL-3.0-only SPDX license identifier, not Unlicense.

Applied to files:

  • script/demoScripts/PolymerCCTP.s.sol
  • test/solidity/Facets/PolymerCCTPFacet.t.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-09-24T23:55:07.082Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1395
File: script/deploy/facets/DeployFeeForwarder.s.sol:1-1
Timestamp: 2025-09-24T23:55:07.082Z
Learning: All Solidity files in the lifinance/contracts repository, including deployment scripts in script/deploy/, must use the SPDX license identifier "// SPDX-License-Identifier: LGPL-3.0-only" as specified in conventions.md. This applies to all .sol files in src/, test/solidity/, and script/ directories.

Applied to files:

  • script/demoScripts/PolymerCCTP.s.sol
  • test/solidity/Facets/PolymerCCTPFacet.t.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-08-29T14:05:58.427Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: script/deploy/facets/LDA/DeployLDAPeripheryRegistryFacet.s.sol:1-3
Timestamp: 2025-08-29T14:05:58.427Z
Learning: For deployment scripts in script/deploy/facets/LDA/, the SPDX license identifier should be immediately followed by the pragma statement without a blank line in between, following the same convention as other LDA-related files, as confirmed by mirooon for script/deploy/facets/LDA/DeployLDAPeripheryRegistryFacet.s.sol.

Applied to files:

  • script/demoScripts/PolymerCCTP.s.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : In {facetName}Data, receiverAddress must be the first field and be validated against bridgeData.receiver; verify targetChainId equals bridgeData.destinationChain for EVM-to-EVM

Applied to files:

  • script/demoScripts/PolymerCCTP.s.sol
  • test/solidity/Facets/PolymerCCTPFacet.t.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-07-16T01:03:08.106Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 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:

  • script/demoScripts/PolymerCCTP.s.sol
  • test/solidity/Facets/PolymerCCTPFacet.t.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : For non-EVM chains: receiverAddress must be bytes (not bytes32), must not be zero, and bridgeData.receiver must equal NON_EVM_ADDRESS

Applied to files:

  • script/demoScripts/PolymerCCTP.s.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 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:

  • script/demoScripts/PolymerCCTP.s.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-07-17T11:31:50.058Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1283
File: config/networks.json:837-839
Timestamp: 2025-07-17T11:31:50.058Z
Learning: In config/networks.json for the Ronin network (chainId 2020), there is a problematic mismatch between verificationType ("blockscout") and explorerApiUrl ("https://sourcify.roninchain.com/server"). This inconsistency can lead to verification issues and should be flagged when networks.json is modified in future PRs.

Applied to files:

  • script/demoScripts/PolymerCCTP.s.sol
📚 Learning: 2025-05-14T16:28:30.376Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1137
File: src/Periphery/GasZipPeriphery.sol:21-23
Timestamp: 2025-05-14T16:28:30.376Z
Learning: When shifting by 16 bits per chain ID in the GasZip contracts, the MAX_CHAINID_LENGTH_ALLOWED constant should be set to 16 (since a uint256 can hold at most 16 chain IDs when using 16 bits per chain). This constant should be consistent between related contracts like GasZipPeriphery and GasZipFacet.

Applied to files:

  • script/demoScripts/PolymerCCTP.s.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to script/**/*.ts : Always use proper TypeChain types (e.g., ILiFi.BridgeDataStruct) and never use any for bridge data or contract structures

Applied to files:

  • script/demoScripts/PolymerCCTP.s.sol
📚 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:

  • script/demoScripts/PolymerCCTP.s.sol
📚 Learning: 2025-08-29T11:33:37.368Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: test/solidity/Periphery/LDA/BaseCoreRouteTest.t.sol:1-3
Timestamp: 2025-08-29T11:33:37.368Z
Learning: For test files in test/solidity/Periphery/LDA/**, the SPDX license identifier should be immediately followed by the pragma statement without a blank line in between, as confirmed by mirooon for test/solidity/Periphery/LDA/BaseCoreRouteTest.t.sol.

Applied to files:

  • script/demoScripts/PolymerCCTP.s.sol
  • test/solidity/Facets/PolymerCCTPFacet.t.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-08-29T11:18:56.656Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Periphery/LDA/Facets/NativeWrapperFacet.sol:1-2
Timestamp: 2025-08-29T11:18:56.656Z
Learning: For src/Periphery/LDA/Facets/NativeWrapperFacet.sol, the SPDX license identifier should be immediately followed by the pragma statement without a blank line in between, as confirmed by mirooon.

Applied to files:

  • script/demoScripts/PolymerCCTP.s.sol
  • test/solidity/Facets/PolymerCCTPFacet.t.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-07-17T11:28:17.454Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1283
File: src/Interfaces/KatanaV3/IKatanaV3AggregateRouter.sol:1-2
Timestamp: 2025-07-17T11:28:17.454Z
Learning: In the lifinance/contracts repository, all Solidity files consistently use `pragma solidity ^0.8.17;` for version consistency across the codebase, even though foundry.toml specifies a different version. This is the preferred approach for maintaining consistency.

Applied to files:

  • script/demoScripts/PolymerCCTP.s.sol
📚 Learning: 2025-10-02T18:14:51.385Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1406
File: src/Facets/UnitFacet.sol:1-2
Timestamp: 2025-10-02T18:14:51.385Z
Learning: For facet contracts in src/Facets/, the SPDX license identifier should be immediately followed by the pragma statement without a blank line in between, as confirmed by mirooon for src/Facets/UnitFacet.sol.

Applied to files:

  • script/demoScripts/PolymerCCTP.s.sol
  • test/solidity/Facets/PolymerCCTPFacet.t.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-07-03T01:44:43.968Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1237
File: conventions.md:56-60
Timestamp: 2025-07-03T01:44:43.968Z
Learning: In the lifinance/contracts codebase, use floating pragma directives (e.g., `pragma solidity ^0.8.17;`) rather than locking to exact versions. This allows the contracts to support multiple solc versions as preferred by the team.

Applied to files:

  • script/demoScripts/PolymerCCTP.s.sol
📚 Learning: 2025-08-20T18:05:14.748Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: test/solidity/Periphery/Lda/Facets/VelodromeV2Facet.t.sol:68-75
Timestamp: 2025-08-20T18:05:14.748Z
Learning: In the lifinance/contracts repository, all Solidity files consistently use `pragma solidity ^0.8.17;` as the standard version, regardless of what is configured in foundry.toml. This is an explicit team preference that should be maintained across all files.

Applied to files:

  • script/demoScripts/PolymerCCTP.s.sol
📚 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:

  • script/demoScripts/PolymerCCTP.s.sol
📚 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:

  • script/demoScripts/PolymerCCTP.s.sol
📚 Learning: 2024-11-22T08:49:35.205Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 827
File: src/Facets/DeBridgeDlnFacet.sol:0-0
Timestamp: 2024-11-22T08:49:35.205Z
Learning: In `DeBridgeDlnFacet.sol`, direct access to the `deBridgeChainId` mapping is acceptable, and using the `getDeBridgeChainId` function is not necessary in these instances.

Applied to files:

  • script/demoScripts/PolymerCCTP.s.sol
📚 Learning: 2025-02-17T07:59:54.979Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 975
File: test/solidity/Facets/DeBridgeDlnFacet.t.sol:0-0
Timestamp: 2025-02-17T07:59:54.979Z
Learning: In DeBridgeDlnFacet, chain ID mappings are hardcoded and not designed to be dynamically updated, so tests for mapping updates are not needed.

Applied to files:

  • script/demoScripts/PolymerCCTP.s.sol
📚 Learning: 2025-02-24T09:35:34.908Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 984
File: src/Periphery/ReceiverChainflip.sol:48-62
Timestamp: 2025-02-24T09:35:34.908Z
Learning: Contract address validations for periphery contracts like ReceiverChainflip are handled in deployment scripts rather than in the constructor.

Applied to files:

  • script/demoScripts/PolymerCCTP.s.sol
📚 Learning: 2025-02-14T09:37:32.647Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 984
File: src/Facets/ChainflipFacet.sol:50-56
Timestamp: 2025-02-14T09:37:32.647Z
Learning: ChainflipData struct in ChainflipFacet.sol does not require additional field validation as confirmed by the team.

Applied to files:

  • script/demoScripts/PolymerCCTP.s.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2024-10-09T03:47:21.269Z
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`).

Applied to files:

  • script/demoScripts/PolymerCCTP.s.sol
📚 Learning: 2025-08-27T23:36:40.773Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 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:

  • script/demoScripts/PolymerCCTP.s.sol
📚 Learning: 2025-10-02T18:10:09.934Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1406
File: src/Facets/UnitFacet.sol:75-81
Timestamp: 2025-10-02T18:10:09.934Z
Learning: In UnitFacet.sol and similar facet contracts in src/Facets/, the LiFiTransferStarted event emission should ALWAYS be at the end of the _startBridge internal function, after all validations and asset transfers have been completed successfully.

Applied to files:

  • script/demoScripts/PolymerCCTP.s.sol
  • test/solidity/Facets/PolymerCCTPFacet.t.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : Emit LiFiTransferStarted at the start of a transaction before external calls; do not emit LiFiTransferCompleted or LiFiTransferRecovered in facets

Applied to files:

  • script/demoScripts/PolymerCCTP.s.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : For native fees in facets, use the _depositAndSwap variant and reserve required fees before execution

Applied to files:

  • script/demoScripts/PolymerCCTP.s.sol
📚 Learning: 2025-07-16T01:04:55.857Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1275
File: src/Facets/AllBridgeFacet.sol:186-188
Timestamp: 2025-07-16T01:04:55.857Z
Learning: In LiFi facet contracts, when public entry point functions have `nonReentrant` modifier protection, internal functions like `_startBridge` that they call benefit from this reentrancy protection, making event emission order after external calls acceptable from a security perspective.

Applied to files:

  • script/demoScripts/PolymerCCTP.s.sol
📚 Learning: 2025-01-22T12:36:12.699Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 945
File: test/solidity/Facets/GlacisFacet.t.sol:214-262
Timestamp: 2025-01-22T12:36:12.699Z
Learning: The GlacisFacet test suite inherits from TestBaseFacet which already covers various failure scenarios including invalid receiver address, invalid amounts, same chain bridging, and insufficient funds, making additional failure scenario tests redundant.

Applied to files:

  • test/solidity/Facets/PolymerCCTPFacet.t.sol
📚 Learning: 2025-08-28T02:41:07.505Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1321
File: docs/RelayDepositoryFacet.md:9-10
Timestamp: 2025-08-28T02:41:07.505Z
Learning: Unit tests for RelayDepositoryFacet cannot verify fund forwarding behavior after deposits because the facet delegates to external IRelayDepository contracts. The forwarding logic is implemented in the Relay Protocol V2 Depository contracts, not in the facet itself.

Applied to files:

  • test/solidity/Facets/PolymerCCTPFacet.t.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : Facet contracts must be located in src/Facets and include "Facet" in the contract name

Applied to files:

  • test/solidity/Facets/PolymerCCTPFacet.t.sol
📚 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:

  • test/solidity/Facets/PolymerCCTPFacet.t.sol
  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2024-11-25T09:04:55.880Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 782
File: script/deploy/_targetState.json:143-143
Timestamp: 2024-11-25T09:04:55.880Z
Learning: Errors about the missing `OpBNBBridgeFacet` contract are expected when it is referenced in the target state but not yet merged into the main branch.

Applied to files:

  • test/solidity/Facets/PolymerCCTPFacet.t.sol
📚 Learning: 2025-02-21T09:05:22.118Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 984
File: test/solidity/Facets/ChainflipFacet.t.sol:269-269
Timestamp: 2025-02-21T09:05:22.118Z
Learning: Fixed gas amounts in test files (e.g., ChainflipFacet.t.sol) don't require extensive documentation or configuration as they are only used for testing purposes.

Applied to files:

  • test/solidity/Facets/PolymerCCTPFacet.t.sol
📚 Learning: 2025-01-28T11:29:09.566Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 945
File: test/solidity/Facets/GlacisFacet.t.sol:0-0
Timestamp: 2025-01-28T11:29:09.566Z
Learning: Empty test methods with explanatory comments are acceptable in test classes when the feature being tested (e.g., native token bridging) is not supported by the implementation.

Applied to files:

  • test/solidity/Facets/PolymerCCTPFacet.t.sol
📚 Learning: 2025-08-29T11:07:57.743Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Periphery/LDA/Facets/CurveFacet.sol:87-91
Timestamp: 2025-08-29T11:07:57.743Z
Learning: In src/Periphery/LDA/Facets/CurveFacet.sol, modern Curve pools (isV2=true, representing V2/NG pools) should reject native tokenIn by adding an early revert check when LibAsset.isNativeAsset(tokenIn) is true, since ICurveV2 exchange functions are non-payable and cannot accept native ETH.

Applied to files:

  • test/solidity/Facets/PolymerCCTPFacet.t.sol
📚 Learning: 2025-08-27T13:07:58.254Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Periphery/Lda/Facets/CurveFacet.sol:0-0
Timestamp: 2025-08-27T13:07:58.254Z
Learning: The CurveFacet in src/Periphery/Lda/Facets/CurveFacet.sol has fundamental issues: it imports a non-existent ICurveLegacy interface, and attempts to send ETH to non-payable exchange functions in both ICurve and ICurveV2 interfaces. All current Curve interfaces are non-payable and cannot accept native ETH.

Applied to files:

  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-08-29T11:04:14.413Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Periphery/LDA/Errors/Errors.sol:1-7
Timestamp: 2025-08-29T11:04:14.413Z
Learning: For error files in src/Periphery/LDA/Errors/Errors.sol and similar error definition files, the file header format should keep NatSpec comments before the pragma statement due to solhint compiler requirements, rather than following the standard convention of SPDX → blank line → pragma → NatSpec.

Applied to files:

  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Interfaces/**/*.sol : Every interface must include NatSpec in this order: title, notice, author LI.FI (https://li.fi), custom:version X.Y.Z

Applied to files:

  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/**/*.sol : Every contract must include NatSpec header in this order: title, author LI.FI (https://li.fi), notice, custom:version X.Y.Z

Applied to files:

  • src/Interfaces/IPolymerCCTP.sol
📚 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:

  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-02-21T09:00:28.226Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 984
File: src/Facets/ChainflipFacet.sol:127-146
Timestamp: 2025-02-21T09:00:28.226Z
Learning: The `validateBridgeData` modifier in `Validatable.sol` already validates that the receiver address is not zero, making additional zero-address checks redundant in functions using this modifier.

Applied to files:

  • src/Interfaces/IPolymerCCTP.sol
📚 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:

  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/**/*.sol : All public/external functions must have NatSpec comments documenting purpose, params, and returns

Applied to files:

  • src/Interfaces/IPolymerCCTP.sol
📚 Learning: 2025-07-16T01:04:11.083Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1275
File: src/Facets/AllBridgeFacet.sol:175-175
Timestamp: 2025-07-16T01:04:11.083Z
Learning: In AllBridgeFacet.sol, zero fee validation is not required at the contract level because the backend system ensures that calldata and msg.value are properly aligned before transactions reach the contract.

Applied to files:

  • src/Interfaces/IPolymerCCTP.sol
📚 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:

  • src/Interfaces/IPolymerCCTP.sol
🧬 Code graph analysis (1)
script/demoScripts/CallPolymerCCTP.sh (1)
script/deploy/DeployPolymerContracts.sh (1)
  • usage (14-25)
🪛 Shellcheck (0.11.0)
script/demoScripts/demoPolymer.sh

[warning] 142-142: TX_CHAIN_ID appears unused. Verify use (or export if used externally).

(SC2034)


[warning] 146-146: POLYMER_TX_DATA appears unused. Verify use (or export if used externally).

(SC2034)

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

♻️ Duplicate comments (3)
src/Facets/PolymerCCTPFacet.sol (3)

14-94: Missing required facet functions per architectural guidelines.

Per coding guidelines, facet contracts must implement:

  • internal _startBridge - to contain the core bridging logic
  • swapAndStartBridgeTokensViaPolymerCCTP - to handle swap-then-bridge flows

Currently, the facet only has startBridgeTokensViaPolymerCCTP. The core logic should be extracted into an internal _startBridge function that can be called by both the direct bridge and swap-then-bridge entry points.

As per coding guidelines

Refactor the implementation:

  1. Extract the bridging logic from startBridgeTokensViaPolymerCCTP (lines 54-93) into an internal _startBridge function:
function _startBridge(
    ILiFi.BridgeData memory _bridgeData,
    PolymerCCTPData calldata _polymerData
) internal {
    // Move lines 54-93 here
}
  1. Add the swapAndStartBridgeTokensViaPolymerCCTP function:
function swapAndStartBridgeTokensViaPolymerCCTP(
    ILiFi.BridgeData memory _bridgeData,
    LibSwap.SwapData[] calldata _swapData,
    PolymerCCTPData calldata _polymerData
)
    external
    payable
    nonReentrant
    refundExcessNative(payable(msg.sender))
    validateBridgeData(_bridgeData)
    onlyAllowSourceToken(_bridgeData, usdc)
    containsSourceSwaps(_bridgeData)
    doesNotContainDestinationCalls(_bridgeData)
{
    _bridgeData.minAmount = _depositAndSwap(
        _bridgeData.transactionId,
        _bridgeData.minAmount,
        _swapData,
        payable(msg.sender),
        _polymerData.polymerTokenFee
    );
    _startBridge(_bridgeData, _polymerData);
}
  1. Update startBridgeTokensViaPolymerCCTP to call _startBridge.

Based on learnings


19-21: Immutables should use CONSTANT_CASE naming.

Per coding guidelines, immutable variables must use CONSTANT_CASE.

As per coding guidelines

Rename the immutables:

-    ITokenMessenger public immutable tokenMessenger;
-    address public immutable usdc;
-    address payable public immutable polymerFeeReceiver;
+    ITokenMessenger public immutable TOKEN_MESSENGER;
+    address public immutable USDC;
+    address payable public immutable POLYMER_FEE_RECEIVER;

And update all references in the constructor (lines 30-32) and throughout the function (lines 45, 60, 63, 66, 72).


66-93: Critical: Events emitted after external call and incorrect CCTP domain usage.

Multiple issues:

  1. Event emission order: Both PolymerCCTPFeeSent (line 78) and LiFiTransferStarted (lines 82-93) are emitted AFTER the external tokenMessenger.depositForBurn call (lines 66-76). Per coding guidelines, "Emit LiFiTransferStarted at the start of a transaction before external calls."

  2. Incorrect domain parameter: Line 68 casts _bridgeData.destinationChainId to uint32, but CCTP uses domain IDs that don't map 1:1 to chain IDs. The _polymerData.destinationDomain field should be used instead.

  3. Non-EVM receiver handling: Lines 69-71 use bytes32 for the non-EVM address, but per coding guidelines for non-EVM chains, "receiverAddress must be bytes (not bytes32)".

  4. TODO comment: Line 81 questions whether to emit the event. Yes, LiFiTransferStarted is required per guidelines.

As per coding guidelines

Apply these fixes:

         LibAsset.depositAsset(_bridgeData.sendingAssetId, _bridgeData.minAmount);
         LibAsset.transferERC20(usdc, polymerFeeReceiver, _polymerData.polymerTokenFee);
 
+        // Emit events before external calls
+        emit PolymerCCTPFeeSent(_bridgeData.minAmount, _polymerData.polymerTokenFee, _polymerData.minFinalityThreshold);
+        emit LiFiTransferStarted(BridgeData(
+                _bridgeData.transactionId,
+                _bridgeData.bridge,
+                _bridgeData.integrator,
+                _bridgeData.referrer,
+                _bridgeData.sendingAssetId,
+                _bridgeData.receiver,
+                _bridgeData.minAmount - _polymerData.polymerTokenFee,
+                _bridgeData.destinationChainId,
+                _bridgeData.hasSourceSwaps,
+                _bridgeData.hasDestinationCall
+            ));
+
         // TODO we don't need to use safe approve here?
         IERC20(usdc).approve(address(tokenMessenger), _bridgeData.minAmount - _polymerData.polymerTokenFee);
 
+        // Validate non-EVM receiver if applicable
+        if (_bridgeData.receiver == NON_EVM_ADDRESS && _polymerData.nonEvmAddress.length == 0) {
+            revert InvalidBridgeReceiver();
+        }
+
         // Need tocheck: can we just use destinationChainID as the normal chain id? and can we just mpass in min Amount as the amountT?
         tokenMessenger.depositForBurn(
             _bridgeData.minAmount - _polymerData.polymerTokenFee,
-            uint32(_bridgeData.destinationChainId),
+            _polymerData.destinationDomain,
             _bridgeData.receiver == NON_EVM_ADDRESS
                 ? _polymerData.nonEvmAddress
                 : bytes32(uint256(uint160(_bridgeData.receiver))),
             usdc,
             bytes32(0), // Unrestricted caller
             _polymerData.maxCCTPFee, // maxFee - 0 means no fee limit
             _polymerData.minFinalityThreshold // minFinalityThreshold - use default
         );
-
-        emit PolymerCCTPFeeSent(_bridgeData.minAmount, _polymerData.polymerTokenFee, _polymerData.minFinalityThreshold);
-
-        // Emit Li.Fi standard event
-        // TODO: Check - do we need to emit this event?
-        emit LiFiTransferStarted(BridgeData(
-                _bridgeData.transactionId,
-                _bridgeData.bridge,
-                _bridgeData.integrator,
-                _bridgeData.referrer,
-                _bridgeData.sendingAssetId,
-                _bridgeData.receiver,
-                _bridgeData.minAmount - _polymerData.polymerTokenFee,
-                _bridgeData.destinationChainId,
-                _bridgeData.hasSourceSwaps,
-                _bridgeData.hasDestinationCall
-            ));

Note: Ensure PolymerCCTPData.destinationDomain exists and nonEvmAddress is typed as bytes32 to match the CCTP interface requirements.

🧹 Nitpick comments (3)
src/Facets/PolymerCCTPFacet.sol (3)

23-24: Resolve or remove TODO comment.

The TODO comment about fee collector should be addressed before merging.

Do you want to implement fee collector logic, or should this TODO be removed if the current design is final?


49-52: Redundant validation: onlyAllowSourceToken modifier already enforces this.

The check at lines 50-52 is redundant because the onlyAllowSourceToken(_bridgeData, usdc) modifier (line 45) already validates that _bridgeData.sendingAssetId == usdc.

Remove the redundant check to reduce code duplication, or keep it for an explicit error message if preferred:

-        // TODO - is it worth validating the integrator and bridge from the bridgeData here?
-        if (_bridgeData.sendingAssetId != usdc) {
-            revert InvalidSendingAsset(_bridgeData.sendingAssetId, usdc);
-        }

58-58: Resolve TODO comment.

The TODO at line 58 questions whether the check is needed since it's always USDC. Since the onlyAllowSourceToken modifier already enforces this, the comment should be removed.

-        // TODO: Do we need this check if it's always going to be usdc?
         LibAsset.depositAsset(_bridgeData.sendingAssetId, _bridgeData.minAmount);
📜 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 ddfd4b5 and 2d66297.

📒 Files selected for processing (1)
  • src/Facets/PolymerCCTPFacet.sol (1 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/Facets/PolymerCCTPFacet.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/Facets/PolymerCCTPFacet.sol
src/Facets/**/*Facet.sol

📄 CodeRabbit inference engine (conventions.md)

src/Facets/**/*Facet.sol: Facet contracts must be located in src/Facets and include "Facet" in the contract name
Facet contracts must implement: internal _startBridge, swapAndStartBridgeTokensVia{FacetName}, and startBridgeTokensVia{FacetName}
Facet contracts must import/use: ILiFi, LibAsset, LibSwap, LibAllowList; and use security mixins ReentrancyGuard, SwapperV2, Validatable; ECDSA optional
Do not use msg.sender as refund address; require an explicit parameter for refund address
In {facetName}Data, receiverAddress must be the first field and be validated against bridgeData.receiver; verify targetChainId equals bridgeData.destinationChain for EVM-to-EVM
Apply required modifiers where appropriate: nonReentrant, refundExcessNative, validateBridgeData, doesNotContainSourceSwaps/doesContainSourceSwaps, doesNotContainDestinationCalls/doesContainDestinationCalls
Emit LiFiTransferStarted at the start of a transaction before external calls; do not emit LiFiTransferCompleted or LiFiTransferRecovered in facets
For native fees in facets, use the _depositAndSwap variant and reserve required fees before execution
For non-EVM chains: receiverAddress must be bytes (not bytes32), must not be zero, and bridgeData.receiver must equal NON_EVM_ADDRESS

Files:

  • src/Facets/PolymerCCTPFacet.sol
**/*.sol

📄 CodeRabbit inference engine (conventions.md)

Follow solhint rules as configured in .solhint.json

Files:

  • src/Facets/PolymerCCTPFacet.sol
🧠 Learnings (59)
📓 Common learnings
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : Apply required modifiers where appropriate: nonReentrant, refundExcessNative, validateBridgeData, doesNotContainSourceSwaps/doesContainSourceSwaps, doesNotContainDestinationCalls/doesContainDestinationCalls
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 782
File: script/deploy/_targetState.json:143-143
Timestamp: 2024-11-25T06:25:01.232Z
Learning: Contracts like `OpBNBBridgeFacet` may be in development and only available in feature branches, resulting in missing source files in the main branch. Before flagging such missing contracts, check for open PRs that might include them.
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 782
File: script/deploy/_targetState.json:49-49
Timestamp: 2024-11-25T09:05:03.917Z
Learning: The `RelayFacet` contract, when missing from the source code but referenced in deployment configurations, should be treated the same way as `OpBNBBridgeFacet` and can be ignored in code reviews.
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : Facet contracts must be located in src/Facets and include "Facet" in the contract name
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 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:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : Apply required modifiers where appropriate: nonReentrant, refundExcessNative, validateBridgeData, doesNotContainSourceSwaps/doesContainSourceSwaps, doesNotContainDestinationCalls/doesContainDestinationCalls

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : In {facetName}Data, receiverAddress must be the first field and be validated against bridgeData.receiver; verify targetChainId equals bridgeData.destinationChain for EVM-to-EVM

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 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:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-07-16T01:03:08.106Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 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/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-10-02T18:10:09.934Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1406
File: src/Facets/UnitFacet.sol:75-81
Timestamp: 2025-10-02T18:10:09.934Z
Learning: In UnitFacet.sol and similar facet contracts in src/Facets/, the LiFiTransferStarted event emission should ALWAYS be at the end of the _startBridge internal function, after all validations and asset transfers have been completed successfully.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2024-10-31T09:09:38.568Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 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:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : Emit LiFiTransferStarted at the start of a transaction before external calls; do not emit LiFiTransferCompleted or LiFiTransferRecovered in facets

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2024-11-25T09:04:55.880Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 782
File: script/deploy/_targetState.json:143-143
Timestamp: 2024-11-25T09:04:55.880Z
Learning: Errors about the missing `OpBNBBridgeFacet` contract are expected when it is referenced in the target state but not yet merged into the main branch.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : For non-EVM chains: receiverAddress must be bytes (not bytes32), must not be zero, and bridgeData.receiver must equal NON_EVM_ADDRESS

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-29T11:18:56.656Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Periphery/LDA/Facets/NativeWrapperFacet.sol:1-2
Timestamp: 2025-08-29T11:18:56.656Z
Learning: For src/Periphery/LDA/Facets/NativeWrapperFacet.sol, the SPDX license identifier should be immediately followed by the pragma statement without a blank line in between, as confirmed by mirooon.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-10-02T18:14:51.385Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1406
File: src/Facets/UnitFacet.sol:1-2
Timestamp: 2025-10-02T18:14:51.385Z
Learning: For facet contracts in src/Facets/, the SPDX license identifier should be immediately followed by the pragma statement without a blank line in between, as confirmed by mirooon for src/Facets/UnitFacet.sol.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-10-02T18:14:45.047Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1406
File: script/deploy/facets/UpdateUnitFacet.s.sol:1-3
Timestamp: 2025-10-02T18:14:45.047Z
Learning: For update scripts in script/deploy/facets/, the SPDX license identifier should be immediately followed by the pragma statement without a blank line in between, as confirmed by mirooon for script/deploy/facets/UpdateUnitFacet.s.sol.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 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:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-27T23:01:41.042Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Libraries/LibUniV3Logic.sol:4-5
Timestamp: 2025-08-27T23:01:41.042Z
Learning: In the lifinance/contracts repository, the pattern `import { IERC20, SafeERC20 } from "openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";` is correctly used throughout the codebase and works as expected. SafeERC20.sol re-exports IERC20 in modern OpenZeppelin versions, making this combined import valid. Do not suggest splitting these imports.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : Facet contracts must be located in src/Facets and include "Facet" in the contract name

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-29T14:06:33.384Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: script/deploy/facets/LDA/UpdateUniV2StyleFacet.s.sol:1-2
Timestamp: 2025-08-29T14:06:33.384Z
Learning: For LDA deployment and update scripts in script/deploy/facets/LDA/, the SPDX license identifier should be immediately followed by the pragma statement without a blank line in between, as confirmed by mirooon for script/deploy/facets/LDA/UpdateUniV2StyleFacet.s.sol.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-29T14:05:37.002Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: script/deploy/facets/LDA/DeployLDAOwnershipFacet.s.sol:1-3
Timestamp: 2025-08-29T14:05:37.002Z
Learning: For deployment script files in script/deploy/facets/LDA/, the SPDX license identifier should be immediately followed by the pragma statement without a blank line in between, as confirmed by mirooon for script/deploy/facets/LDA/DeployLDAOwnershipFacet.s.sol.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2024-11-25T06:25:01.232Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 782
File: script/deploy/_targetState.json:143-143
Timestamp: 2024-11-25T06:25:01.232Z
Learning: Contracts like `OpBNBBridgeFacet` may be in development and only available in feature branches, resulting in missing source files in the main branch. Before flagging such missing contracts, check for open PRs that might include them.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2024-11-25T09:05:03.917Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 782
File: script/deploy/_targetState.json:49-49
Timestamp: 2024-11-25T09:05:03.917Z
Learning: The `RelayFacet` contract, when missing from the source code but referenced in deployment configurations, should be treated the same way as `OpBNBBridgeFacet` and can be ignored in code reviews.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : For native fees in facets, use the _depositAndSwap variant and reserve required fees before execution

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-27T23:36:40.773Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 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/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-07-16T01:04:11.083Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1275
File: src/Facets/AllBridgeFacet.sol:175-175
Timestamp: 2025-07-16T01:04:11.083Z
Learning: In AllBridgeFacet.sol, zero fee validation is not required at the contract level because the backend system ensures that calldata and msg.value are properly aligned before transactions reach the contract.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2024-10-22T03:14:52.578Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 807
File: src/Facets/GasZipFacet.sol:53-53
Timestamp: 2024-10-22T03:14:52.578Z
Learning: In `src/Facets/GasZipFacet.sol`, when checking `msg.value`, the team prefers to use strict equality (`msg.value == _bridgeData.minAmount`) to save gas and avoid refunding excess Ether.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-29T11:53:38.549Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 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:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-02-24T09:35:34.908Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 984
File: src/Periphery/ReceiverChainflip.sol:48-62
Timestamp: 2025-02-24T09:35:34.908Z
Learning: Contract address validations for periphery contracts like ReceiverChainflip are handled in deployment scripts rather than in the constructor.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 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:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-02-21T09:00:28.226Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 984
File: src/Facets/ChainflipFacet.sol:127-146
Timestamp: 2025-02-21T09:00:28.226Z
Learning: The `validateBridgeData` modifier in `Validatable.sol` already validates that the receiver address is not zero, making additional zero-address checks redundant in functions using this modifier.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 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:

  • src/Facets/PolymerCCTPFacet.sol
📚 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:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-27T22:29:51.839Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: test/solidity/Periphery/Lda/Facets/SyncSwapV2Facet.t.sol:1-2
Timestamp: 2025-08-27T22:29:51.839Z
Learning: Test files in test/solidity/**/*.t.sol should use the LGPL-3.0-only SPDX license identifier, not Unlicense.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to {src,test/solidity,script}/**/*.sol : Use SPDX license identifier // SPDX-License-Identifier: LGPL-3.0-only for all first-party Solidity files

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-29T14:06:01.072Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: script/deploy/facets/LDA/UpdateCurveFacet.s.sol:1-2
Timestamp: 2025-08-29T14:06:01.072Z
Learning: For script/deploy/facets/LDA/UpdateCurveFacet.s.sol and similar LDA update scripts, the SPDX license identifier should be immediately followed by the pragma statement without requiring a blank line in between, as confirmed by mirooon.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-29T11:33:37.368Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: test/solidity/Periphery/LDA/BaseCoreRouteTest.t.sol:1-3
Timestamp: 2025-08-29T11:33:37.368Z
Learning: For test files in test/solidity/Periphery/LDA/**, the SPDX license identifier should be immediately followed by the pragma statement without a blank line in between, as confirmed by mirooon for test/solidity/Periphery/LDA/BaseCoreRouteTest.t.sol.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-29T11:11:35.958Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Periphery/LDA/Facets/SyncSwapV2Facet.sol:1-3
Timestamp: 2025-08-29T11:11:35.958Z
Learning: For LDA facet files in src/Periphery/LDA/Facets/, the SPDX license identifier can be immediately followed by the pragma statement without requiring a blank line in between, as confirmed by mirooon.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-27T23:01:41.042Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Libraries/LibUniV3Logic.sol:4-5
Timestamp: 2025-08-27T23:01:41.042Z
Learning: In OpenZeppelin v4.0+, SafeERC20.sol re-exports IERC20, so importing both IERC20 and SafeERC20 from "openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol" is valid and works correctly. Do not suggest splitting this import into separate files.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-27T23:01:41.042Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Libraries/LibUniV3Logic.sol:4-5
Timestamp: 2025-08-27T23:01:41.042Z
Learning: In OpenZeppelin v4.0+, SafeERC20.sol re-exports IERC20, so importing both IERC20 and SafeERC20 from "openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol" is valid and works correctly. Do not suggest splitting this import.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-27T23:01:41.042Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Libraries/LibUniV3Logic.sol:4-5
Timestamp: 2025-08-27T23:01:41.042Z
Learning: In modern OpenZeppelin versions (v4.0+), SafeERC20.sol re-exports IERC20, making the combined import `import { IERC20, SafeERC20 } from "openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";` completely valid and working. Do not suggest splitting these imports as they work correctly as a combined import.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-19T05:07:19.448Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 1303
File: script/deploy/tron/utils.ts:3-5
Timestamp: 2025-08-19T05:07:19.448Z
Learning: In the lifinance/contracts repository, consola and TronWeb should be imported as named exports: `import { consola } from 'consola'` and `import { TronWeb } from 'tronweb'`. Do not flag these as incorrect default export usage.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-19T05:07:19.448Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 1303
File: script/deploy/tron/utils.ts:3-5
Timestamp: 2025-08-19T05:07:19.448Z
Learning: In the lifinance/contracts repository, consola and TronWeb should be imported as named exports: `import { consola } from 'consola'` and `import { TronWeb } from 'tronweb'`. Do not flag these as incorrect default export usage. Both libraries provide named exports, not default exports.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-27T13:07:58.254Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Periphery/Lda/Facets/CurveFacet.sol:0-0
Timestamp: 2025-08-27T13:07:58.254Z
Learning: In the LiFi contracts codebase, ICurveLegacy interface is defined inline in src/Periphery/LiFiDEXAggregator.sol with a non-payable exchange function. The CurveFacet diff shows importing it from a separate file that doesn't exist, creating a compilation issue.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2024-09-23T02:04:16.323Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 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/Facets/PolymerCCTPFacet.sol
📚 Learning: 2024-10-09T03:47:21.269Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 807
File: src/Periphery/GasZipPeriphery.sol:4-14
Timestamp: 2024-10-09T03:47:21.269Z
Learning: In `GasZipPeriphery.sol`, `LibUtil` and `Validatable` are used, so ensure not to suggest their removal in future reviews.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to {src,test/solidity,script}/**/*.sol : 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

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-09-19T14:10:55.064Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 1324
File: src/Facets/EcoFacet.sol:68-71
Timestamp: 2025-09-19T14:10:55.064Z
Learning: In src/Facets/EcoFacet.sol, the team prefers to use msg.sender as the refund address for native tokens in the refundExcessNative modifier, contrary to the general guideline of requiring explicit refund parameters.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to {src,test/solidity,script}/**/*.sol : Error names must be descriptive PascalCase, use custom errors instead of revert() strings, and omit error messages for gas optimization

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-09-12T10:17:51.686Z
Learnt from: CR
Repo: lifinance/contracts PR: 0
File: conventions.md:0-0
Timestamp: 2025-09-12T10:17:51.686Z
Learning: Applies to src/Facets/**/*Facet.sol : Do not use msg.sender as refund address; require an explicit parameter for refund address

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 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:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-09-25T00:12:58.536Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1395
File: src/Periphery/FeeForwarder.sol:78-120
Timestamp: 2025-09-25T00:12:58.536Z
Learning: In src/Periphery/FeeForwarder.sol, the team intentionally uses address(this).balance for refunding remaining native tokens because the contract is designed to never hold funds and not collect dust, making it safe to return the entire balance to the caller.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-10-13T11:13:48.847Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1413
File: src/Facets/EverclearV2Facet.sol:75-96
Timestamp: 2025-10-13T11:13:48.847Z
Learning: In EverclearV2Facet (and all LiFi facets), the team standard is to use msg.sender as the refund address with the refundExcessNative modifier, not requiring an explicit refund address parameter.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-01-28T14:29:00.823Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 924
File: script/deploy/zksync/utils/UpdateScriptBase.sol:112-178
Timestamp: 2025-01-28T14:29:00.823Z
Learning: The suggestion to modify `buildDiamondCut` function in `UpdateScriptBase.sol` to handle selectors from multiple old facets differently was deemed unnecessary by the maintainer.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-29T11:07:57.743Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Periphery/LDA/Facets/CurveFacet.sol:87-91
Timestamp: 2025-08-29T11:07:57.743Z
Learning: In src/Periphery/LDA/Facets/CurveFacet.sol, modern Curve pools (isV2=true, representing V2/NG pools) should reject native tokenIn by adding an early revert check when LibAsset.isNativeAsset(tokenIn) is true, since ICurveV2 exchange functions are non-payable and cannot accept native ETH.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2024-12-02T06:33:33.309Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 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/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-02-21T09:00:28.226Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 984
File: src/Facets/ChainflipFacet.sol:127-146
Timestamp: 2025-02-21T09:00:28.226Z
Learning: The `validateBridgeData` modifier in `Validatable.sol` validates that the receiver address is not zero using `LibUtil.isZeroAddress`, making additional zero-address checks redundant in functions using this modifier.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-20T09:56:24.259Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1327
File: docs/OutputValidator.md:23-29
Timestamp: 2025-08-20T09:56:24.259Z
Learning: In src/Periphery/OutputValidator.sol, the ERC20 token flow that uses balanceOf(msg.sender) to compute actualAmount and transfers the excess via safeTransferFrom is intentional behavior, as confirmed by 0xDEnYO. The design choice to potentially include pre-existing ERC20 balances in the excess calculation is by design and should not be flagged as a security concern in future reviews.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-07-16T01:04:55.857Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1275
File: src/Facets/AllBridgeFacet.sol:186-188
Timestamp: 2025-07-16T01:04:55.857Z
Learning: In LiFi facet contracts, when public entry point functions have `nonReentrant` modifier protection, internal functions like `_startBridge` that they call benefit from this reentrancy protection, making event emission order after external calls acceptable from a security perspective.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2024-10-14T08:23:38.076Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 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/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-04-28T07:46:24.084Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 1116
File: src/Libraries/LibAsset.sol:102-108
Timestamp: 2025-04-28T07:46:24.084Z
Learning: In the LiFi contracts, the `LibAsset.transferFromERC20` function is intentionally designed to accept arbitrary `from` parameters, with security ensured through a whitelist mechanism that controls which contracts can call this function.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
🔇 Additional comments (1)
src/Facets/PolymerCCTPFacet.sol (1)

62-63: Resolve TODO about safe approve.

The TODO at line 62 questions whether safe approve is needed. Consider using LibAsset.maxApproveERC20 for consistency with the codebase, or resolve this TODO with a decision.

Is safe approval needed here? The standard practice in other facets varies, but explicit approval management is safer.

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

♻️ Duplicate comments (11)
script/demoScripts/demoPolymer.sh (1)

25-27: Cast exit-status checks, tx submission/receipt handling, and balance queries

These were previously flagged; please apply the fail-fast patterns across cast send/call/receipt and balance calls.

Also applies to: 155-173, 185-201, 204-208, 68-73, 262-264

script/demoScripts/PolymerCCTP.s.sol (1)

45-48: destinationChainId set to domain (semantic deviation)

This was discussed earlier; leaving a note to confirm final design choice with maintainers as it impacts analytics/integrations.

script/deploy/DeployPolymerContracts.sh (4)

13-24: Fix usage/help text to match required args

Align Usage and Example with actual 3 positional args and remove “[OPTIONS]”.

-    echo "Usage: $0 <RPC_URL> <PRIVATE_KEY> [OPTIONS]"
+    echo "Usage: $0 <RPC_URL> <PRIVATE_KEY> <ADDRESSES_FILE>"
@@
-    echo "  $0 https://arb-sepolia.g.alchemy.com/v2/KEY 0xYOURKEY 3"
+    echo "  $0 https://arb-sepolia.g.alchemy.com/v2/KEY 0xYOURKEY ./testnet-addresses.json"

10-10: Remove unused color variable (SC2034)

YELLOW is unused.

-YELLOW='\033[1;33m'

80-87: Fix copy/paste hint for polymer fee recipient key

Use polymerFeeRecipient key in the JSON example.

-        echo "  2. Add it to $ADDRESSES_FILE under \"$CHAIN_ID\": { \"usdc\": \"0x...\" }"
+        echo "  2. Add it to $ADDRESSES_FILE under \"$CHAIN_ID\": { \"polymerFeeRecipient\": \"0x...\" }"

133-133: Correct follow-up script path

Call script is under script/demoScripts, not script/deploy.

-echo -e "${BLUE}To call the facet, run: ./script/deploy/CallPolymerCCTP.sh <RPC_URL> <PRIVATE_KEY> <DESTINATION_DOMAIN> $ADDRESSES_FILE${NC}"
+echo -e "${BLUE}To call the facet, run: ./script/demoScripts/CallPolymerCCTP.sh <RPC_URL> <PRIVATE_KEY> <DESTINATION_DOMAIN> $ADDRESSES_FILE${NC}"
src/Interfaces/IPolymerCCTP.sol (2)

6-11: Add required NatSpec and align struct with interface guidelines

Add interface header and consider using bytes receiverAddress first, plus destinationDomain, to separate EVM chainId from CCTP domain. As per coding guidelines

+/// @title IPolymerCCTPFacet
+/// @notice Facet interface for bridging USDC via Polymer CCTP
+/// @author LI.FI (https://li.fi)
+/// @custom:version 1.0.0
 struct PolymerCCTPData {
-    uint256 polymerTokenFee;
-    uint256 maxCCTPFee;
-    bytes32 nonEvmAddress; // Should only be nonzero if submitting to a nonEvm chain
-    uint32 minFinalityThreshold;
+    bytes receiverAddress;      // first; bytes for non‑EVM, empty for EVM
+    uint32 destinationDomain;   // CCTP domain id
+    uint256 polymerTokenFee;
+    uint256 maxCCTPFee;
+    uint32 minFinalityThreshold;
 }

Also applies to: 13-23


1-2: Wrong SPDX for first-party interfaces

Switch to LGPL-3.0-only per repo standards.

-// SPDX-License-Identifier: MIT
+// SPDX-License-Identifier: LGPL-3.0-only
test/solidity/Facets/PolymerCCTPFacet.t.sol (1)

1-2: Use LGPL-3.0-only in tests

Update SPDX to project standard.

-// SPDX-License-Identifier: Unlicense
+// SPDX-License-Identifier: LGPL-3.0-only
script/deploy/facets/DeployDiamondWithPolymerCCTPFacet.s.sol (1)

1-2: Doesn’t follow facet deploy conventions (naming, base, deploy pattern)

Keep facet deploys focused: DeployPolymerCCTPFacet.s.sol inheriting DeployScriptBase; don’t (re)deploy the Diamond from facets/. This was raised earlier; consider splitting orchestration to a top-level script. Based on learnings

Also applies to: 8-9, 11-54

src/Facets/PolymerCCTPFacet.sol (1)

4-21: Duplicate/conflicting imports — won’t compile

Same modules imported twice. Keep a single, ordered import list.

-import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
-import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
-import {ILiFi} from "../Interfaces/ILiFi.sol";
-import {ITokenMessenger} from "../Interfaces/ITokenMessenger.sol";
-import {IPolymerCCTPFacet, PolymerCCTPData} from "../Interfaces/IPolymerCCTP.sol";
-import {LiFiData} from "../Helpers/LiFiData.sol";
-import {LibAsset} from "../Libraries/LibAsset.sol";
-import {LibUtil} from "../Libraries/LibUtil.sol";
-import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
-import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
-import {ILiFi} from "../Interfaces/ILiFi.sol";
-import {ITokenMessenger} from "../Interfaces/ITokenMessenger.sol";
-import {IPolymerCCTPFacet, PolymerCCTPData} from "../Interfaces/IPolymerCCTP.sol";
-import {LiFiData} from "../Helpers/LiFiData.sol";
-import {LibAsset} from "../Libraries/LibAsset.sol";
-import {LibSwap} from "../Libraries/LibSwap.sol";
-import { SwapperV2 } from "../Helpers/SwapperV2.sol";
-import {Validatable} from "../Helpers/Validatable.sol";
+import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
+import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
+import {ILiFi} from "../Interfaces/ILiFi.sol";
+import {ITokenMessenger} from "../Interfaces/ITokenMessenger.sol";
+import {IPolymerCCTPFacet, PolymerCCTPData} from "../Interfaces/IPolymerCCTP.sol";
+import {LiFiData} from "../Helpers/LiFiData.sol";
+import {LibAsset} from "../Libraries/LibAsset.sol";
+import {LibSwap} from "../Libraries/LibSwap.sol";
+import {SwapperV2} from "../Helpers/SwapperV2.sol";
+import {Validatable} from "../Helpers/Validatable.sol";
🧹 Nitpick comments (10)
script/deploy/facets/DeployDiamondWithPolymerCCTPFacet.s.sol (1)

37-47: Cut all public/external selectors, not just one

If PolymerCCTPFacet exposes both startBridgeTokensViaPolymerCCTP and swapAndStartBridgeTokensViaPolymerCCTP, include both in functionSelectors to avoid missing entrypoints.

-        bytes4[] memory selectors = new bytes4[](1);
-        selectors[0] = PolymerCCTPFacet.startBridgeTokensViaPolymerCCTP.selector;
+        bytes4[] memory selectors = new bytes4[](2);
+        selectors[0] = PolymerCCTPFacet.startBridgeTokensViaPolymerCCTP.selector;
+        selectors[1] = PolymerCCTPFacet.swapAndStartBridgeTokensViaPolymerCCTP.selector;
script/demoScripts/demoPolymer.sh (3)

102-108: Make route selection robust (fallback to first route when only one exists)

Index 1 fails if the API returns a single route. Prefer “fast if present, else first.”

-# Extract the first route and its first step. Routes[0] will contain the slow route, and Routes[1] contains the fast route - we want to use fast route here.
-FIRST_STEP=$(echo "$ROUTES_RESPONSE" | jq -r '.routes[1].steps[0]')
+ROUTES_COUNT=$(echo "$ROUTES_RESPONSE" | jq -r '.routes | length')
+ROUTE_IDX=$([ "$ROUTES_COUNT" -ge 2 ] && echo 1 || echo 0)
+FIRST_STEP=$(echo "$ROUTES_RESPONSE" | jq -r ".routes[$ROUTE_IDX].steps[0]")

3-5: Harden shell options

Use pipefail (and optionally nounset) to catch failures in pipelines.

-# Exit on error
-set -e
+# Exit on error and fail pipelines; optionally uncomment -u to catch unset vars
+set -eo pipefail
+# set -u

142-147: Remove or use unused variables (TX_CHAIN_ID, POLYMER_TX_DATA)

Either validate TX_CHAIN_ID against FROM_CHAIN_ID (safer) or drop both if not needed.

-TX_CHAIN_ID=$(echo "$STEP_TX_RESPONSE" | jq -r '.transactionRequest.chainId')
-POLYMER_TX_DATA=$(echo "$STEP_TX_RESPONSE" | jq -r '.polymerTransactionData // empty')
+# Optional safety check:
+TX_CHAIN_ID=$(echo "$STEP_TX_RESPONSE" | jq -r '.transactionRequest.chainId')
+if [ -n "$TX_CHAIN_ID" ] && [ "$TX_CHAIN_ID" != "$FROM_CHAIN_ID" ]; then
+  echo "Error: Mismatched chainId in transactionRequest ($TX_CHAIN_ID != $FROM_CHAIN_ID)"; exit 1;
+fi
script/demoScripts/PolymerCCTP.s.sol (1)

18-21: Make amount/fee configurable via env for demos

Allow overriding defaults without editing the file.

-        uint256 amount = uint256(1000);
-        uint256 polymerTokenFee = uint256(10);
+        uint256 amount = vm.envOr("AMOUNT", uint256(1000));
+        uint256 polymerTokenFee = vm.envOr("POLYMER_TOKEN_FEE", uint256(10));

Also applies to: 33-35

script/deploy/DeployPolymerContracts.sh (1)

3-5: Harden script: pipefail and cast exit-status check

Ensure failures in pipelines surface and fail fast on chain-id lookup.

-set -e
+set -eo pipefail
@@
-CHAIN_ID=$(cast chain-id -r "$RPC_URL")
+CHAIN_ID=$(cast chain-id -r "$RPC_URL" 2>&1) || { echo -e "${RED}Error: cast chain-id failed for $RPC_URL${NC}"; exit 1; }
+[ -z "$CHAIN_ID" ] && { echo -e "${RED}Error: Empty chain id from $RPC_URL${NC}"; exit 1; }

Also applies to: 38-45

src/Facets/PolymerCCTPFacet.sol (3)

114-116: Prefer LibAsset approval helper to avoid allowance edge cases

Use project helper for safe/max approval.

-        IERC20(USDC).approve(address(TOKEN_MESSENGER), _bridgeData.minAmount - _polymerData.polymerTokenFee);
+        LibAsset.maxApproveERC20(USDC, address(TOKEN_MESSENGER), _bridgeData.minAmount - _polymerData.polymerTokenFee);

117-128: Confirm CCTP domain vs chainId parameter

depositForBurn usually expects a CCTP domain, not EVM chainId. If design intends domain, source it from PolymerCCTPData (destinationDomain).


43-53: Trim TODOs or file follow-ups

Open TODOs around fee collector, comments on checks, and event necessity should be resolved or tracked in an issue for clarity.

Also applies to: 71-75, 130-146

test/solidity/Facets/PolymerCCTPFacet.t.sol (1)

95-135: Assert emitted events for stronger guarantees

Add vm.expectEmit for PolymerCCTPFeeSent and LiFiTransferStarted before calling the facet to verify params.

Also applies to: 250-319

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

♻️ Duplicate comments (8)
script/demoScripts/demoPolymer.sh (3)

25-26: Check cast chain-id exit status immediately.

Capture stderr and fail fast on cast errors to avoid propagating garbage.

-FROM_CHAIN_ID=$(cast chain-id -r "$FROM_RPC_URL")
-TO_CHAIN_ID=$(cast chain-id -r "$TO_RPC_URL")
+FROM_CHAIN_ID=$(cast chain-id -r "$FROM_RPC_URL" 2>&1) || { echo "Error: cast chain-id failed for FROM_RPC_URL"; exit 1; }
+TO_CHAIN_ID=$(cast chain-id -r "$TO_RPC_URL" 2>&1) || { echo "Error: cast chain-id failed for TO_RPC_URL"; exit 1; }

154-168: Harden approval tx submission and hash extraction.

Add exit-status check and guard empty hash.

-APPROVE_TX_OUTPUT=$(cast send "$FROM_TOKEN" \
+APPROVE_TX_OUTPUT=$(cast send "$FROM_TOKEN" \
   "approve(address,uint256)(bool)" \
   "$TX_TO" \
   "$APPROVAL_AMOUNT" \
   --private-key "$PRIVATE_KEY" \
   --rpc-url "$FROM_RPC_URL" \
-  --json)
+  --json) || { echo "Error: Approval transaction failed"; exit 1; }
 
 APPROVE_TX_HASH=$(echo "$APPROVE_TX_OUTPUT" | jq -r '.transactionHash')
-echo Approve tx hash: $APPROVE_TX_HASH
+echo "Approve tx hash: $APPROVE_TX_HASH"
 
 if [ -z "$APPROVE_TX_HASH" ]; then
   echo "Error: Approval transaction failed"
+  echo "Output: $APPROVE_TX_OUTPUT"
   exit 1
 fi

207-211: Validate receipt retrieval.

Check cast receipt exit status (or poll with timeout) before parsing.

-echo "Waiting for transaction to be mined..."
-TX_RECEIPT=$(cast receipt "$TX_HASH" --rpc-url "$FROM_RPC_URL" --json)
+echo "Waiting for transaction to be mined..."
+if ! TX_RECEIPT=$(cast receipt "$TX_HASH" --rpc-url "$FROM_RPC_URL" --json 2>&1); then
+  echo "Error: Failed to retrieve transaction receipt"
+  echo "$TX_RECEIPT"
+  exit 1
+fi
 TX_STATUS=$(echo "$TX_RECEIPT" | jq -r '.status')
script/deploy/DeployPolymerContracts.sh (2)

12-24: Fix usage/help text to match 3 required args.

“Usage” shows 2 args, script requires 3.

-usage() {
-    echo "Usage: $0 <RPC_URL> <PRIVATE_KEY> "
+usage() {
+    echo "Usage: $0 <RPC_URL> <PRIVATE_KEY> <ADDRESSES_FILE>"
@@
-    echo "  PRIVATE_KEY          Private key for deployment"
-    echo "  ADDRESSES_FILE       Path to addresses json"  
+    echo "  PRIVATE_KEY          Private key for deployment"
+    echo "  ADDRESSES_FILE       Path to addresses json"

132-132: Fix helper path in follow-up instruction.

Script lives under demoScripts.

-echo -e "${BLUE}To call the facet, run: ./script/deploy/CallPolymerCCTP.sh <RPC_URL> <PRIVATE_KEY> <DESTINATION_DOMAIN> $ADDRESSES_FILE${NC}"
+echo -e "${BLUE}To call the facet, run: ./script/demoScripts/CallPolymerCCTP.sh <RPC_URL> <PRIVATE_KEY> <DESTINATION_DOMAIN> $ADDRESSES_FILE${NC}"
src/Facets/PolymerCCTPFacet.sol (3)

12-21: Remove duplicate imports to fix compilation.

The block repeats imports already declared above, causing “Identifier already declared.”

-import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
-import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
-import {ILiFi} from "../Interfaces/ILiFi.sol";
-import {ITokenMessenger} from "../Interfaces/ITokenMessenger.sol";
-import {IPolymerCCTPFacet, PolymerCCTPData} from "../Interfaces/IPolymerCCTP.sol";
-import {LiFiData} from "../Helpers/LiFiData.sol";
-import {LibAsset} from "../Libraries/LibAsset.sol";
+// (duplicates removed)

144-159: Emit events before external call to TOKEN_MESSENGER.

Per facet guidelines, LiFiTransferStarted (and fee event) must be emitted before external calls for correct ordering and observability.

-        TOKEN_MESSENGER.depositForBurn(
+        emit PolymerCCTPFeeSent(_bridgeData.minAmount, _polymerData.polymerTokenFee, _polymerData.minFinalityThreshold);
+        emit LiFiTransferStarted(
+            BridgeData(
+                _bridgeData.transactionId,
+                _bridgeData.bridge,
+                _bridgeData.integrator,
+                _bridgeData.referrer,
+                _bridgeData.sendingAssetId,
+                _bridgeData.receiver,
+                _bridgeData.minAmount - _polymerData.polymerTokenFee,
+                _bridgeData.destinationChainId,
+                _bridgeData.hasSourceSwaps,
+                _bridgeData.hasDestinationCall
+            )
+        );
+
+        TOKEN_MESSENGER.depositForBurn(
             _bridgeData.minAmount - _polymerData.polymerTokenFee,
             uint32(_bridgeData.destinationChainId),
             _bridgeData.receiver == NON_EVM_ADDRESS
                 ? _polymerData.nonEvmAddress
                 : bytes32(uint256(uint160(_bridgeData.receiver))),
             USDC,
             bytes32(0),
             _polymerData.maxCCTPFee,
             _polymerData.minFinalityThreshold
         );
-
-        emit PolymerCCTPFeeSent(_bridgeData.minAmount, _polymerData.polymerTokenFee, _polymerData.minFinalityThreshold);
-        emit LiFiTransferStarted(BridgeData( /* ... */ ));

As per coding guidelines


133-136: Add destinationDomain field to PolymerCCTPData struct and pass Circle CCTP domain ID instead of chainId.

CCTP destinationDomain is a Circle-issued identifier (not EVM chainId). The code at line 134 of PolymerCCTPFacet.sol casts destinationChainId directly to uint32, but it should pass a Circle domain value instead.

Evidence of the issue:

  • Line 127–128 contains a TODO comment: "Need tocheck: can we just use destinationChainID as the normal chain id?"—indicating the developer recognized this uncertainty
  • PolymerCCTPData struct lacks a destinationDomain field; you're forced to reuse _bridgeData.destinationChainId
  • The archived CircleBridgeFacet.sol (line 105) correctly uses a separate _circleBridgeData.dstDomain field
  • script/demoScripts/PolymerCCTP.s.sol (line 45) comments on avoiding "SLOAD to read from a mapping," suggesting domain conversion is expected

Fix: Add uint32 destinationDomain to PolymerCCTPData and update the call at line 134 to pass this value. Alternatively, maintain a mapping from chainId to Circle domain and convert before calling depositForBurn.

🧹 Nitpick comments (4)
script/demoScripts/demoPolymer.sh (1)

139-147: Remove unused POLYMER_TX_DATA or use it.

Variable is assigned but never used. Clean up to reduce noise.

script/deploy/DeployPolymerContracts.sh (1)

39-49: Harden CHAIN_ID retrieval.

Mirror the error-checked pattern used in CallPolymerCCTP.sh.

-CHAIN_ID=$(cast chain-id -r "$RPC_URL")
-if [ -z "$CHAIN_ID" ]; then
-    echo -e "${RED}Error: Could not fetch chain ID from RPC${NC}"
-    exit 1
-fi
+if ! CHAIN_ID=$(cast chain-id -r "$RPC_URL" 2>&1); then
+    echo -e "${RED}Error: cast chain-id failed for $RPC_URL${NC}"
+    echo "$CHAIN_ID"
+    exit 1
+fi
+if [ -z "$CHAIN_ID" ]; then
+    echo -e "${RED}Error: Could not fetch chain ID from RPC${NC}"
+    exit 1
+fi
src/Interfaces/IPolymerCCTP.sol (1)

20-23: Clarify error name to reflect condition.

Name “FeeCannotBeLessThanAmount” contradicts check (fee >= amount). Consider renaming to FeeTooHigh or FeeExceedsOrEqualsAmount.

src/Facets/PolymerCCTPFacet.sol (1)

128-131: Consider safe allowance pattern.

Prefer safeIncreaseAllowance or reset-to-zero then approve to avoid ERC20 non‑standard behaviors. Optional here since this is a one-shot approve.

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

♻️ Duplicate comments (6)
script/deploy/facets/DeployDiamondWithPolymerCCTPFacet.s.sol (1)

11-54: Facet deploy script breaks repo conventions; split responsibilities

This script deploys Diamond + facet from script/deploy/facets/. Facet scripts should inherit DeployScriptBase and only deploy the facet via deploy(...). Move Diamond orchestration to a top-level script (e.g., script/deploy/DeployDiamondWithPolymerCCTPFacet.s.sol), and keep a minimal DeployPolymerCCTPFacet.s.sol here.

Example refactor within this file:

-contract DeployDiamondWithPolymerCCTPFacet is Script {
+contract DeployPolymerCCTPFacet is DeployScriptBase {
     function run() external {
-        uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
-
-        // Read PolymerCCTPFacet constructor arguments from environment
-        address tokenMessenger = vm.envAddress("TOKEN_MESSENGER");
-        address usdc = vm.envAddress("USDC");
-        address polymerFeeRecipient = vm.envAddress("POLYMER_FEE_RECIPIENT");
-
-        vm.startBroadcast(deployerPrivateKey);
-
-        // Deploy DiamondCutFacet
-        console2.log("Deploying DiamondCutFacet...");
-        DiamondCutFacet diamondCutFacet = new DiamondCutFacet();
-        console2.log("DiamondCutFacet deployed at:", address(diamondCutFacet));
-
-        // Deploy LiFiDiamond
-        console2.log("Deploying LiFiDiamond...");
-        LiFiDiamond diamond = new LiFiDiamond(vm.addr(deployerPrivateKey), address(diamondCutFacet));
-        console2.log("LiFiDiamond deployed at:", address(diamond));
-
-        // Deploy PolymerCCTPFacet
-        console2.log("Deploying PolymerCCTPFacet...");
-        PolymerCCTPFacet polymerCCTPFacet = new PolymerCCTPFacet(tokenMessenger, usdc, polymerFeeRecipient);
-        console2.log("PolymerCCTPFacet deployed at:", address(polymerCCTPFacet));
-
-        // Add PolymerCCTPFacet to diamond
-        console2.log("Adding PolymerCCTPFacet to diamond...");
-        bytes4[] memory selectors = new bytes4[](1);
-        selectors[0] = PolymerCCTPFacet.startBridgeTokensViaPolymerCCTP.selector;
-        LibDiamond.FacetCut[] memory cuts = new LibDiamond.FacetCut[](1);
-        cuts[0] = LibDiamond.FacetCut({
-            facetAddress: address(polymerCCTPFacet),
-            action: LibDiamond.FacetCutAction.Add,
-            functionSelectors: selectors
-        });
-        DiamondCutFacet(address(diamond)).diamondCut(cuts, address(0), "");
-        console2.log("PolymerCCTPFacet successfully added to diamond");
-
-        vm.stopBroadcast();
+        address tokenMessenger = vm.envAddress("TOKEN_MESSENGER");
+        address usdc = vm.envAddress("USDC");
+        address polymerFeeRecipient = vm.envAddress("POLYMER_FEE_RECIPIENT");
+        bytes memory args = abi.encode(tokenMessenger, usdc, polymerFeeRecipient);
+        address facet = deploy(type(PolymerCCTPFacet).creationCode, args);
+        console2.log("PolymerCCTPFacet deployed at:", facet);
     }
 }

Based on learnings.

script/demoScripts/demoPolymer.sh (4)

25-27: Check exit status of cast chain-id (fail fast)

Attach error handling to cast calls to avoid propagating garbage on failure.

-FROM_CHAIN_ID=$(cast chain-id -r "$FROM_RPC_URL")
-TO_CHAIN_ID=$(cast chain-id -r "$TO_RPC_URL")
+FROM_CHAIN_ID=$(cast chain-id -r "$FROM_RPC_URL" 2>&1) || { echo "Error: cast chain-id failed for FROM_RPC_URL=$FROM_RPC_URL"; exit 1; }
+TO_CHAIN_ID=$(cast chain-id -r "$TO_RPC_URL" 2>&1) || { echo "Error: cast chain-id failed for TO_RPC_URL=$TO_RPC_URL"; exit 1; }

154-168: Harden approval tx submission and hash extraction

Guard cast send and ensure transactionHash is non-empty.

 APPPROVE_TX_OUTPUT=$(cast send "$FROM_TOKEN" \
   "approve(address,uint256)(bool)" \
   "$TX_TO" \
   "$APPROVAL_AMOUNT" \
   --private-key "$PRIVATE_KEY" \
   --rpc-url "$FROM_RPC_URL" \
-  --json)
+  --json) || { echo "Error: Approval transaction failed"; exit 1; }
 
 APPROVE_TX_HASH=$(echo "$APPROVE_TX_OUTPUT" | jq -r '.transactionHash')
-echo Approve tx hash: $APPROVE_TX_HASH
+echo "Approve tx hash: $APPROVE_TX_HASH"
 
 if [ -z "$APPROVE_TX_HASH" ]; then
   echo "Error: Approval transaction failed"
+  echo "Output: $APPROVE_TX_OUTPUT"
   exit 1
 fi

207-211: Validate receipt retrieval (avoid silent failures)

Check cast receipt exit code (or poll with timeout) before parsing.

-echo "Waiting for transaction to be mined..."
-TX_RECEIPT=$(cast receipt "$TX_HASH" --rpc-url "$FROM_RPC_URL" --json)
+echo "Waiting for transaction to be mined..."
+if ! TX_RECEIPT=$(cast receipt "$TX_HASH" --rpc-url "$FROM_RPC_URL" --json); then
+  echo "Error: Failed to retrieve transaction receipt for $TX_HASH"
+  exit 1
+fi
 TX_STATUS=$(echo "$TX_RECEIPT" | jq -r '.status')

264-271: Fix typo: balanceOf and casing in logs

The function is balanceOf (capital O); the lowercase variant fails.

-echo "==================== balances after transaction ====================="
-FROM_BALANCE_AFTER=$(cast call "$FROM_TOKEN" "balanceof(address)(uint256)" "$USER_ADDRESS" -r "$FROM_RPC_URL" 2>&1) || FROM_BALANCE_AFTER="<query failed>"
-TO_BALANCE_AFTER=$(cast call "$TO_TOKEN" "balanceof(address)(uint256)" "$USER_ADDRESS" -r "$TO_RPC_URL" 2>&1) || TO_BALANCE_AFTER="<query failed>"
-echo "from chain ($FROM_CHAIN_ID) usdc balance: $FROM_BALANCE_AFTER"
-echo "to chain ($TO_CHAIN_ID) usdc balance:     $TO_BALANCE_AFTER"
-echo "====================================================================="
+echo "==================== Balances After Transaction ====================="
+FROM_BALANCE_AFTER=$(cast call "$FROM_TOKEN" "balanceOf(address)(uint256)" "$USER_ADDRESS" -r "$FROM_RPC_URL" 2>&1) || FROM_BALANCE_AFTER="<query failed>"
+TO_BALANCE_AFTER=$(cast call "$TO_TOKEN" "balanceOf(address)(uint256)" "$USER_ADDRESS" -r "$TO_RPC_URL" 2>&1) || TO_BALANCE_AFTER="<query failed>"
+echo "From Chain ($FROM_CHAIN_ID) USDC Balance: $FROM_BALANCE_AFTER"
+echo "To Chain ($TO_CHAIN_ID) USDC Balance:     $TO_BALANCE_AFTER"
+echo "====================================================================="
 echo ""
src/Facets/PolymerCCTPFacet.sol (1)

12-19: Duplicate imports — remove redundant block

These re-import the same modules and add noise. Clean them up.

-import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
-import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
-import {ILiFi} from "../Interfaces/ILiFi.sol";
-import {ITokenMessenger} from "../Interfaces/ITokenMessenger.sol";
-import {IPolymerCCTPFacet, PolymerCCTPData} from "../Interfaces/IPolymerCCTP.sol";
-import {LiFiData} from "../Helpers/LiFiData.sol";
-import {LibAsset} from "../Libraries/LibAsset.sol";
+// (duplicates removed)
🧹 Nitpick comments (5)
src/Interfaces/IPolymerCCTP.sol (1)

6-13: Clarify minFinalityThreshold note (optional)

Consider adding which values map to “fast path” per current Polymer/CCTP docs to help operators tune it.

src/Facets/PolymerCCTPFacet.sol (2)

64-67: Resolve TODOs in init; keep rationale comment only

Finalize the decision (one-time max approval, no safeApprove) and remove TODOs to avoid ambiguity.

-        // TODO - confirm during audit that:
-        // 1.) We can just do a one-time max approve instead of doing an as-needed approve within the startBridge method.
-        // 2.) We don't need to safeApprove and can just use approve since we're only ever transferring usdc
+        // One-time max approval is intentional to save gas; safe for USDC and diamond never holds funds long-term.

99-106: Consider enforcing bridge token in swap path

Add onlyAllowSourceToken(_bridgeData, USDC) to swapAndStartBridgeTokensViaPolymerCCTP so events reflect the actual bridged asset and configs stay consistent.

     function swapAndStartBridgeTokensViaPolymerCCTP(
 ...
-        containsSourceSwaps(_bridgeData)
+        containsSourceSwaps(_bridgeData)
+        onlyAllowSourceToken(_bridgeData, USDC)
         doesNotContainDestinationCalls(_bridgeData)
         validatePolymerData(_bridgeData, _polymerData)

Based on learnings.

test/solidity/Facets/PolymerCCTPFacet.t.sol (2)

232-301: Add a non‑EVM receiver test

Please add a test where bridgeData.receiver == NON_EVM_ADDRESS and polymerData.nonEvmAddress is set, asserting BridgeToNonEVMChainBytes32 emission and messenger mintRecipient. This closes a key path.


29-37: Optionally assert domain mapping

Add a small test for chainIdToDomainId (e.g., 8453 -> 6) to pin interface expectations.

@RnkSngh RnkSngh requested a review from 0xDEnYO November 6, 2025 16:44
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 9a76298 and b767dc8.

📒 Files selected for processing (1)
  • script/demoScripts/demoPolymer.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
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: 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: 782
File: script/deploy/_targetState.json:143-143
Timestamp: 2024-11-25T06:25:01.232Z
Learning: Contracts like `OpBNBBridgeFacet` may be in development and only available in feature branches, resulting in missing source files in the main branch. Before flagging such missing contracts, check for open PRs that might include them.
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 782
File: script/deploy/_targetState.json:49-49
Timestamp: 2024-11-25T09:05:03.917Z
Learning: The `RelayFacet` contract, when missing from the source code but referenced in deployment configurations, should be treated the same way as `OpBNBBridgeFacet` and can be ignored in code reviews.
Learnt from: ezynda3
Repo: lifinance/contracts PR: 924
File: deployments/abstract.json:2-4
Timestamp: 2025-01-28T14:30:06.911Z
Learning: In the LiFi contract architecture, "LiFiDiamond" is the diamond proxy contract that connects all facets into a cohesive system. The facets are individual contracts that provide specific functionality, and the diamond proxy delegates calls to these facets.
Learnt from: mirooon
Repo: lifinance/contracts PR: 945
File: test/solidity/Facets/GlacisFacet.t.sol:214-262
Timestamp: 2025-01-22T12:36:12.699Z
Learning: In the LiFi contracts, token pair validation is handled by third-party contracts, and route discovery including liquidity checks are performed off-chain. The contract only receives pre-validated routes, making token pair and liquidity validation tests unnecessary at the contract level.
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: 1275
File: src/Facets/AllBridgeFacet.sol:186-188
Timestamp: 2025-07-16T01:04:55.857Z
Learning: In LiFi facet contracts, when public entry point functions have `nonReentrant` modifier protection, internal functions like `_startBridge` that they call benefit from this reentrancy protection, making event emission order after external calls acceptable from a security perspective.
Learnt from: mirooon
Repo: lifinance/contracts PR: 1406
File: src/Facets/UnitFacet.sol:75-81
Timestamp: 2025-10-02T18:10:09.934Z
Learning: In UnitFacet.sol and similar facet contracts in src/Facets/, the LiFiTransferStarted event emission should ALWAYS be at the end of the _startBridge internal function, after all validations and asset transfers have been completed successfully.
📚 Learning: 2024-11-05T17:14:24.059Z
Learnt from: maxklenk
Repo: lifinance/contracts PR: 782
File: script/demoScripts/demoPermit2.ts:119-124
Timestamp: 2024-11-05T17:14:24.059Z
Learning: In the file `script/demoScripts/demoPermit2.ts`, adding error handling for external API calls is not preferred as it may overload the demo.

Applied to files:

  • script/demoScripts/demoPolymer.ts
📚 Learning: 2025-08-22T10:03:58.794Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1299
File: script/demoScripts/demoAcrossV4.ts:728-737
Timestamp: 2025-08-22T10:03:58.794Z
Learning: Demo scripts in `script/demoScripts` are exempt from the citty CLI argument parsing requirement that applies to other TypeScript scripts. Templates for demo scripts don't use citty and this is acceptable.

Applied to files:

  • script/demoScripts/demoPolymer.ts

/// @notice Get CCTP domain ID for destination chain
/// @param chainId LIFI specific chain id
/// @return CCTP domain ID recognized by TokenMessenger
function _chainIdToDomainId(uint256 chainId) internal pure returns (uint32) {
Copy link
Contributor

@0xDEnYO 0xDEnYO Nov 7, 2025

Choose a reason for hiding this comment

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

are they ordered by expected volume/likelihood?
If not, maybe we can do some optimizations? e.g. Base is probably more likely than avalanche ?

Copy link
Author

@RnkSngh RnkSngh Nov 7, 2025

Choose a reason for hiding this comment

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

I also considered ordering by volume / likelihood as well but ultimately it's very chain dependent and hard to reason about.

e.g. your suggestion of putting base first might be more optimal for most chains (assuming base is indeed the popular chain) but it would be strictly worse for bridges originating from base itself, since base should never occur as a destination domain if it's the source domain, but always be checked against in your suggestion. So it's not enough to know that base is a popular chain; we'd instead also need to be sure that base is more often used as a to bridge than a from bridge, etc.

I'm happy to implement any ordering you suggest, but I certainly don't know enough to suggest an ordering myself ~

Copy link
Contributor

@0xDEnYO 0xDEnYO Nov 10, 2025

Choose a reason for hiding this comment

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

I think at the end of the day the gas usage is not so huge here despite this being a lot of if branches. I would be OK to leave as-is.

We could optimize this a little by ordering incrementally by chainId and implementing a simple tree searching algo.

@maxklenk what is your take on this?

Copy link
Contributor

Choose a reason for hiding this comment

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

I think the overhead is not big and predicting the volume for a custom order is hard to guess and maintain.
I saw nested ifs that represent a tree structure to get to all cases with a constant amount of if checks, we can try that if you want.

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

📜 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 7486ebf and eb4fb96.

📒 Files selected for processing (1)
  • src/Facets/PolymerCCTPFacet.sol (1 hunks)
🧰 Additional context used
🧠 Learnings (97)
📓 Common learnings
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.
📚 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:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-07-16T01:03:08.106Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 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/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-11-04T16:27:25.773Z
Learnt from: RnkSngh
Repo: lifinance/contracts PR: 1422
File: src/Interfaces/ITokenMessenger.sol:1-3
Timestamp: 2025-11-04T16:27:25.773Z
Learning: For external/third-party interfaces in src/Interfaces/ (e.g., ITokenMessenger.sol for Polymer/CCTP), the team does not require following the project's NatSpec, licensing (LGPL-3.0-only), or pragma conventions, as these interfaces represent contracts the team doesn't control.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2024-10-31T09:09:38.568Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 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:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2024-11-25T09:04:55.880Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 782
File: script/deploy/_targetState.json:143-143
Timestamp: 2024-11-25T09:04:55.880Z
Learning: Errors about the missing `OpBNBBridgeFacet` contract are expected when it is referenced in the target state but not yet merged into the main branch.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-10-02T18:10:09.934Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1406
File: src/Facets/UnitFacet.sol:75-81
Timestamp: 2025-10-02T18:10:09.934Z
Learning: In UnitFacet.sol and similar facet contracts in src/Facets/, the LiFiTransferStarted event emission should ALWAYS be at the end of the _startBridge internal function, after all validations and asset transfers have been completed successfully.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-27T13:07:58.254Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Periphery/Lda/Facets/CurveFacet.sol:0-0
Timestamp: 2025-08-27T13:07:58.254Z
Learning: The CurveFacet in src/Periphery/Lda/Facets/CurveFacet.sol has fundamental issues: it imports a non-existent ICurveLegacy interface, and attempts to send ETH to non-payable exchange functions in both ICurve and ICurveV2 interfaces. All current Curve interfaces are non-payable and cannot accept native ETH.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 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:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-29T11:07:57.743Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Periphery/LDA/Facets/CurveFacet.sol:87-91
Timestamp: 2025-08-29T11:07:57.743Z
Learning: In src/Periphery/LDA/Facets/CurveFacet.sol, modern Curve pools (isV2=true, representing V2/NG pools) should reject native tokenIn by adding an early revert check when LibAsset.isNativeAsset(tokenIn) is true, since ICurveV2 exchange functions are non-payable and cannot accept native ETH.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 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:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-29T11:18:56.656Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Periphery/LDA/Facets/NativeWrapperFacet.sol:1-2
Timestamp: 2025-08-29T11:18:56.656Z
Learning: For src/Periphery/LDA/Facets/NativeWrapperFacet.sol, the SPDX license identifier should be immediately followed by the pragma statement without a blank line in between, as confirmed by mirooon.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-10-02T18:14:51.385Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1406
File: src/Facets/UnitFacet.sol:1-2
Timestamp: 2025-10-02T18:14:51.385Z
Learning: For facet contracts in src/Facets/, the SPDX license identifier should be immediately followed by the pragma statement without a blank line in between, as confirmed by mirooon for src/Facets/UnitFacet.sol.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-10-02T18:14:45.047Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1406
File: script/deploy/facets/UpdateUnitFacet.s.sol:1-3
Timestamp: 2025-10-02T18:14:45.047Z
Learning: For update scripts in script/deploy/facets/, the SPDX license identifier should be immediately followed by the pragma statement without a blank line in between, as confirmed by mirooon for script/deploy/facets/UpdateUnitFacet.s.sol.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-27T23:01:41.042Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Libraries/LibUniV3Logic.sol:4-5
Timestamp: 2025-08-27T23:01:41.042Z
Learning: In the lifinance/contracts repository, the pattern `import { IERC20, SafeERC20 } from "openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";` is correctly used throughout the codebase and works as expected. SafeERC20.sol re-exports IERC20 in modern OpenZeppelin versions, making this combined import valid. Do not suggest splitting these imports.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-29T14:06:33.384Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: script/deploy/facets/LDA/UpdateUniV2StyleFacet.s.sol:1-2
Timestamp: 2025-08-29T14:06:33.384Z
Learning: For LDA deployment and update scripts in script/deploy/facets/LDA/, the SPDX license identifier should be immediately followed by the pragma statement without a blank line in between, as confirmed by mirooon for script/deploy/facets/LDA/UpdateUniV2StyleFacet.s.sol.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-29T14:05:37.002Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: script/deploy/facets/LDA/DeployLDAOwnershipFacet.s.sol:1-3
Timestamp: 2025-08-29T14:05:37.002Z
Learning: For deployment script files in script/deploy/facets/LDA/, the SPDX license identifier should be immediately followed by the pragma statement without a blank line in between, as confirmed by mirooon for script/deploy/facets/LDA/DeployLDAOwnershipFacet.s.sol.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2024-10-09T03:47:21.269Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 807
File: src/Periphery/GasZipPeriphery.sol:4-14
Timestamp: 2024-10-09T03:47:21.269Z
Learning: In `GasZipPeriphery.sol`, `LibUtil` and `Validatable` are used, so ensure not to suggest their removal in future reviews.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2024-09-23T02:04:16.323Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 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/Facets/PolymerCCTPFacet.sol
📚 Learning: 2024-11-25T06:25:01.232Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 782
File: script/deploy/_targetState.json:143-143
Timestamp: 2024-11-25T06:25:01.232Z
Learning: Contracts like `OpBNBBridgeFacet` may be in development and only available in feature branches, resulting in missing source files in the main branch. Before flagging such missing contracts, check for open PRs that might include them.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2024-11-25T09:05:03.917Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 782
File: script/deploy/_targetState.json:49-49
Timestamp: 2024-11-25T09:05:03.917Z
Learning: The `RelayFacet` contract, when missing from the source code but referenced in deployment configurations, should be treated the same way as `OpBNBBridgeFacet` and can be ignored in code reviews.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 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:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-27T23:36:40.773Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 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/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-07-16T01:04:11.083Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1275
File: src/Facets/AllBridgeFacet.sol:175-175
Timestamp: 2025-07-16T01:04:11.083Z
Learning: In AllBridgeFacet.sol, zero fee validation is not required at the contract level because the backend system ensures that calldata and msg.value are properly aligned before transactions reach the contract.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2024-10-22T03:14:52.578Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 807
File: src/Facets/GasZipFacet.sol:53-53
Timestamp: 2024-10-22T03:14:52.578Z
Learning: In `src/Facets/GasZipFacet.sol`, when checking `msg.value`, the team prefers to use strict equality (`msg.value == _bridgeData.minAmount`) to save gas and avoid refunding excess Ether.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-29T11:53:38.549Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 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:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-27T22:23:51.257Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Periphery/Lda/Facets/NativeWrapperFacet.sol:0-0
Timestamp: 2025-08-27T22:23:51.257Z
Learning: In src/Periphery/Lda/Facets/NativeWrapperFacet.sol, the wrapNative function should not validate msg.value == amountIn because CoreRouteFacet's DistributeNative command (command type 3) overrides the amountIn parameter with address(this).balance, making such validation incorrect and causing false failures.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-20T09:56:24.259Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1327
File: docs/OutputValidator.md:23-29
Timestamp: 2025-08-20T09:56:24.259Z
Learning: In src/Periphery/OutputValidator.sol, the ERC20 token flow that uses balanceOf(msg.sender) to compute actualAmount and transfers the excess via safeTransferFrom is intentional behavior, as confirmed by 0xDEnYO. The design choice to potentially include pre-existing ERC20 balances in the excess calculation is by design and should not be flagged as a security concern in future reviews.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 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:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-02-24T09:35:34.908Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 984
File: src/Periphery/ReceiverChainflip.sol:48-62
Timestamp: 2025-02-24T09:35:34.908Z
Learning: Contract address validations for periphery contracts like ReceiverChainflip are handled in deployment scripts rather than in the constructor.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 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:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-02-21T09:00:28.226Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 984
File: src/Facets/ChainflipFacet.sol:127-146
Timestamp: 2025-02-21T09:00:28.226Z
Learning: The `validateBridgeData` modifier in `Validatable.sol` already validates that the receiver address is not zero, making additional zero-address checks redundant in functions using this modifier.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 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:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2024-10-09T03:47:21.269Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 819
File: deployments/boba.diamond.json:68-68
Timestamp: 2024-10-09T03:47:21.269Z
Learning: In the `lifinance/contracts` repository, `ReceiverStargateV2` may not be deployed to all networks, and it's acceptable for its address to remain empty in deployment files when it is not deployed. PRs unrelated to `ReceiverStargateV2` should not raise comments about its absence.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-02-21T09:00:28.226Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 984
File: src/Facets/ChainflipFacet.sol:127-146
Timestamp: 2025-02-21T09:00:28.226Z
Learning: The `validateBridgeData` modifier in `Validatable.sol` validates that the receiver address is not zero using `LibUtil.isZeroAddress`, making additional zero-address checks redundant in functions using this modifier.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-27T22:29:51.839Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: test/solidity/Periphery/Lda/Facets/SyncSwapV2Facet.t.sol:1-2
Timestamp: 2025-08-27T22:29:51.839Z
Learning: Test files in test/solidity/**/*.t.sol should use the LGPL-3.0-only SPDX license identifier, not Unlicense.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-29T14:06:01.072Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: script/deploy/facets/LDA/UpdateCurveFacet.s.sol:1-2
Timestamp: 2025-08-29T14:06:01.072Z
Learning: For script/deploy/facets/LDA/UpdateCurveFacet.s.sol and similar LDA update scripts, the SPDX license identifier should be immediately followed by the pragma statement without requiring a blank line in between, as confirmed by mirooon.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-29T11:33:37.368Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: test/solidity/Periphery/LDA/BaseCoreRouteTest.t.sol:1-3
Timestamp: 2025-08-29T11:33:37.368Z
Learning: For test files in test/solidity/Periphery/LDA/**, the SPDX license identifier should be immediately followed by the pragma statement without a blank line in between, as confirmed by mirooon for test/solidity/Periphery/LDA/BaseCoreRouteTest.t.sol.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-29T11:11:35.958Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Periphery/LDA/Facets/SyncSwapV2Facet.sol:1-3
Timestamp: 2025-08-29T11:11:35.958Z
Learning: For LDA facet files in src/Periphery/LDA/Facets/, the SPDX license identifier can be immediately followed by the pragma statement without requiring a blank line in between, as confirmed by mirooon.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-09-25T00:05:33.977Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1395
File: src/Periphery/FeeForwarder.sol:1-2
Timestamp: 2025-09-25T00:05:33.977Z
Learning: In the lifinance/contracts repository, the established convention is that SPDX license identifiers should be immediately followed by the pragma statement without a blank line in between. This applies to all Solidity files across src/, test/, and script/ directories, as confirmed by 0xDEnYO and verified by codebase analysis.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-27T23:01:41.042Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Libraries/LibUniV3Logic.sol:4-5
Timestamp: 2025-08-27T23:01:41.042Z
Learning: In OpenZeppelin v4.0+, SafeERC20.sol re-exports IERC20, so importing both IERC20 and SafeERC20 from "openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol" is valid and works correctly. Do not suggest splitting this import into separate files.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-27T23:01:41.042Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Libraries/LibUniV3Logic.sol:4-5
Timestamp: 2025-08-27T23:01:41.042Z
Learning: In OpenZeppelin v4.0+, SafeERC20.sol re-exports IERC20, so importing both IERC20 and SafeERC20 from "openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol" is valid and works correctly. Do not suggest splitting this import.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-27T23:01:41.042Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Libraries/LibUniV3Logic.sol:4-5
Timestamp: 2025-08-27T23:01:41.042Z
Learning: In modern OpenZeppelin versions (v4.0+), SafeERC20.sol re-exports IERC20, making the combined import `import { IERC20, SafeERC20 } from "openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";` completely valid and working. Do not suggest splitting these imports as they work correctly as a combined import.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-19T05:07:19.448Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 1303
File: script/deploy/tron/utils.ts:3-5
Timestamp: 2025-08-19T05:07:19.448Z
Learning: In the lifinance/contracts repository, consola and TronWeb should be imported as named exports: `import { consola } from 'consola'` and `import { TronWeb } from 'tronweb'`. Do not flag these as incorrect default export usage.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-19T05:07:19.448Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 1303
File: script/deploy/tron/utils.ts:3-5
Timestamp: 2025-08-19T05:07:19.448Z
Learning: In the lifinance/contracts repository, consola and TronWeb should be imported as named exports: `import { consola } from 'consola'` and `import { TronWeb } from 'tronweb'`. Do not flag these as incorrect default export usage. Both libraries provide named exports, not default exports.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-27T13:07:58.254Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Periphery/Lda/Facets/CurveFacet.sol:0-0
Timestamp: 2025-08-27T13:07:58.254Z
Learning: In the LiFi contracts codebase, ICurveLegacy interface is defined inline in src/Periphery/LiFiDEXAggregator.sol with a non-payable exchange function. The CurveFacet diff shows importing it from a separate file that doesn't exist, creating a compilation issue.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-01-28T11:28:16.225Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 945
File: src/Facets/GlacisFacet.sol:1-3
Timestamp: 2025-01-28T11:28:16.225Z
Learning: The GlacisFacet contract audit will be conducted and added to the audit log in later stages of development. This is acceptable during the initial development phase.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-09-19T14:10:55.064Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 1324
File: src/Facets/EcoFacet.sol:68-71
Timestamp: 2025-09-19T14:10:55.064Z
Learning: In src/Facets/EcoFacet.sol, the team prefers to use msg.sender as the refund address for native tokens in the refundExcessNative modifier, contrary to the general guideline of requiring explicit refund parameters.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-19T05:10:12.922Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 1303
File: script/utils/network.ts:27-32
Timestamp: 2025-08-19T05:10:12.922Z
Learning: In the lifinance/contracts repository, the team decided to standardize RPC environment variable naming by converting all hyphens to underscores in network names. For example, "tron-shasta" becomes "ETH_NODE_URI_TRON_SHASTA". The getRPCEnvVarName() helper in script/utils/network.ts implements this standard and should be used consistently across the codebase instead of direct string manipulation.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 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:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-09-25T00:12:58.536Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1395
File: src/Periphery/FeeForwarder.sol:78-120
Timestamp: 2025-09-25T00:12:58.536Z
Learning: In src/Periphery/FeeForwarder.sol, the team intentionally uses address(this).balance for refunding remaining native tokens because the contract is designed to never hold funds and not collect dust, making it safe to return the entire balance to the caller.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-10-13T11:13:48.847Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1413
File: src/Facets/EverclearV2Facet.sol:75-96
Timestamp: 2025-10-13T11:13:48.847Z
Learning: In EverclearV2Facet (and all LiFi facets), the team standard is to use msg.sender as the refund address with the refundExcessNative modifier, not requiring an explicit refund address parameter.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2024-12-02T06:33:33.309Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 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/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-07-16T01:04:55.857Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1275
File: src/Facets/AllBridgeFacet.sol:186-188
Timestamp: 2025-07-16T01:04:55.857Z
Learning: In LiFi facet contracts, when public entry point functions have `nonReentrant` modifier protection, internal functions like `_startBridge` that they call benefit from this reentrancy protection, making event emission order after external calls acceptable from a security perspective.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2024-10-14T08:23:38.076Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 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/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-04-28T07:46:24.084Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 1116
File: src/Libraries/LibAsset.sol:102-108
Timestamp: 2025-04-28T07:46:24.084Z
Learning: In the LiFi contracts, the `LibAsset.transferFromERC20` function is intentionally designed to accept arbitrary `from` parameters, with security ensured through a whitelist mechanism that controls which contracts can call this function.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2024-11-26T01:16:27.721Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 782
File: deployments/worldchain.diamond.json:40-43
Timestamp: 2024-11-26T01:16:27.721Z
Learning: Version inconsistencies of 'GenericSwapFacetV3' across deployment configurations are acceptable if they are only due to differences in Solidity pragma directives.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2024-10-09T03:47:21.269Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 819
File: deployments/bsc.json:48-48
Timestamp: 2024-10-09T03:47:21.269Z
Learning: The `LiFiDEXAggregator` contract does not include a `swapAndCompare` function.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-02-21T11:39:07.847Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 984
File: test/solidity/Facets/ChainflipFacet.t.sol:154-156
Timestamp: 2025-02-21T11:39:07.847Z
Learning: Bitcoin address validation is not required in Chainflip test cases as the validation is handled elsewhere.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-01-30T08:16:28.174Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 924
File: script/deploy/zksync/DeployExecutor.zksync.s.sol:40-46
Timestamp: 2025-01-30T08:16:28.174Z
Learning: Validation for withdraw wallet address is not required in DeployExecutor.zksync.s.sol as it's handled elsewhere in the system.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-06-05T14:28:34.813Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 1124
File: script/demoScripts/demoPatcher.ts:0-0
Timestamp: 2025-06-05T14:28:34.813Z
Learning: For demo scripts in the lifinance/contracts repository, simpler validation (like checking for zero address) is acceptable and additional contract code validation is not necessary.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2024-10-11T09:34:52.828Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 832
File: deployments/_deployments_log_file.json:23088-23096
Timestamp: 2024-10-11T09:34:52.828Z
Learning: In EVM networks, it's acceptable for contracts to have the same address across multiple networks; this should not be flagged as an issue in code reviews.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-23T11:07:02.778Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1322
File: src/Facets/MayanFacet.sol:259-272
Timestamp: 2025-08-23T11:07:02.778Z
Learning: In src/Facets/MayanFacet.sol, the offset 0xe4 for parsing receiver addresses from HCDepositInitiator::deposit calldata is correct. The mload operation reads 32-byte words and the address extraction via address(uint160(uint256(receiver))) properly handles ABI padding, so theoretical byte-level offset calculations can be misleading when the actual implementation works correctly with real-world data.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-01-28T14:27:46.906Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 924
File: script/deploy/zksync/DeployStargateFacetV2.s.sol:23-28
Timestamp: 2025-01-28T14:27:46.906Z
Learning: Zero-address validation is not required in StargateFacetV2 constructor for tokenMessaging parameter.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 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:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2024-10-04T09:17:19.275Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 819
File: deployments/avalanche.diamond.json:105-105
Timestamp: 2024-10-04T09:17:19.275Z
Learning: In the `lifinance/contracts` repository, contracts may have different addresses across networks. Do not check if contracts have the same addresses across all networks in future reviews.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-26T02:20:52.515Z
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.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2024-09-23T01:48:39.325Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 807
File: src/Periphery/GasZipPeriphery.sol:29-32
Timestamp: 2024-09-23T01:48:39.325Z
Learning: In our codebase, input validation for zero addresses in constructor parameters is not required because our deployment flow ensures that contracts cannot be deployed with zero address parameters unless we are okay with it.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-29T11:12:02.184Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Periphery/LDA/Facets/SyncSwapV2Facet.sol:33-38
Timestamp: 2025-08-29T11:12:02.184Z
Learning: In LDA facets (src/Periphery/Lda/Facets/**/*.sol), tokenIn parameter validation (checking for address(0)) is intentionally not performed as part of the design architecture.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-02-11T12:17:46.597Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 978
File: script/deploy/zksync/DeployGenericSwapFacetV3.zksync.s.sol:30-32
Timestamp: 2025-02-11T12:17:46.597Z
Learning: The native address in network configuration can legitimately be address(0) and should not be validated against zero address.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2024-10-09T03:47:21.269Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 782
File: src/Helpers/WithdrawablePeriphery.sol:20-34
Timestamp: 2024-10-09T03:47:21.269Z
Learning: In the `WithdrawablePeriphery.sol` contract, for admin-only functions protected by the `onlyOwner` modifier, it's acceptable to omit balance checks and reentrancy protection since admins are trusted to handle these functions appropriately.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-06-05T14:28:24.620Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 1124
File: script/demoScripts/demoPatcher.ts:0-0
Timestamp: 2025-06-05T14:28:24.620Z
Learning: In demo scripts (files in script/demoScripts/ or similar demo contexts), unlimited token approvals using ethers.constants.MaxUint256 or type(uint256).max are acceptable for convenience, even though they would be flagged as security concerns in production code. The team acknowledges this is fine for demonstration purposes.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-06-05T14:48:58.816Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 1124
File: src/Periphery/Patcher.sol:160-167
Timestamp: 2025-06-05T14:48:58.816Z
Learning: In src/Periphery/Patcher.sol, the unlimited token approval to finalTarget in depositAndExecuteWithDynamicPatches and depositAndExecuteWithMultiplePatches functions is intentional behavior, as confirmed by ezynda3. This design choice aligns with the contract's stateless/ephemeral architecture.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-07-17T04:21:26.825Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1266
File: script/deploy/safe/execute-pending-timelock-tx.ts:627-628
Timestamp: 2025-07-17T04:21:26.825Z
Learning: In the lifinance/contracts repository, 0xDEnYO prefers to keep '0x0' as a fallback address in gas estimation calls rather than throwing errors when the wallet account address is not available, prioritizing code simplicity over strict validation.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-28T22:41:58.814Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: test/solidity/Periphery/Lda/BaseCoreRouteTest.t.sol:387-395
Timestamp: 2025-08-28T22:41:58.814Z
Learning: In the lifinance/contracts repository, SafeERC20's safeApprove() method handles the two-step approval pattern internally when needed, so manual reset-to-zero followed by approval is unnecessary. Simply changing from IERC20.approve() to IERC20.safeApprove() provides the safety benefits.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2024-10-09T03:47:21.269Z
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`).

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-27T13:16:55.613Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Periphery/Lda/Facets/CoreRouteFacet.sol:0-0
Timestamp: 2025-08-27T13:16:55.613Z
Learning: In LDA (LiFi DEX Aggregator) contracts, using address(this).balance to route all available ETH including dust and deposits from previous calls is intentional design. LDA contracts should not hold any funds and are designed to route any accumulated ETH to prevent fund accumulation. This is a feature, not a security issue.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-02-13T08:54:06.846Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 984
File: src/Facets/ChainflipFacet.sol:119-131
Timestamp: 2025-02-13T08:54:06.846Z
Learning: In Solidity, when mapping between fixed values known at compile time (like chain IDs), using if-else statements with constants is preferred over mappings since mappings cannot be declared as constant or immutable.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-05-14T16:28:30.376Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1137
File: src/Periphery/GasZipPeriphery.sol:21-23
Timestamp: 2025-05-14T16:28:30.376Z
Learning: When shifting by 16 bits per chain ID in the GasZip contracts, the MAX_CHAINID_LENGTH_ALLOWED constant should be set to 16 (since a uint256 can hold at most 16 chain IDs when using 16 bits per chain). This constant should be consistent between related contracts like GasZipPeriphery and GasZipFacet.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2024-11-22T08:49:35.205Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 827
File: src/Facets/DeBridgeDlnFacet.sol:0-0
Timestamp: 2024-11-22T08:49:35.205Z
Learning: In `DeBridgeDlnFacet.sol`, direct access to the `deBridgeChainId` mapping is acceptable, and using the `getDeBridgeChainId` function is not necessary in these instances.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-01-22T12:36:12.699Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 945
File: test/solidity/Facets/GlacisFacet.t.sol:214-262
Timestamp: 2025-01-22T12:36:12.699Z
Learning: In the LiFi contracts, token pair validation is handled by third-party contracts, and route discovery including liquidity checks are performed off-chain. The contract only receives pre-validated routes, making token pair and liquidity validation tests unnecessary at the contract level.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-02-17T07:59:54.979Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 975
File: test/solidity/Facets/DeBridgeDlnFacet.t.sol:0-0
Timestamp: 2025-02-17T07:59:54.979Z
Learning: In DeBridgeDlnFacet, chain ID mappings are hardcoded and not designed to be dynamically updated, so tests for mapping updates are not needed.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-01-28T14:30:06.911Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 924
File: deployments/abstract.json:2-4
Timestamp: 2025-01-28T14:30:06.911Z
Learning: In the LiFi contract architecture, "LiFiDiamond" is the diamond proxy contract that connects all facets into a cohesive system. The facets are individual contracts that provide specific functionality, and the diamond proxy delegates calls to these facets.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2024-09-23T02:04:15.621Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 807
File: src/Periphery/GasZipPeriphery.sol:64-64
Timestamp: 2024-09-23T02:04:15.621Z
Learning: In the `depositToGasZipERC20` function, checking if `swapOutputAmount` is greater than zero after decoding is not required to avoid unnecessary gas consumption.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2024-10-09T03:47:21.269Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 819
File: deployments/polygon.json:0-0
Timestamp: 2024-10-09T03:47:21.269Z
Learning: Ensure that scripts used for codebase verification produce valid and accurate results before reporting issues, especially when checking Ethereum address checksums in `deployments/polygon.json`.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-27T22:29:00.940Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: test/solidity/Periphery/Lda/Facets/SyncSwapV2Facet.t.sol:312-323
Timestamp: 2025-08-27T22:29:00.940Z
Learning: In the LDA test suite, the `_buildRouteAndExecuteSwap` helper function already handles token approvals internally, so manual approval calls are not needed in individual test functions.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-09-16T07:56:45.093Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 1324
File: test/solidity/Facets/EcoFacet.t.sol:135-141
Timestamp: 2025-09-16T07:56:45.093Z
Learning: In EcoFacet test setup, the isNative parameter in swap-and-bridge flows refers to the bridge token type (output), not the swap input type. The ERC20 branch (isNative=false) can still swap from native ETH to ERC20 tokens, requiring msg.value = swapData[0].fromAmount for the ETH input to the swap.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-27T22:32:07.679Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Periphery/Lda/Facets/VelodromeV2Facet.sol:60-70
Timestamp: 2025-08-27T22:32:07.679Z
Learning: In src/Periphery/Lda/Facets/VelodromeV2Facet.sol, the fall-through behavior when the 'from' parameter doesn't match INTERNAL_INPUT_SOURCE, address(this), or msg.sender is intentional. The function should continue execution without transferring tokens, effectively treating unknown 'from' values as if tokens are already available in the pool, similar to INTERNAL_INPUT_SOURCE behavior.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-27T12:56:48.869Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Libraries/LibPackedStream.sol:0-0
Timestamp: 2025-08-27T12:56:48.869Z
Learning: In LibPackedStream.sol, prioritize gas efficiency over defensive bounds checking in readBytesWithLength function. The backend constructs entire payloads so mismatches will revert anyway, making additional bounds checks unnecessary gas overhead.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2024-10-22T03:17:36.803Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 807
File: src/Periphery/GasZipPeriphery.sol:77-77
Timestamp: 2024-10-22T03:17:36.803Z
Learning: In `GasZipPeriphery.sol`, prefer to avoid making more expensive function calls like calling the contract's own functions with `msg.value` after a swap, in order to save gas.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-29T10:02:15.095Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 1299
File: src/Facets/AcrossFacetPackedV4.sol:186-214
Timestamp: 2025-08-29T10:02:15.095Z
Learning: In AcrossFacetPackedV4.sol, calldata length validation is intentionally omitted to prioritize gas optimization over safety checks. The team accepts the risk of potential out-of-bounds reads in favor of lower gas costs for the packed implementation.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-08-27T12:56:34.605Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1328
File: src/Libraries/LibPackedStream.sol:2-3
Timestamp: 2025-08-27T12:56:34.605Z
Learning: In LibPackedStream.sol, bounds checking is intentionally omitted for gas efficiency since payloads are constructed by the backend and any corruption would cause natural reverts during stream operations anyway. Gas optimization is prioritized over defensive programming in this library.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2024-11-21T08:39:29.530Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 861
File: config/dexs.json:748-752
Timestamp: 2024-11-21T08:39:29.530Z
Learning: In the 'worldchain' network, the addresses `0x50D5a8aCFAe13Dceb217E9a071F6c6Bd5bDB4155`, `0x8f023b4193a6b18C227B4a755f8e28B3D30Ef9a1`, and `0x603a538477d44064eA5A5d8C345b4Ff6fca1142a` are used as DEXs and should be included in `config/dexs.json`.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2024-10-09T03:47:21.269Z
Learnt from: ezynda3
Repo: lifinance/contracts PR: 806
File: deployments/_deployments_log_file.json:5780-5793
Timestamp: 2024-10-09T03:47:21.269Z
Learning: The owner address `0x11f11121df7256c40339393b0fb045321022ce44` and the `DiamondCutFacet` address `0xd5cf40a2a18b633cfd6a1ae16d1771596498cf83` in the `LiFiDiamond` deployment on `xlayer` are correct and should not be flagged as issues, even if they are not referenced in the Solidity files.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 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:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2024-09-23T01:41:16.425Z
Learnt from: 0xDEnYO
Repo: lifinance/contracts PR: 807
File: config/gaszip.json:4-19
Timestamp: 2024-09-23T01:41:16.425Z
Learning: In our project, it's acceptable for contracts to have the same address on different chains, and duplicate addresses across multiple networks in configuration files are intentional.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol
📚 Learning: 2025-05-06T09:09:38.108Z
Learnt from: mirooon
Repo: lifinance/contracts PR: 1117
File: test/solidity/Periphery/LiFiDEXAggregator.t.sol:111-127
Timestamp: 2025-05-06T09:09:38.108Z
Learning: The `setupApechain()` function in LiFiDEXAggregator tests intentionally avoids calling `initTestBase()` to prevent needing to define standard contracts (like ADDRESS_USDC_PROXY and Uniswap) that aren't needed for Apechain-specific tests.

Applied to files:

  • src/Facets/PolymerCCTPFacet.sol

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

♻️ Duplicate comments (7)
script/demoScripts/demoPolymer.ts (2)

263-300: Handle single-route responses from the Polymer API.

The script assumes a fast route at routes[1], so it exits even when the API returns a valid single route (a common case on testnets). Please fall back to the first available route and surface HTTP errors explicitly instead of aborting.

-  const routesResponse = await fetch(`${POLYMER_API_URL}/v1/routes`, {
+  const routesResponse = await fetch(`${POLYMER_API_URL}/v1/routes`, {
     method: 'POST',
     headers: {
       'Content-Type': 'application/json',
     },
     body: JSON.stringify({
       fromChainId,
       toChainId,
       fromTokenAddress: fromToken,
       toTokenAddress: toToken,
       fromAmount: FROM_AMOUNT,
       fromAddress,
       toAddress,
     }),
   })
-
-  console.log("got routes response", routesResponse)
-  const routesData: RoutesResponse = await routesResponse.json()
+  console.log('got routes response', routesResponse)
+
+  if (!routesResponse.ok) {
+    const errorBody = await routesResponse.text()
+    error(`Error: v1/routes returned HTTP status ${routesResponse.status}`)
+    console.log(errorBody)
+    process.exit(1)
+  }
+
+  const routesData: RoutesResponse = await routesResponse.json()
 
   if ('error' in routesData) {
     error('Error fetching routes:')
     console.log(JSON.stringify(routesData, null, 2))
     process.exit(1)
   }
 
-  // Routes[0] contains the slow route, Routes[1] contains the fast route - we want the fast route
-  const firstStep = routesData.routes[1]?.steps[0]
+  const routes = Array.isArray(routesData.routes) ? routesData.routes : []
+  const selectedRoute = routes[1] ?? routes[0]
+  const firstStep = selectedRoute?.steps?.[0]
 
   if (!firstStep) {
     error('No routes found')
     process.exit(1)
   }

369-386: Preserve the value provided by Polymer when submitting the transaction.

Polymer can ask us to forward native value (e.g., remote gas). Dropping transactionRequest.value causes the on-chain call to revert. Thread that field through to sendTransaction.

-async function submitTransaction(
-  wallet: Wallet,
-  txTo: string,
-  txData: string,
-  txGasLimit: string
-): Promise<string> {
+async function submitTransaction(
+  wallet: Wallet,
+  txTo: string,
+  txData: string,
+  txGasLimit: string,
+  txValue?: string
+): Promise<string> {
   console.log('Step 3: Submitting transaction...')
   console.log(`To Address:  ${txTo}`)
   console.log(`Gas Limit:   ${txGasLimit}`)
   console.log('')
 
-  const tx = await wallet.sendTransaction({
-    to: txTo,
-    data: txData,
-    gasLimit: BigNumber.from(txGasLimit),
-  })
+  const txRequest: providers.TransactionRequest = {
+    to: txTo,
+    data: txData,
+    gasLimit: BigNumber.from(txGasLimit),
+  }
+
+  if (txValue) {
+    txRequest.value = BigNumber.from(txValue)
+  }
+
+  const tx = await wallet.sendTransaction(txRequest)
-    txHash = await submitTransaction(wallet!, txTo, txData, txGasLimit)
+    txHash = await submitTransaction(
+      wallet!,
+      txTo,
+      txData,
+      txGasLimit,
+      stepTxData.transactionRequest.value
+    )

Also applies to: 539-561

script/deploy/DeployPolymerContracts.sh (3)

124-132: Point users to the actual call script path.

The follow-up message directs people to script/deploy/CallPolymerCCTP.sh, but the script lives in script/demoScripts. Send them to the real location to avoid dead ends.

-echo -e "${BLUE}To call the facet, run: ./script/deploy/CallPolymerCCTP.sh <RPC_URL> <PRIVATE_KEY> <DESTINATION_DOMAIN> $ADDRESSES_FILE${NC}"
+echo -e "${BLUE}To call the facet, run: ./script/demoScripts/CallPolymerCCTP.sh <RPC_URL> <PRIVATE_KEY> <DESTINATION_DOMAIN> $ADDRESSES_FILE${NC}"

12-23: Fix the usage banner to list all required arguments.

The script refuses to run unless three positional arguments are supplied, but the usage string only shows two. That makes the help text misleading for anyone copying it. Please add <ADDRESSES_FILE> to the usage line so it matches the actual interface.

-    echo "Usage: $0 <RPC_URL> <PRIVATE_KEY> "
+    echo "Usage: $0 <RPC_URL> <PRIVATE_KEY> <ADDRESSES_FILE>"

79-88: Correct the copy/paste hint for polymerFeeRecipient.

When polymerFeeRecipient is missing, the instruction still tells users to add a usdc key. That leads to the wrong field being populated. Update the hint to reference the polymerFeeRecipient key instead.

-        echo "  2. Add it to $ADDRESSES_FILE under \"$CHAIN_ID\": { \"usdc\": \"0x...\" }"
+        echo "  2. Add it to $ADDRESSES_FILE under \"$CHAIN_ID\": { \"polymerFeeRecipient\": \"0x...\" }"
test/solidity/Facets/PolymerCCTPFacet.t.sol (1)

84-301: Test coverage gaps - leverage TestBaseFacet and add missing scenarios.

Current tests cover only basic happy paths and a few error cases. Based on past review feedback, unit test coverage must be 100%, and TestBaseFacet.sol provides standard test patterns for bridge facets that should be leveraged.

Missing test scenarios:

  • NON_EVM_ADDRESS bridging path with nonEvmAddress validation
  • Same network bridging error (CannotBridgeToSameNetwork)
  • Chain ID to domain ID mapping for various supported chains
  • Edge case where polymerTokenFee equals minAmount (resulting in zero bridge amount)
  • Event emission verification (PolymerCCTPFeeSent, LiFiTransferStarted, BridgeToNonEVMChainBytes32)
  • initPolymerCCTP function behavior and approval state
  • Unsupported chain ID revert with InvalidCallData

Based on learnings

src/Facets/PolymerCCTPFacet.sol (1)

4-16: Duplicate imports prevent compilation.

Lines 4-10 and 11-16 declare the same identifiers twice (IERC20, ReentrancyGuard, ILiFi, ITokenMessenger, LiFiData, LibAsset), causing a compilation failure with "Identifier already declared" errors. Merge these into a single import block.

Apply this diff to consolidate imports:

-import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
-import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
-import {ILiFi} from "../Interfaces/ILiFi.sol";
-import {ITokenMessenger} from "../Interfaces/ITokenMessenger.sol";
-import {LiFiData} from "../Helpers/LiFiData.sol";
-import {LibAsset} from "../Libraries/LibAsset.sol";
-import {LibUtil} from "../Libraries/LibUtil.sol";
 import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
 import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
 import {ILiFi} from "../Interfaces/ILiFi.sol";
 import {ITokenMessenger} from "../Interfaces/ITokenMessenger.sol";
 import {LiFiData} from "../Helpers/LiFiData.sol";
 import {LibAsset} from "../Libraries/LibAsset.sol";
+import {LibUtil} from "../Libraries/LibUtil.sol";
 import {LibSwap} from "../Libraries/LibSwap.sol";
 import {SwapperV2} from "../Helpers/SwapperV2.sol";
 import {Validatable} from "../Helpers/Validatable.sol";
-import {InvalidReceiver, InvalidAmount, CannotBridgeToSameNetwork, InvalidConfig} from "../Errors/GenericErrors.sol";
-import {InvalidCallData} from "../Errors/GenericErrors.sol";
+import {InvalidReceiver, InvalidAmount, CannotBridgeToSameNetwork, InvalidConfig, InvalidCallData} from "../Errors/GenericErrors.sol";
 import {LibDiamond} from "../Libraries/LibDiamond.sol";
🧹 Nitpick comments (2)
src/Facets/PolymerCCTPFacet.sol (2)

74-76: Resolve or remove audit TODO comment.

This TODO references audit confirmation for max approval and safe approve usage. Based on past review discussions, the team has confirmed this approach is acceptable. Remove the TODO or convert it to a clarifying comment explaining the safety rationale.

Apply this diff:

-        // TODO - confirm during audit that:
-        // 1.) We can just do a one-time max approve instead of doing an as-needed approve within the startBridge method.
-        // 2.) We don't need to safeApprove and can just use approve since we're only ever transferring usdc
+        // Max approval is safe: diamond never holds funds, so even if TOKEN_MESSENGER turns malicious,
+        // there are no funds to steal. Standard approve is sufficient for USDC's clean implementation.
         IERC20(USDC).approve(address(TOKEN_MESSENGER), type(uint256).max);

94-94: Remove redundant TODO comment.

The onlyAllowSourceToken modifier already enforces that sendingAssetId == USDC, making this check and TODO unnecessary. Remove the TODO comment.

Apply this diff:

-        // TODO: Do we need this check if it's always going to be usdc?
         LibAsset.transferFromERC20(USDC, msg.sender, address(this), _bridgeData.minAmount);

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

♻️ Duplicate comments (1)
src/Facets/PolymerCCTPFacet.sol (1)

4-19: Critical: Duplicate imports will prevent compilation.

Lines 4-10 and 11-16 both import the same identifiers: IERC20, ReentrancyGuard, ILiFi, ITokenMessenger, LiFiData, and LibAsset. Solidity will fail with "Identifier already declared" errors.

Remove the duplicate imports:

 import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
 import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
 import {ILiFi} from "../Interfaces/ILiFi.sol";
 import {ITokenMessenger} from "../Interfaces/ITokenMessenger.sol";
 import {LiFiData} from "../Helpers/LiFiData.sol";
 import {LibAsset} from "../Libraries/LibAsset.sol";
 import {LibUtil} from "../Libraries/LibUtil.sol";
-import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
-import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
-import {ILiFi} from "../Interfaces/ILiFi.sol";
-import {ITokenMessenger} from "../Interfaces/ITokenMessenger.sol";
-import {LiFiData} from "../Helpers/LiFiData.sol";
-import {LibAsset} from "../Libraries/LibAsset.sol";
 import {LibSwap} from "../Libraries/LibSwap.sol";
 import {SwapperV2} from "../Helpers/SwapperV2.sol";
 import {Validatable} from "../Helpers/Validatable.sol";
🧹 Nitpick comments (1)
src/Facets/PolymerCCTPFacet.sol (1)

94-94: Consider removing resolved TODO.

The TODO asks whether a check is needed "if it's always going to be usdc". The onlyAllowSourceToken modifier (line 90) already enforces this, so the comment can be removed.

-        // TODO: Do we need this check if it's always going to be usdc?
         LibAsset.transferFromERC20(USDC, msg.sender, address(this), _bridgeData.minAmount);

@0xDEnYO 0xDEnYO closed this Nov 17, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants