Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions src/Interfaces/IERC20PermitExtended.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// SPDX-License-Identifier: MIT
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix the license identifier to LGPL-3.0-only.

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

Apply this diff:

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

Based on coding guidelines.

📝 Committable suggestion

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

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

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

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

pragma solidity ^0.8.17;

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

interface IERC20PermitExtended is IERC20Permit {
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Add required NatSpec documentation for the interface.

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

Apply this diff:

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

Based on coding guidelines and pipeline failures.

📝 Committable suggestion

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

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

function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
bytes calldata signature
) external;

function transferWithAuthorization(
address from,
address to,
uint256 value,
uint256 validAfter,
uint256 validBefore,
bytes32 nonce,
uint8 v,
bytes32 r,
bytes32 s
) external;

function transferWithAuthorization(
address from,
address to,
uint256 value,
uint256 validAfter,
uint256 validBefore,
bytes32 nonce,
bytes calldata signature
) external;
}
138 changes: 131 additions & 7 deletions src/Periphery/Permit2Proxy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { ISignatureTransfer } from "permit2/interfaces/ISignatureTransfer.sol";
import { LibAsset, IERC20 } from "lifi/Libraries/LibAsset.sol";
import { LibUtil } from "lifi/Libraries/LibUtil.sol";
import { PermitHash } from "permit2/libraries/PermitHash.sol";
import { ERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
import { IERC20PermitExtended } from "lifi/Interfaces/IERC20PermitExtended.sol";
import { WithdrawablePeriphery } from "lifi/Helpers/WithdrawablePeriphery.sol";

/// @title Permit2Proxy
Expand Down Expand Up @@ -61,6 +61,60 @@ contract Permit2Proxy is WithdrawablePeriphery {

/// External Functions ///

/// @notice Allows to bridge tokens through a LI.FI diamond contract using
/// an EIP2612 gasless permit (only works with tokenAddresses that
/// implement EIP2612)
/// The permit signer must be the caller to prevent front-running and ensure
/// the calldata cannot be replaced by others.
/// Can only be called by the permit signer to prevent front-running.
/// @param tokenAddress Address of the token to be bridged
/// @param amount Amount of tokens to be bridged
/// @param deadline Transaction must be completed before this timestamp
/// @param signature User signature (packed bytes)
/// @param diamondCalldata calldata to execute
function callDiamondWithEIP2612Signature(
address tokenAddress,
uint256 amount,
uint256 deadline,
bytes calldata signature,
bytes calldata diamondCalldata
) public payable returns (bytes memory) {
try
IERC20PermitExtended(tokenAddress).permit(
msg.sender,
address(this),
amount,
deadline,
signature
)
{} catch Error(string memory reason) {
if (
IERC20(tokenAddress).allowance(msg.sender, address(this)) <
amount
) {
revert(reason);
}
} catch (bytes memory reason) {
if (
IERC20(tokenAddress).allowance(msg.sender, address(this)) <
amount
) {
LibUtil.revertWith(reason);
}
}

LibAsset.transferFromERC20(
tokenAddress,
msg.sender,
address(this),
amount
);

LibAsset.maxApproveERC20(IERC20(tokenAddress), LIFI_DIAMOND, amount);

return _executeCalldata(diamondCalldata);
}

/// @notice Allows to bridge tokens through a LI.FI diamond contract using
/// an EIP2612 gasless permit (only works with tokenAddresses that
/// implement EIP2612)
Expand All @@ -83,10 +137,9 @@ contract Permit2Proxy is WithdrawablePeriphery {
bytes32 s,
bytes calldata diamondCalldata
) public payable returns (bytes memory) {
// call permit on token contract to register approval using signature
try
ERC20Permit(tokenAddress).permit(
msg.sender, // Ensure msg.sender is same wallet that signed permit
IERC20PermitExtended(tokenAddress).permit(
msg.sender,
address(this),
amount,
deadline,
Expand All @@ -110,18 +163,89 @@ contract Permit2Proxy is WithdrawablePeriphery {
}
}

// deposit assets
LibAsset.transferFromERC20(
tokenAddress,
msg.sender,
address(this),
amount
);

// maxApprove token to diamond if current allowance is insufficient
LibAsset.maxApproveERC20(IERC20(tokenAddress), LIFI_DIAMOND, amount);

// call our diamond to execute calldata
return _executeCalldata(diamondCalldata);
}

/// @notice Allows to bridge tokens through a LI.FI diamond contract using
/// an EIP3009 transferWithAuthorization (only works with tokenAddresses that
/// implement EIP3009, such as USDC)
/// @param tokenAddress Address of the token to be bridged
/// @param amount Amount of tokens to be bridged
/// @param validAfter The time after which this is valid (unix time)
/// @param validBefore The time before which this is valid (unix time)
/// @param nonce Unique nonce for this authorization
/// @param signature User signature (packed bytes)
/// @param diamondCalldata calldata to execute
function callDiamondWithEIP3009Signature(
address tokenAddress,
uint256 amount,
uint256 validAfter,
uint256 validBefore,
bytes32 nonce,
bytes calldata signature,
bytes calldata diamondCalldata
) public payable returns (bytes memory) {
IERC20PermitExtended(tokenAddress).transferWithAuthorization(
msg.sender,
address(this),
amount,
validAfter,
validBefore,
nonce,
signature
);

LibAsset.maxApproveERC20(IERC20(tokenAddress), LIFI_DIAMOND, amount);

return _executeCalldata(diamondCalldata);
}

/// @notice Allows to bridge tokens through a LI.FI diamond contract using
/// an EIP3009 transferWithAuthorization (only works with tokenAddresses that
/// implement EIP3009, such as USDC)
/// @param tokenAddress Address of the token to be bridged
/// @param amount Amount of tokens to be bridged
/// @param validAfter The time after which this is valid (unix time)
/// @param validBefore The time before which this is valid (unix time)
/// @param nonce Unique nonce for this authorization
/// @param v User signature (recovery ID)
/// @param r User signature (ECDSA output)
/// @param s User signature (ECDSA output)
/// @param diamondCalldata calldata to execute
function callDiamondWithEIP3009Signature(
address tokenAddress,
uint256 amount,
uint256 validAfter,
uint256 validBefore,
bytes32 nonce,
uint8 v,
bytes32 r,
bytes32 s,
bytes calldata diamondCalldata
) public payable returns (bytes memory) {
IERC20PermitExtended(tokenAddress).transferWithAuthorization(
msg.sender,
address(this),
amount,
validAfter,
validBefore,
nonce,
v,
r,
s
);

LibAsset.maxApproveERC20(IERC20(tokenAddress), LIFI_DIAMOND, amount);

return _executeCalldata(diamondCalldata);
}

Expand Down
Loading
Loading