From e4c59473f22fe1624b59ac885d238da096513285 Mon Sep 17 00:00:00 2001 From: Ed Zynda Date: Thu, 22 Aug 2024 19:20:35 +0300 Subject: [PATCH 01/69] Add Permit2Proxy --- test/solidity/Periphery/Permit2Proxy.t.sol | 45 ++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 test/solidity/Periphery/Permit2Proxy.t.sol diff --git a/test/solidity/Periphery/Permit2Proxy.t.sol b/test/solidity/Periphery/Permit2Proxy.t.sol new file mode 100644 index 000000000..d1c38a8fc --- /dev/null +++ b/test/solidity/Periphery/Permit2Proxy.t.sol @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import { Test, console } from "forge-std/Test.sol"; +import { Permit2Proxy } from "lifi/Periphery/Permit2Proxy.sol"; +import { ISignatureTransfer } from "lifi/Interfaces/ISignatureTransfer.sol"; +import "forge-std/console.sol"; + +contract Permit2ProxyTest is Test { + Permit2Proxy public permit2proxy; + + function setUp() public { + permit2proxy = new Permit2Proxy(); + vm.createSelectFork(vm.envString("ETH_NODE_URI_MAINNET"), 20261175); + console.logAddress(address(permit2proxy)); + } + + function test_hardcoded_sig() public { + uint256 amount = 10 ** 18; + address token = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; + address owner = vm.addr( + 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 + ); + + ISignatureTransfer.PermitTransferFrom + memory transfer = ISignatureTransfer.PermitTransferFrom( + ISignatureTransfer.TokenPermissions(token, amount), + 0, + type(uint256).max + ); + + bytes + memory sig = hex"496bd11f1de6e3824f1d8032977c10f752ceb1bda1aec025b5b5a7956ffb0e182a0ae2d49de265ff334b47f7adf6233f5455b1d6d3a921ccdfcfbd4c2cab218e1b"; + console.logBytes(sig); + permit2proxy.diamondCallSingle( + address(0), + address(0), + keccak256(hex"deadbeef"), + hex"deadbeef", + owner, + transfer, + sig + ); + } +} From 797e772cfb4fb9aa17e8f259a0bb42d51e3e4fad Mon Sep 17 00:00:00 2001 From: Ed Zynda Date: Thu, 22 Aug 2024 19:24:50 +0300 Subject: [PATCH 02/69] Add Permit2Proxy --- src/Interfaces/IEIP712.sol | 6 + src/Interfaces/ISignatureTransfer.sol | 138 +++++++++++++++++++++ src/Periphery/Permit2Proxy.sol | 91 ++++++++++++++ test/solidity/Periphery/Permit2Proxy.t.sol | 2 +- 4 files changed, 236 insertions(+), 1 deletion(-) create mode 100644 src/Interfaces/IEIP712.sol create mode 100644 src/Interfaces/ISignatureTransfer.sol create mode 100644 src/Periphery/Permit2Proxy.sol diff --git a/src/Interfaces/IEIP712.sol b/src/Interfaces/IEIP712.sol new file mode 100644 index 000000000..1a5be726b --- /dev/null +++ b/src/Interfaces/IEIP712.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +interface IEIP712 { + function DOMAIN_SEPARATOR() external view returns (bytes32); +} diff --git a/src/Interfaces/ISignatureTransfer.sol b/src/Interfaces/ISignatureTransfer.sol new file mode 100644 index 000000000..9b379e881 --- /dev/null +++ b/src/Interfaces/ISignatureTransfer.sol @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import { IEIP712 } from "./IEIP712.sol"; + +/// @title SignatureTransfer +/// @notice Handles ERC20 token transfers through signature based actions +/// @dev Requires user's token approval on the Permit2 contract +interface ISignatureTransfer is IEIP712 { + /// @notice Thrown when the requested amount for a transfer is larger than the permissioned amount + /// @param maxAmount The maximum amount a spender can request to transfer + error InvalidAmount(uint256 maxAmount); + + /// @notice Thrown when the number of tokens permissioned to a spender does not match the number of tokens being transferred + /// @dev If the spender does not need to transfer the number of tokens permitted, the spender can request amount 0 to be transferred + error LengthMismatch(); + + /// @notice Emits an event when the owner successfully invalidates an unordered nonce. + event UnorderedNonceInvalidation( + address indexed owner, + uint256 word, + uint256 mask + ); + + /// @notice The token and amount details for a transfer signed in the permit transfer signature + struct TokenPermissions { + // ERC20 token address + address token; + // the maximum amount that can be spent + uint256 amount; + } + + /// @notice The signed permit message for a single token transfer + struct PermitTransferFrom { + TokenPermissions permitted; + // a unique value for every token owner's signature to prevent signature replays + uint256 nonce; + // deadline on the permit signature + uint256 deadline; + } + + /// @notice Specifies the recipient address and amount for batched transfers. + /// @dev Recipients and amounts correspond to the index of the signed token permissions array. + /// @dev Reverts if the requested amount is greater than the permitted signed amount. + struct SignatureTransferDetails { + // recipient address + address to; + // spender requested amount + uint256 requestedAmount; + } + + /// @notice Used to reconstruct the signed permit message for multiple token transfers + /// @dev Do not need to pass in spender address as it is required that it is msg.sender + /// @dev Note that a user still signs over a spender address + struct PermitBatchTransferFrom { + // the tokens and corresponding amounts permitted for a transfer + TokenPermissions[] permitted; + // a unique value for every token owner's signature to prevent signature replays + uint256 nonce; + // deadline on the permit signature + uint256 deadline; + } + + /// @notice A map from token owner address and a caller specified word index to a bitmap. Used to set bits in the bitmap to prevent against signature replay protection + /// @dev Uses unordered nonces so that permit messages do not need to be spent in a certain order + /// @dev The mapping is indexed first by the token owner, then by an index specified in the nonce + /// @dev It returns a uint256 bitmap + /// @dev The index, or wordPosition is capped at type(uint248).max + function nonceBitmap(address, uint256) external view returns (uint256); + + /// @notice Transfers a token using a signed permit message + /// @dev Reverts if the requested amount is greater than the permitted signed amount + /// @param permit The permit data signed over by the owner + /// @param owner The owner of the tokens to transfer + /// @param transferDetails The spender's requested transfer details for the permitted token + /// @param signature The signature to verify + function permitTransferFrom( + PermitTransferFrom memory permit, + SignatureTransferDetails calldata transferDetails, + address owner, + bytes calldata signature + ) external; + + /// @notice Transfers a token using a signed permit message + /// @notice Includes extra data provided by the caller to verify signature over + /// @dev The witness type string must follow EIP712 ordering of nested structs and must include the TokenPermissions type definition + /// @dev Reverts if the requested amount is greater than the permitted signed amount + /// @param permit The permit data signed over by the owner + /// @param owner The owner of the tokens to transfer + /// @param transferDetails The spender's requested transfer details for the permitted token + /// @param witness Extra data to include when checking the user signature + /// @param witnessTypeString The EIP-712 type definition for remaining string stub of the typehash + /// @param signature The signature to verify + function permitWitnessTransferFrom( + PermitTransferFrom memory permit, + SignatureTransferDetails calldata transferDetails, + address owner, + bytes32 witness, + string calldata witnessTypeString, + bytes calldata signature + ) external; + + /// @notice Transfers multiple tokens using a signed permit message + /// @param permit The permit data signed over by the owner + /// @param owner The owner of the tokens to transfer + /// @param transferDetails Specifies the recipient and requested amount for the token transfer + /// @param signature The signature to verify + function permitTransferFrom( + PermitBatchTransferFrom memory permit, + SignatureTransferDetails[] calldata transferDetails, + address owner, + bytes calldata signature + ) external; + + /// @notice Transfers multiple tokens using a signed permit message + /// @dev The witness type string must follow EIP712 ordering of nested structs and must include the TokenPermissions type definition + /// @notice Includes extra data provided by the caller to verify signature over + /// @param permit The permit data signed over by the owner + /// @param owner The owner of the tokens to transfer + /// @param transferDetails Specifies the recipient and requested amount for the token transfer + /// @param witness Extra data to include when checking the user signature + /// @param witnessTypeString The EIP-712 type definition for remaining string stub of the typehash + /// @param signature The signature to verify + function permitWitnessTransferFrom( + PermitBatchTransferFrom memory permit, + SignatureTransferDetails[] calldata transferDetails, + address owner, + bytes32 witness, + string calldata witnessTypeString, + bytes calldata signature + ) external; + + /// @notice Invalidates the bits specified in mask for the bitmap at the word position + /// @dev The wordPos is maxed at type(uint248).max + /// @param wordPos A number to index the nonceBitmap at + /// @param mask A bitmap masked against msg.sender's current bitmap at the word position + function invalidateUnorderedNonces(uint256 wordPos, uint256 mask) external; +} diff --git a/src/Periphery/Permit2Proxy.sol b/src/Periphery/Permit2Proxy.sol new file mode 100644 index 000000000..e39f1cfed --- /dev/null +++ b/src/Periphery/Permit2Proxy.sol @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.17; + +import { ISignatureTransfer } from "../Interfaces/ISignatureTransfer.sol"; +import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +contract Permit2Proxy { + ISignatureTransfer immutable PERMIT2 = + ISignatureTransfer(0x000000000022D473030F116dDEE9F6B43aC78BA3); + + error CallToDiamondFailed(bytes); + + // LIFI Specific Witness to verify + struct LIFICall { + address tokenReceiver; + address diamondAddress; + bytes32 diamondCalldataHash; + } + + string private constant WITNESS_TYPE_STRING = + "LIFICall witness)TokenPermissions(address token,uint256 amount)LIFICall(address tokenReceiver,address diamondAddress,bytes32 diamondCalldataHash)"; + bytes32 private WITNESS_TYPEHASH = + keccak256( + "LIFICall(address tokenReceiver,address diamondAddress,bytes32 diamondCalldataHash)" + ); + + function maxApproveERC20( + IERC20 assetId, + address spender, + uint256 amount + ) internal { + if (address(assetId) == address(0)) { + return; + } + + if (assetId.allowance(address(this), spender) < amount) { + SafeERC20.safeIncreaseAllowance(IERC20(assetId), spender, 0); + SafeERC20.safeIncreaseAllowance( + IERC20(assetId), + spender, + type(uint).max + ); + } + } + + function diamondCallSingle( + address _tokenReceiver, + address _diamondAddress, + bytes32 _diamondCalldataHash, + bytes calldata _diamondCalldata, + address _owner, + ISignatureTransfer.PermitTransferFrom calldata _permit, + bytes calldata _signature + ) external payable { + bytes32 witnessHash = keccak256( + abi.encode( + WITNESS_TYPEHASH, + LIFICall(_tokenReceiver, _diamondAddress, _diamondCalldataHash) + ) + ); + + PERMIT2.permitWitnessTransferFrom( + _permit, + ISignatureTransfer.SignatureTransferDetails({ + to: address(this), + requestedAmount: _permit.permitted.amount + }), + _owner, + witnessHash, + WITNESS_TYPE_STRING, + _signature + ); + + maxApproveERC20( + IERC20(_permit.permitted.token), + _diamondAddress, + _permit.permitted.amount + ); + + // call diamond with provided calldata + // solhint-disable-next-line avoid-low-level-calls + (bool success, bytes memory data) = _diamondAddress.call{ + value: msg.value + }(_diamondCalldata); + // throw error to make sure tx reverts if low-level call was unsuccessful + if (!success) { + revert CallToDiamondFailed(data); + } + } +} diff --git a/test/solidity/Periphery/Permit2Proxy.t.sol b/test/solidity/Periphery/Permit2Proxy.t.sol index d1c38a8fc..51e981cbf 100644 --- a/test/solidity/Periphery/Permit2Proxy.t.sol +++ b/test/solidity/Periphery/Permit2Proxy.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; +pragma solidity ^0.8.17; import { Test, console } from "forge-std/Test.sol"; import { Permit2Proxy } from "lifi/Periphery/Permit2Proxy.sol"; From 48b97032e9263660e02ade7b097914761466ab53 Mon Sep 17 00:00:00 2001 From: Ed Zynda Date: Thu, 22 Aug 2024 19:25:28 +0300 Subject: [PATCH 03/69] forge install: Permit2 --- .gitmodules | 3 +++ lib/Permit2 | 1 + 2 files changed, 4 insertions(+) create mode 160000 lib/Permit2 diff --git a/.gitmodules b/.gitmodules index 916ca16ca..ce1a6a0ef 100644 --- a/.gitmodules +++ b/.gitmodules @@ -22,3 +22,6 @@ [submodule "lib/solady"] path = lib/solady url = https://github.com/Vectorized/solady +[submodule "lib/Permit2"] + path = lib/Permit2 + url = https://github.com/Uniswap/Permit2 diff --git a/lib/Permit2 b/lib/Permit2 new file mode 160000 index 000000000..cc56ad0f3 --- /dev/null +++ b/lib/Permit2 @@ -0,0 +1 @@ +Subproject commit cc56ad0f3439c502c246fc5cfcc3db92bb8b7219 From 9795b16729cf542e78013bfc673b63e422903ece Mon Sep 17 00:00:00 2001 From: Ed Zynda Date: Fri, 23 Aug 2024 18:48:37 +0300 Subject: [PATCH 04/69] Get basic test working... --- remappings.txt | 3 +- src/Periphery/Permit2Proxy.sol | 50 +++++--- test/solidity/Periphery/Permit2Proxy.t.sol | 137 +++++++++++++++++---- 3 files changed, 151 insertions(+), 39 deletions(-) diff --git a/remappings.txt b/remappings.txt index cf75a7770..ed6769e68 100644 --- a/remappings.txt +++ b/remappings.txt @@ -9,7 +9,8 @@ celer-network/=lib/sgn-v2-contracts/ create3-factory/=lib/create3-factory/src/ solmate/=lib/solmate/src/ solady/=lib/solady/src/ - +permit2-test-utils/=lib/Permit2/test/utils/ +permit2/=lib/Permit2/src/ ds-test/=lib/ds-test/src/ forge-std/=lib/forge-std/src/ diff --git a/src/Periphery/Permit2Proxy.sol b/src/Periphery/Permit2Proxy.sol index e39f1cfed..2a23aa894 100644 --- a/src/Periphery/Permit2Proxy.sol +++ b/src/Periphery/Permit2Proxy.sol @@ -1,29 +1,42 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.17; -import { ISignatureTransfer } from "../Interfaces/ISignatureTransfer.sol"; +import { ISignatureTransfer } from "permit2/interfaces/ISignatureTransfer.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; contract Permit2Proxy { - ISignatureTransfer immutable PERMIT2 = - ISignatureTransfer(0x000000000022D473030F116dDEE9F6B43aC78BA3); + /// Storage /// - error CallToDiamondFailed(bytes); + ISignatureTransfer public immutable PERMIT2; + + string public constant WITNESS_TYPE_STRING = + "LIFICall witness)LIFICall(address tokenReceiver,address diamondAddress,bytes32 diamondCalldataHash)TokenPermissions(address token,uint256 amount)"; + bytes32 public constant WITNESS_TYPEHASH = + keccak256( + "LIFICall(address tokenReceiver,address diamondAddress,bytes32 diamondCalldataHash)" + ); - // LIFI Specific Witness to verify + /// Types /// + + // @dev LIFI Specific Witness to verify struct LIFICall { address tokenReceiver; address diamondAddress; bytes32 diamondCalldataHash; } - string private constant WITNESS_TYPE_STRING = - "LIFICall witness)TokenPermissions(address token,uint256 amount)LIFICall(address tokenReceiver,address diamondAddress,bytes32 diamondCalldataHash)"; - bytes32 private WITNESS_TYPEHASH = - keccak256( - "LIFICall(address tokenReceiver,address diamondAddress,bytes32 diamondCalldataHash)" - ); + /// Errors /// + + error CallToDiamondFailed(bytes); + + /// Constructor /// + + constructor(ISignatureTransfer _permit2) { + PERMIT2 = _permit2; + } + + /// External Functions /// function maxApproveERC20( IERC20 assetId, @@ -47,16 +60,23 @@ contract Permit2Proxy { function diamondCallSingle( address _tokenReceiver, address _diamondAddress, - bytes32 _diamondCalldataHash, bytes calldata _diamondCalldata, address _owner, ISignatureTransfer.PermitTransferFrom calldata _permit, bytes calldata _signature ) external payable { - bytes32 witnessHash = keccak256( + LIFICall memory lifiCall = LIFICall( + _tokenReceiver, + _diamondAddress, + keccak256(_diamondCalldata) + ); + + bytes32 witness = keccak256( abi.encode( WITNESS_TYPEHASH, - LIFICall(_tokenReceiver, _diamondAddress, _diamondCalldataHash) + lifiCall.tokenReceiver, + lifiCall.diamondAddress, + lifiCall.diamondCalldataHash ) ); @@ -67,7 +87,7 @@ contract Permit2Proxy { requestedAmount: _permit.permitted.amount }), _owner, - witnessHash, + witness, WITNESS_TYPE_STRING, _signature ); diff --git a/test/solidity/Periphery/Permit2Proxy.t.sol b/test/solidity/Periphery/Permit2Proxy.t.sol index 51e981cbf..2632a6ce1 100644 --- a/test/solidity/Periphery/Permit2Proxy.t.sol +++ b/test/solidity/Periphery/Permit2Proxy.t.sol @@ -3,43 +3,134 @@ pragma solidity ^0.8.17; import { Test, console } from "forge-std/Test.sol"; import { Permit2Proxy } from "lifi/Periphery/Permit2Proxy.sol"; -import { ISignatureTransfer } from "lifi/Interfaces/ISignatureTransfer.sol"; +import { ISignatureTransfer } from "permit2/interfaces/ISignatureTransfer.sol"; +import { PermitHash } from "permit2/libraries/PermitHash.sol"; +import { ERC20 } from "../utils/TestBase.sol"; import "forge-std/console.sol"; contract Permit2ProxyTest is Test { - Permit2Proxy public permit2proxy; + using PermitHash for ISignatureTransfer.PermitTransferFrom; + address internal constant PERMIT2_ADDRESS = + 0x000000000022D473030F116dDEE9F6B43aC78BA3; + address internal constant LINK_ADDRESS = + 0x514910771AF9Ca656af840dff83E8264EcF986CA; + bytes32 internal PERMIT_WITH_WITNESS_TYPEHASH; + + Permit2Proxy internal permit2Proxy; + + ISignatureTransfer internal uniPermit2; + uint256 internal PRIVATE_KEY = 0x1234567890; + address internal USER; function setUp() public { - permit2proxy = new Permit2Proxy(); + uniPermit2 = ISignatureTransfer(PERMIT2_ADDRESS); + permit2Proxy = new Permit2Proxy(uniPermit2); + PERMIT_WITH_WITNESS_TYPEHASH = keccak256( + abi.encodePacked( + PermitHash._PERMIT_TRANSFER_FROM_WITNESS_TYPEHASH_STUB, + permit2Proxy.WITNESS_TYPE_STRING() + ) + ); vm.createSelectFork(vm.envString("ETH_NODE_URI_MAINNET"), 20261175); - console.logAddress(address(permit2proxy)); + USER = vm.addr(PRIVATE_KEY); } - function test_hardcoded_sig() public { - uint256 amount = 10 ** 18; - address token = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; - address owner = vm.addr( - 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 + function test_can_call_diamond_single() public { + // Token Permissions + ISignatureTransfer.TokenPermissions + memory tokenPermissions = ISignatureTransfer.TokenPermissions( + LINK_ADDRESS, // LINK + 100 ether + ); + bytes32 permit = getTokenPermissionsHash(tokenPermissions); + + // Witness + Permit2Proxy.LIFICall memory lifiCall = Permit2Proxy.LIFICall( + USER, + address(0x11f1), + keccak256(hex"d34db33f") ); + bytes32 witness = getWitnessHash(lifiCall); - ISignatureTransfer.PermitTransferFrom - memory transfer = ISignatureTransfer.PermitTransferFrom( - ISignatureTransfer.TokenPermissions(token, amount), + // PermitTransferWithWitness + bytes32 msgHash = getPermitWitnessTransferFromHash( + uniPermit2.DOMAIN_SEPARATOR(), + permit, + address(permit2Proxy), + 0, + type(uint256).max, + witness + ); + + (uint8 v, bytes32 r, bytes32 s) = vm.sign(PRIVATE_KEY, msgHash); + bytes memory sig = bytes.concat(r, s, bytes1(v)); + + deal(LINK_ADDRESS, USER, 10000 ether); + // Approve to Permit2 + vm.prank(USER); + ERC20(LINK_ADDRESS).approve(PERMIT2_ADDRESS, 100 ether); + + permit2Proxy.diamondCallSingle( + USER, + address(0x11f1), + hex"d34db33f", + USER, + ISignatureTransfer.PermitTransferFrom( + tokenPermissions, 0, type(uint256).max + ), + sig + ); + } + + function getTokenPermissionsHash( + ISignatureTransfer.TokenPermissions memory tokenPermissions + ) internal pure returns (bytes32) { + return + keccak256( + abi.encode( + PermitHash._TOKEN_PERMISSIONS_TYPEHASH, + tokenPermissions.token, + tokenPermissions.amount + ) ); + } - bytes - memory sig = hex"496bd11f1de6e3824f1d8032977c10f752ceb1bda1aec025b5b5a7956ffb0e182a0ae2d49de265ff334b47f7adf6233f5455b1d6d3a921ccdfcfbd4c2cab218e1b"; - console.logBytes(sig); - permit2proxy.diamondCallSingle( - address(0), - address(0), - keccak256(hex"deadbeef"), - hex"deadbeef", - owner, - transfer, - sig + function getWitnessHash( + Permit2Proxy.LIFICall memory lifiCall + ) internal view returns (bytes32) { + return + keccak256( + abi.encode( + permit2Proxy.WITNESS_TYPEHASH(), + lifiCall.tokenReceiver, + lifiCall.diamondAddress, + lifiCall.diamondCalldataHash + ) + ); + } + + function getPermitWitnessTransferFromHash( + bytes32 domainSeparator, + bytes32 permit, + address spender, + uint256 nonce, + uint256 deadline, + bytes32 witness + ) internal view returns (bytes32) { + bytes32 dataHash = keccak256( + abi.encode( + PERMIT_WITH_WITNESS_TYPEHASH, + permit, + spender, + nonce, + deadline, + witness + ) ); + + return + keccak256(abi.encodePacked("\x19\x01", domainSeparator, dataHash)); } } From 745c5c7b135b9458ea12e29c37f19e0338b33097 Mon Sep 17 00:00:00 2001 From: Ed Zynda Date: Mon, 26 Aug 2024 14:32:17 +0300 Subject: [PATCH 05/69] Add more basic tests --- src/Periphery/Permit2Proxy.sol | 82 +++--- test/solidity/Periphery/Permit2Proxy.t.sol | 303 ++++++++++++++++++--- 2 files changed, 312 insertions(+), 73 deletions(-) diff --git a/src/Periphery/Permit2Proxy.sol b/src/Periphery/Permit2Proxy.sol index 2a23aa894..ef4040597 100644 --- a/src/Periphery/Permit2Proxy.sol +++ b/src/Periphery/Permit2Proxy.sol @@ -2,13 +2,16 @@ pragma solidity ^0.8.17; import { ISignatureTransfer } from "permit2/interfaces/ISignatureTransfer.sol"; +import { TransferrableOwnership } from "lifi/Helpers/TransferrableOwnership.sol"; +import { LibAsset, IERC20 } from "lifi/Libraries/LibAsset.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -contract Permit2Proxy { +contract Permit2Proxy is TransferrableOwnership { /// Storage /// ISignatureTransfer public immutable PERMIT2; + mapping(address => bool) public diamondWhitelist; string public constant WITNESS_TYPE_STRING = "LIFICall witness)LIFICall(address tokenReceiver,address diamondAddress,bytes32 diamondCalldataHash)TokenPermissions(address token,uint256 amount)"; @@ -29,34 +32,23 @@ contract Permit2Proxy { /// Errors /// error CallToDiamondFailed(bytes); + error DiamondAddressNotWhitelisted(); + + /// Events /// + + event WhitelistUpdated(address[] addresses, bool[] values); /// Constructor /// - constructor(ISignatureTransfer _permit2) { + constructor( + address _owner, + ISignatureTransfer _permit2 + ) TransferrableOwnership(_owner) { PERMIT2 = _permit2; } /// External Functions /// - function maxApproveERC20( - IERC20 assetId, - address spender, - uint256 amount - ) internal { - if (address(assetId) == address(0)) { - return; - } - - if (assetId.allowance(address(this), spender) < amount) { - SafeERC20.safeIncreaseAllowance(IERC20(assetId), spender, 0); - SafeERC20.safeIncreaseAllowance( - IERC20(assetId), - spender, - type(uint).max - ); - } - } - function diamondCallSingle( address _tokenReceiver, address _diamondAddress, @@ -71,14 +63,7 @@ contract Permit2Proxy { keccak256(_diamondCalldata) ); - bytes32 witness = keccak256( - abi.encode( - WITNESS_TYPEHASH, - lifiCall.tokenReceiver, - lifiCall.diamondAddress, - lifiCall.diamondCalldataHash - ) - ); + bytes32 witness = keccak256(abi.encode(WITNESS_TYPEHASH, lifiCall)); PERMIT2.permitWitnessTransferFrom( _permit, @@ -92,20 +77,53 @@ contract Permit2Proxy { _signature ); - maxApproveERC20( + // maxApprove token to diamond if current allowance is insufficient + LibAsset.maxApproveERC20( IERC20(_permit.permitted.token), _diamondAddress, _permit.permitted.amount ); + _executeCalldata(_diamondAddress, _diamondCalldata); + } + + function _executeCalldata( + address diamondAddress, + bytes memory diamondCalldata + ) private { + // make sure diamondAddress is whitelisted + // this limits the usage of this Permit2Proxy contracts to only work with our diamond contracts + if (!diamondWhitelist[diamondAddress]) + revert DiamondAddressNotWhitelisted(); + // call diamond with provided calldata // solhint-disable-next-line avoid-low-level-calls - (bool success, bytes memory data) = _diamondAddress.call{ + (bool success, bytes memory data) = diamondAddress.call{ value: msg.value - }(_diamondCalldata); + }(diamondCalldata); // throw error to make sure tx reverts if low-level call was unsuccessful if (!success) { revert CallToDiamondFailed(data); } } + + /// @notice Allows to update the whitelist of diamond contracts + /// @dev Admin function + /// @param addresses Addresses to be added (true) or removed (false) from whitelist + /// @param values Values for each address that should be updated + function updateWhitelist( + address[] calldata addresses, + bool[] calldata values + ) external onlyOwner { + for (uint i; i < addresses.length; ) { + // update whitelist address value + diamondWhitelist[addresses[i]] = values[i]; + + // gas-efficient way to increase the loop counter + unchecked { + ++i; + } + } + emit WhitelistUpdated(addresses, values); + } } diff --git a/test/solidity/Periphery/Permit2Proxy.t.sol b/test/solidity/Periphery/Permit2Proxy.t.sol index 2632a6ce1..ee1c6ee23 100644 --- a/test/solidity/Periphery/Permit2Proxy.t.sol +++ b/test/solidity/Periphery/Permit2Proxy.t.sol @@ -1,14 +1,14 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.17; -import { Test, console } from "forge-std/Test.sol"; +import { Test, TestBase, DSTest, ILiFi, console, ERC20 } from "../utils/TestBase.sol"; import { Permit2Proxy } from "lifi/Periphery/Permit2Proxy.sol"; import { ISignatureTransfer } from "permit2/interfaces/ISignatureTransfer.sol"; import { PermitHash } from "permit2/libraries/PermitHash.sol"; import { ERC20 } from "../utils/TestBase.sol"; -import "forge-std/console.sol"; +import { PolygonBridgeFacet } from "lifi/Facets/PolygonBridgeFacet.sol"; -contract Permit2ProxyTest is Test { +contract Permit2ProxyTest is TestBase { using PermitHash for ISignatureTransfer.PermitTransferFrom; address internal constant PERMIT2_ADDRESS = 0x000000000022D473030F116dDEE9F6B43aC78BA3; @@ -20,40 +20,261 @@ contract Permit2ProxyTest is Test { ISignatureTransfer internal uniPermit2; uint256 internal PRIVATE_KEY = 0x1234567890; - address internal USER; + address internal PERMIT2_USER; + + /// Errors /// + + error InvalidSigner(); + error InvalidNonce(); function setUp() public { + customBlockNumberForForking = 20261175; + initTestBase(); + uniPermit2 = ISignatureTransfer(PERMIT2_ADDRESS); - permit2Proxy = new Permit2Proxy(uniPermit2); + permit2Proxy = new Permit2Proxy(address(this), uniPermit2); PERMIT_WITH_WITNESS_TYPEHASH = keccak256( abi.encodePacked( PermitHash._PERMIT_TRANSFER_FROM_WITNESS_TYPEHASH_STUB, permit2Proxy.WITNESS_TYPE_STRING() ) ); - vm.createSelectFork(vm.envString("ETH_NODE_URI_MAINNET"), 20261175); - USER = vm.addr(PRIVATE_KEY); + + address[] memory whitelist = new address[](1); + whitelist[0] = address(0x11f1); + bool[] memory allowed = new bool[](1); + allowed[0] = true; + permit2Proxy.updateWhitelist(whitelist, allowed); + PERMIT2_USER = vm.addr(PRIVATE_KEY); + vm.label(PERMIT2_USER, "Permit2 User"); + deal(LINK_ADDRESS, PERMIT2_USER, 10000 ether); + + // Infinite approve to Permit2 + vm.prank(PERMIT2_USER); + ERC20(LINK_ADDRESS).approve(PERMIT2_ADDRESS, type(uint256).max); } function test_can_call_diamond_single() public { + bytes memory diamondCalldata; + ISignatureTransfer.PermitTransferFrom memory permitTransferFrom; + bytes memory signature; + ( + diamondCalldata, + permitTransferFrom, + , + signature + ) = _getPermitWitnessTransferFromParams(); + + // Execute + permit2Proxy.diamondCallSingle( + PERMIT2_USER, + address(0x11f1), + diamondCalldata, + PERMIT2_USER, + permitTransferFrom, + signature + ); + } + + function testRevert_cannot_call_diamond_single_with_same_signature_more_than_once() + public + { + deal(LINK_ADDRESS, PERMIT2_USER, 10000 ether); + bytes memory diamondCalldata; + ISignatureTransfer.PermitTransferFrom memory permitTransferFrom; + bytes memory signature; + ( + diamondCalldata, + permitTransferFrom, + , + signature + ) = _getPermitWitnessTransferFromParams(); + + // Execute x2 + permit2Proxy.diamondCallSingle( + PERMIT2_USER, + address(0x11f1), + diamondCalldata, + PERMIT2_USER, + permitTransferFrom, + signature + ); + vm.expectRevert(InvalidNonce.selector); + permit2Proxy.diamondCallSingle( + PERMIT2_USER, + address(0x11f1), + diamondCalldata, + PERMIT2_USER, + permitTransferFrom, + signature + ); + } + + function testRevert_cannot_set_different_receiver_than_intended() public { + deal(LINK_ADDRESS, PERMIT2_USER, 10000 ether); + bytes memory diamondCalldata; + ISignatureTransfer.PermitTransferFrom memory permitTransferFrom; + bytes memory signature; + ( + diamondCalldata, + permitTransferFrom, + , + signature + ) = _getPermitWitnessTransferFromParams(); + + address MALICIOUS_RECEIVER; + + // Execute + vm.expectRevert(InvalidSigner.selector); + permit2Proxy.diamondCallSingle( + MALICIOUS_RECEIVER, + address(0x11f1), + diamondCalldata, + PERMIT2_USER, + permitTransferFrom, + signature + ); + } + + function testRevert_cannot_set_different_diamond_address_than_intended() + public + { + deal(LINK_ADDRESS, PERMIT2_USER, 10000 ether); + bytes memory diamondCalldata; + ISignatureTransfer.PermitTransferFrom memory permitTransferFrom; + bytes memory signature; + ( + diamondCalldata, + permitTransferFrom, + , + signature + ) = _getPermitWitnessTransferFromParams(); + + address MALICIOUS_CONTRACT; + + // Execute + vm.expectRevert(InvalidSigner.selector); + permit2Proxy.diamondCallSingle( + PERMIT2_USER, + MALICIOUS_CONTRACT, + diamondCalldata, + PERMIT2_USER, + permitTransferFrom, + signature + ); + } + + function testRevert_cannot_set_different_calldata_than_intended() public { + deal(LINK_ADDRESS, PERMIT2_USER, 10000 ether); + bytes memory diamondCalldata; + ISignatureTransfer.PermitTransferFrom memory permitTransferFrom; + bytes memory signature; + ( + diamondCalldata, + permitTransferFrom, + , + signature + ) = _getPermitWitnessTransferFromParams(); + + bytes memory MALICIOUS_CALLDATA; + + // Execute + vm.expectRevert(InvalidSigner.selector); + permit2Proxy.diamondCallSingle( + PERMIT2_USER, + address(0x11f1), + MALICIOUS_CALLDATA, + PERMIT2_USER, + permitTransferFrom, + signature + ); + } + + function testRevert_cannot_use_signature_from_another_wallet() public { + deal(LINK_ADDRESS, PERMIT2_USER, 10000 ether); + bytes memory diamondCalldata; + ISignatureTransfer.PermitTransferFrom memory permitTransferFrom; + bytes32 msgHash; + ( + diamondCalldata, + permitTransferFrom, + msgHash, + + ) = _getPermitWitnessTransferFromParams(); + + bytes memory signature = _signMsgHash(msgHash, 987654321); + + // Execute + vm.expectRevert(InvalidSigner.selector); + permit2Proxy.diamondCallSingle( + PERMIT2_USER, + address(0x11f1), + diamondCalldata, + PERMIT2_USER, + permitTransferFrom, + signature + ); + } + + function testRevert_cannot_transfer_more_tokens_than_intended() public { + deal(LINK_ADDRESS, PERMIT2_USER, 10000 ether); + bytes memory diamondCalldata; + ISignatureTransfer.PermitTransferFrom memory permitTransferFrom; + bytes32 msgHash; + ( + diamondCalldata, + permitTransferFrom, + msgHash, + + ) = _getPermitWitnessTransferFromParams(); + + bytes memory signature = _signMsgHash(msgHash, 987654321); + + permitTransferFrom.permitted.amount = 500 ether; + + // Execute + vm.expectRevert(InvalidSigner.selector); + permit2Proxy.diamondCallSingle( + PERMIT2_USER, + address(0x11f1), + diamondCalldata, + PERMIT2_USER, + permitTransferFrom, + signature + ); + } + + /// Helper Functions /// + + function _getPermitWitnessTransferFromParams() + internal + view + returns ( + bytes memory diamondCalldata, + ISignatureTransfer.PermitTransferFrom memory permitTransferFrom, + bytes32 msgHash, + bytes memory signature + ) + { // Token Permissions ISignatureTransfer.TokenPermissions memory tokenPermissions = ISignatureTransfer.TokenPermissions( LINK_ADDRESS, // LINK 100 ether ); - bytes32 permit = getTokenPermissionsHash(tokenPermissions); + bytes32 permit = _getTokenPermissionsHash(tokenPermissions); // Witness + diamondCalldata = _getCalldataForBridging(); Permit2Proxy.LIFICall memory lifiCall = Permit2Proxy.LIFICall( - USER, + PERMIT2_USER, address(0x11f1), - keccak256(hex"d34db33f") + keccak256(diamondCalldata) ); - bytes32 witness = getWitnessHash(lifiCall); + bytes32 witness = _getWitnessHash(lifiCall); // PermitTransferWithWitness - bytes32 msgHash = getPermitWitnessTransferFromHash( + msgHash = _getPermitWitnessTransferFromHash( uniPermit2.DOMAIN_SEPARATOR(), permit, address(permit2Proxy), @@ -62,29 +283,36 @@ contract Permit2ProxyTest is Test { witness ); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(PRIVATE_KEY, msgHash); - bytes memory sig = bytes.concat(r, s, bytes1(v)); - - deal(LINK_ADDRESS, USER, 10000 ether); - // Approve to Permit2 - vm.prank(USER); - ERC20(LINK_ADDRESS).approve(PERMIT2_ADDRESS, 100 ether); + signature = _signMsgHash(msgHash, PRIVATE_KEY); - permit2Proxy.diamondCallSingle( - USER, - address(0x11f1), - hex"d34db33f", - USER, - ISignatureTransfer.PermitTransferFrom( - tokenPermissions, - 0, - type(uint256).max - ), - sig + permitTransferFrom = ISignatureTransfer.PermitTransferFrom( + tokenPermissions, + 0, + type(uint256).max ); } - function getTokenPermissionsHash( + function _signMsgHash( + bytes32 msgHash, + uint256 privateKey + ) internal pure returns (bytes memory signature) { + (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, msgHash); + signature = bytes.concat(r, s, bytes1(v)); + } + + function _getCalldataForBridging() + private + view + returns (bytes memory diamondCalldata) + { + bytes4 selector = PolygonBridgeFacet + .startBridgeTokensViaPolygonBridge + .selector; + + diamondCalldata = abi.encodeWithSelector(selector, bridgeData); + } + + function _getTokenPermissionsHash( ISignatureTransfer.TokenPermissions memory tokenPermissions ) internal pure returns (bytes32) { return @@ -97,21 +325,14 @@ contract Permit2ProxyTest is Test { ); } - function getWitnessHash( + function _getWitnessHash( Permit2Proxy.LIFICall memory lifiCall ) internal view returns (bytes32) { return - keccak256( - abi.encode( - permit2Proxy.WITNESS_TYPEHASH(), - lifiCall.tokenReceiver, - lifiCall.diamondAddress, - lifiCall.diamondCalldataHash - ) - ); + keccak256(abi.encode(permit2Proxy.WITNESS_TYPEHASH(), lifiCall)); } - function getPermitWitnessTransferFromHash( + function _getPermitWitnessTransferFromHash( bytes32 domainSeparator, bytes32 permit, address spender, From e18643630484cec7e4cda76cdc8643c36aab4706 Mon Sep 17 00:00:00 2001 From: Ed Zynda Date: Mon, 26 Aug 2024 15:11:24 +0300 Subject: [PATCH 06/69] Remove superfluous receiver param --- src/Periphery/Permit2Proxy.sol | 14 ++-- test/solidity/Periphery/Permit2Proxy.t.sol | 80 +++++++--------------- 2 files changed, 34 insertions(+), 60 deletions(-) diff --git a/src/Periphery/Permit2Proxy.sol b/src/Periphery/Permit2Proxy.sol index ef4040597..21b3721b9 100644 --- a/src/Periphery/Permit2Proxy.sol +++ b/src/Periphery/Permit2Proxy.sol @@ -24,7 +24,6 @@ contract Permit2Proxy is TransferrableOwnership { // @dev LIFI Specific Witness to verify struct LIFICall { - address tokenReceiver; address diamondAddress; bytes32 diamondCalldataHash; } @@ -49,16 +48,21 @@ contract Permit2Proxy is TransferrableOwnership { /// External Functions /// + /// @notice Forwards a call to a whitelisted LIFI diamond + /// pulling tokens from the user using Uniswap Permit2 + /// @param _diamondAddress the diamond contract to execute the call + /// @param _diamondCalldata the calldata to execute + /// @param _signer the signer giving permission to transfer tokens + /// @param _permit the Uniswap Permit2 parameters + /// @param _signature the signature giving approval to transfer tokens function diamondCallSingle( - address _tokenReceiver, address _diamondAddress, bytes calldata _diamondCalldata, - address _owner, + address _signer, ISignatureTransfer.PermitTransferFrom calldata _permit, bytes calldata _signature ) external payable { LIFICall memory lifiCall = LIFICall( - _tokenReceiver, _diamondAddress, keccak256(_diamondCalldata) ); @@ -71,7 +75,7 @@ contract Permit2Proxy is TransferrableOwnership { to: address(this), requestedAmount: _permit.permitted.amount }), - _owner, + _signer, witness, WITNESS_TYPE_STRING, _signature diff --git a/test/solidity/Periphery/Permit2Proxy.t.sol b/test/solidity/Periphery/Permit2Proxy.t.sol index ee1c6ee23..547ff477c 100644 --- a/test/solidity/Periphery/Permit2Proxy.t.sol +++ b/test/solidity/Periphery/Permit2Proxy.t.sol @@ -10,16 +10,20 @@ import { PolygonBridgeFacet } from "lifi/Facets/PolygonBridgeFacet.sol"; contract Permit2ProxyTest is TestBase { using PermitHash for ISignatureTransfer.PermitTransferFrom; + + /// Constants /// + address internal constant PERMIT2_ADDRESS = 0x000000000022D473030F116dDEE9F6B43aC78BA3; - address internal constant LINK_ADDRESS = - 0x514910771AF9Ca656af840dff83E8264EcF986CA; - bytes32 internal PERMIT_WITH_WITNESS_TYPEHASH; + uint256 internal PRIVATE_KEY = 0x1234567890; + address internal constant DIAMOND_ADDRESS = + 0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE; - Permit2Proxy internal permit2Proxy; + /// Storage /// + bytes32 internal PERMIT_WITH_WITNESS_TYPEHASH; + Permit2Proxy internal permit2Proxy; ISignatureTransfer internal uniPermit2; - uint256 internal PRIVATE_KEY = 0x1234567890; address internal PERMIT2_USER; /// Errors /// @@ -41,17 +45,17 @@ contract Permit2ProxyTest is TestBase { ); address[] memory whitelist = new address[](1); - whitelist[0] = address(0x11f1); + whitelist[0] = DIAMOND_ADDRESS; bool[] memory allowed = new bool[](1); allowed[0] = true; permit2Proxy.updateWhitelist(whitelist, allowed); PERMIT2_USER = vm.addr(PRIVATE_KEY); vm.label(PERMIT2_USER, "Permit2 User"); - deal(LINK_ADDRESS, PERMIT2_USER, 10000 ether); + deal(ADDRESS_USDC, PERMIT2_USER, 10000 ether); // Infinite approve to Permit2 vm.prank(PERMIT2_USER); - ERC20(LINK_ADDRESS).approve(PERMIT2_ADDRESS, type(uint256).max); + ERC20(ADDRESS_USDC).approve(PERMIT2_ADDRESS, type(uint256).max); } function test_can_call_diamond_single() public { @@ -67,8 +71,7 @@ contract Permit2ProxyTest is TestBase { // Execute permit2Proxy.diamondCallSingle( - PERMIT2_USER, - address(0x11f1), + DIAMOND_ADDRESS, diamondCalldata, PERMIT2_USER, permitTransferFrom, @@ -79,7 +82,7 @@ contract Permit2ProxyTest is TestBase { function testRevert_cannot_call_diamond_single_with_same_signature_more_than_once() public { - deal(LINK_ADDRESS, PERMIT2_USER, 10000 ether); + deal(ADDRESS_USDC, PERMIT2_USER, 10000 ether); bytes memory diamondCalldata; ISignatureTransfer.PermitTransferFrom memory permitTransferFrom; bytes memory signature; @@ -92,8 +95,7 @@ contract Permit2ProxyTest is TestBase { // Execute x2 permit2Proxy.diamondCallSingle( - PERMIT2_USER, - address(0x11f1), + DIAMOND_ADDRESS, diamondCalldata, PERMIT2_USER, permitTransferFrom, @@ -101,34 +103,7 @@ contract Permit2ProxyTest is TestBase { ); vm.expectRevert(InvalidNonce.selector); permit2Proxy.diamondCallSingle( - PERMIT2_USER, - address(0x11f1), - diamondCalldata, - PERMIT2_USER, - permitTransferFrom, - signature - ); - } - - function testRevert_cannot_set_different_receiver_than_intended() public { - deal(LINK_ADDRESS, PERMIT2_USER, 10000 ether); - bytes memory diamondCalldata; - ISignatureTransfer.PermitTransferFrom memory permitTransferFrom; - bytes memory signature; - ( - diamondCalldata, - permitTransferFrom, - , - signature - ) = _getPermitWitnessTransferFromParams(); - - address MALICIOUS_RECEIVER; - - // Execute - vm.expectRevert(InvalidSigner.selector); - permit2Proxy.diamondCallSingle( - MALICIOUS_RECEIVER, - address(0x11f1), + DIAMOND_ADDRESS, diamondCalldata, PERMIT2_USER, permitTransferFrom, @@ -139,7 +114,7 @@ contract Permit2ProxyTest is TestBase { function testRevert_cannot_set_different_diamond_address_than_intended() public { - deal(LINK_ADDRESS, PERMIT2_USER, 10000 ether); + deal(ADDRESS_USDC, PERMIT2_USER, 10000 ether); bytes memory diamondCalldata; ISignatureTransfer.PermitTransferFrom memory permitTransferFrom; bytes memory signature; @@ -155,7 +130,6 @@ contract Permit2ProxyTest is TestBase { // Execute vm.expectRevert(InvalidSigner.selector); permit2Proxy.diamondCallSingle( - PERMIT2_USER, MALICIOUS_CONTRACT, diamondCalldata, PERMIT2_USER, @@ -165,7 +139,7 @@ contract Permit2ProxyTest is TestBase { } function testRevert_cannot_set_different_calldata_than_intended() public { - deal(LINK_ADDRESS, PERMIT2_USER, 10000 ether); + deal(ADDRESS_USDC, PERMIT2_USER, 10000 ether); bytes memory diamondCalldata; ISignatureTransfer.PermitTransferFrom memory permitTransferFrom; bytes memory signature; @@ -181,8 +155,7 @@ contract Permit2ProxyTest is TestBase { // Execute vm.expectRevert(InvalidSigner.selector); permit2Proxy.diamondCallSingle( - PERMIT2_USER, - address(0x11f1), + DIAMOND_ADDRESS, MALICIOUS_CALLDATA, PERMIT2_USER, permitTransferFrom, @@ -191,7 +164,7 @@ contract Permit2ProxyTest is TestBase { } function testRevert_cannot_use_signature_from_another_wallet() public { - deal(LINK_ADDRESS, PERMIT2_USER, 10000 ether); + deal(ADDRESS_USDC, PERMIT2_USER, 10000 ether); bytes memory diamondCalldata; ISignatureTransfer.PermitTransferFrom memory permitTransferFrom; bytes32 msgHash; @@ -207,8 +180,7 @@ contract Permit2ProxyTest is TestBase { // Execute vm.expectRevert(InvalidSigner.selector); permit2Proxy.diamondCallSingle( - PERMIT2_USER, - address(0x11f1), + DIAMOND_ADDRESS, diamondCalldata, PERMIT2_USER, permitTransferFrom, @@ -217,7 +189,7 @@ contract Permit2ProxyTest is TestBase { } function testRevert_cannot_transfer_more_tokens_than_intended() public { - deal(LINK_ADDRESS, PERMIT2_USER, 10000 ether); + deal(ADDRESS_USDC, PERMIT2_USER, 10000 ether); bytes memory diamondCalldata; ISignatureTransfer.PermitTransferFrom memory permitTransferFrom; bytes32 msgHash; @@ -235,8 +207,7 @@ contract Permit2ProxyTest is TestBase { // Execute vm.expectRevert(InvalidSigner.selector); permit2Proxy.diamondCallSingle( - PERMIT2_USER, - address(0x11f1), + DIAMOND_ADDRESS, diamondCalldata, PERMIT2_USER, permitTransferFrom, @@ -259,7 +230,7 @@ contract Permit2ProxyTest is TestBase { // Token Permissions ISignatureTransfer.TokenPermissions memory tokenPermissions = ISignatureTransfer.TokenPermissions( - LINK_ADDRESS, // LINK + ADDRESS_USDC, // LINK 100 ether ); bytes32 permit = _getTokenPermissionsHash(tokenPermissions); @@ -267,8 +238,7 @@ contract Permit2ProxyTest is TestBase { // Witness diamondCalldata = _getCalldataForBridging(); Permit2Proxy.LIFICall memory lifiCall = Permit2Proxy.LIFICall( - PERMIT2_USER, - address(0x11f1), + DIAMOND_ADDRESS, keccak256(diamondCalldata) ); bytes32 witness = _getWitnessHash(lifiCall); From 143d4c21f96f0424690486c6183ca1f50503c5ee Mon Sep 17 00:00:00 2001 From: Ed Zynda Date: Mon, 26 Aug 2024 15:39:54 +0300 Subject: [PATCH 07/69] Add utility method for getting a valid and working msgHash to sign --- src/Periphery/Permit2Proxy.sol | 134 ++++++++++++++++++--- test/solidity/Periphery/Permit2Proxy.t.sol | 47 +++++++- 2 files changed, 158 insertions(+), 23 deletions(-) diff --git a/src/Periphery/Permit2Proxy.sol b/src/Periphery/Permit2Proxy.sol index 21b3721b9..dbc37dc72 100644 --- a/src/Periphery/Permit2Proxy.sol +++ b/src/Periphery/Permit2Proxy.sol @@ -4,6 +4,7 @@ pragma solidity ^0.8.17; import { ISignatureTransfer } from "permit2/interfaces/ISignatureTransfer.sol"; import { TransferrableOwnership } from "lifi/Helpers/TransferrableOwnership.sol"; import { LibAsset, IERC20 } from "lifi/Libraries/LibAsset.sol"; +import { PermitHash } from "permit2/libraries/PermitHash.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; @@ -19,6 +20,7 @@ contract Permit2Proxy is TransferrableOwnership { keccak256( "LIFICall(address tokenReceiver,address diamondAddress,bytes32 diamondCalldataHash)" ); + bytes32 public immutable PERMIT_WITH_WITNESS_TYPEHASH; /// Types /// @@ -44,6 +46,13 @@ contract Permit2Proxy is TransferrableOwnership { ISignatureTransfer _permit2 ) TransferrableOwnership(_owner) { PERMIT2 = _permit2; + + PERMIT_WITH_WITNESS_TYPEHASH = keccak256( + abi.encodePacked( + PermitHash._PERMIT_TRANSFER_FROM_WITNESS_TYPEHASH_STUB, + WITNESS_TYPE_STRING + ) + ); } /// External Functions /// @@ -91,26 +100,6 @@ contract Permit2Proxy is TransferrableOwnership { _executeCalldata(_diamondAddress, _diamondCalldata); } - function _executeCalldata( - address diamondAddress, - bytes memory diamondCalldata - ) private { - // make sure diamondAddress is whitelisted - // this limits the usage of this Permit2Proxy contracts to only work with our diamond contracts - if (!diamondWhitelist[diamondAddress]) - revert DiamondAddressNotWhitelisted(); - - // call diamond with provided calldata - // solhint-disable-next-line avoid-low-level-calls - (bool success, bytes memory data) = diamondAddress.call{ - value: msg.value - }(diamondCalldata); - // throw error to make sure tx reverts if low-level call was unsuccessful - if (!success) { - revert CallToDiamondFailed(data); - } - } - /// @notice Allows to update the whitelist of diamond contracts /// @dev Admin function /// @param addresses Addresses to be added (true) or removed (false) from whitelist @@ -130,4 +119,109 @@ contract Permit2Proxy is TransferrableOwnership { } emit WhitelistUpdated(addresses, values); } + + /// @notice utitlity method for constructing a valid Permit2 message hash + /// @param _diamondAddress the diamond address to call + /// @param _diamondCalldata the calldata to execute + /// @param _assetId the address of the token to approve + /// @param _amount amount of tokens to approve + /// @param _nonce the nonce to use + /// @param _deadline the expiration deadline + function getPermit2MsgHash( + address _diamondAddress, + bytes calldata _diamondCalldata, + address _assetId, + uint256 _amount, + uint256 _nonce, + uint256 _deadline + ) external view returns (bytes32 msgHash) { + // Token Permissions + ISignatureTransfer.TokenPermissions + memory tokenPermissions = ISignatureTransfer.TokenPermissions( + _assetId, + _amount + ); + bytes32 permit = _getTokenPermissionsHash(tokenPermissions); + + // Witness + Permit2Proxy.LIFICall memory lifiCall = LIFICall( + _diamondAddress, + keccak256(_diamondCalldata) + ); + bytes32 witness = _getWitnessHash(lifiCall); + + // PermitTransferWithWitness + msgHash = _getPermitWitnessTransferFromHash( + PERMIT2.DOMAIN_SEPARATOR(), + permit, + address(this), + _nonce, + _deadline, + witness + ); + } + + /// Internal Functions /// + + function _getTokenPermissionsHash( + ISignatureTransfer.TokenPermissions memory tokenPermissions + ) internal pure returns (bytes32) { + return + keccak256( + abi.encode( + PermitHash._TOKEN_PERMISSIONS_TYPEHASH, + tokenPermissions.token, + tokenPermissions.amount + ) + ); + } + + function _getWitnessHash( + Permit2Proxy.LIFICall memory lifiCall + ) internal pure returns (bytes32) { + return keccak256(abi.encode(WITNESS_TYPEHASH, lifiCall)); + } + + function _getPermitWitnessTransferFromHash( + bytes32 domainSeparator, + bytes32 permit, + address spender, + uint256 nonce, + uint256 deadline, + bytes32 witness + ) internal view returns (bytes32) { + bytes32 dataHash = keccak256( + abi.encode( + PERMIT_WITH_WITNESS_TYPEHASH, + permit, + spender, + nonce, + deadline, + witness + ) + ); + + return + keccak256(abi.encodePacked("\x19\x01", domainSeparator, dataHash)); + } + + function _executeCalldata( + address diamondAddress, + bytes memory diamondCalldata + ) internal { + // make sure diamondAddress is whitelisted + // this limits the usage of this Permit2Proxy contracts to only work with our diamond contracts + if (!diamondWhitelist[diamondAddress]) + revert DiamondAddressNotWhitelisted(); + + // call diamond with provided calldata + // solhint-disable-next-line avoid-low-level-calls + (bool success, bytes memory data) = diamondAddress.call{ + value: msg.value + }(diamondCalldata); + // throw error to make sure tx reverts if low-level call was unsuccessful + if (!success) { + revert CallToDiamondFailed(data); + } + } } diff --git a/test/solidity/Periphery/Permit2Proxy.t.sol b/test/solidity/Periphery/Permit2Proxy.t.sol index 547ff477c..52ceae401 100644 --- a/test/solidity/Periphery/Permit2Proxy.t.sol +++ b/test/solidity/Periphery/Permit2Proxy.t.sol @@ -16,7 +16,7 @@ contract Permit2ProxyTest is TestBase { address internal constant PERMIT2_ADDRESS = 0x000000000022D473030F116dDEE9F6B43aC78BA3; uint256 internal PRIVATE_KEY = 0x1234567890; - address internal constant DIAMOND_ADDRESS = + address internal DIAMOND_ADDRESS = 0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE; /// Storage /// @@ -30,6 +30,7 @@ contract Permit2ProxyTest is TestBase { error InvalidSigner(); error InvalidNonce(); + error DiamondAddressNotWhitelisted(); function setUp() public { customBlockNumberForForking = 20261175; @@ -79,6 +80,46 @@ contract Permit2ProxyTest is TestBase { ); } + function test_can_generrate_a_valid_msg_hash_for_signing() public { + bytes32 msgHash; + bytes32 generatedMsgHash; + (, , msgHash, ) = _getPermitWitnessTransferFromParams(); + + generatedMsgHash = permit2Proxy.getPermit2MsgHash( + DIAMOND_ADDRESS, + _getCalldataForBridging(), + ADDRESS_USDC, + defaultUSDCAmount, + 0, + type(uint256).max + ); + + assertEq(msgHash, generatedMsgHash); + } + + function testRevery_cannot_call_unwhitelisted_diamond() public { + DIAMOND_ADDRESS = address(0x11f1); // Not whitelisted + bytes memory diamondCalldata; + ISignatureTransfer.PermitTransferFrom memory permitTransferFrom; + bytes memory signature; + ( + diamondCalldata, + permitTransferFrom, + , + signature + ) = _getPermitWitnessTransferFromParams(); + + // Execute + vm.expectRevert(DiamondAddressNotWhitelisted.selector); + permit2Proxy.diamondCallSingle( + DIAMOND_ADDRESS, + diamondCalldata, + PERMIT2_USER, + permitTransferFrom, + signature + ); + } + function testRevert_cannot_call_diamond_single_with_same_signature_more_than_once() public { @@ -230,8 +271,8 @@ contract Permit2ProxyTest is TestBase { // Token Permissions ISignatureTransfer.TokenPermissions memory tokenPermissions = ISignatureTransfer.TokenPermissions( - ADDRESS_USDC, // LINK - 100 ether + ADDRESS_USDC, + defaultUSDCAmount ); bytes32 permit = _getTokenPermissionsHash(tokenPermissions); From 779bd8bfb4fe7e8460b98246052ac77279ada227 Mon Sep 17 00:00:00 2001 From: Ed Zynda Date: Mon, 26 Aug 2024 15:47:18 +0300 Subject: [PATCH 08/69] Change name to be more specific --- src/Periphery/Permit2Proxy.sol | 2 +- test/solidity/Periphery/Permit2Proxy.t.sol | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Periphery/Permit2Proxy.sol b/src/Periphery/Permit2Proxy.sol index dbc37dc72..5fb008b27 100644 --- a/src/Periphery/Permit2Proxy.sol +++ b/src/Periphery/Permit2Proxy.sol @@ -64,7 +64,7 @@ contract Permit2Proxy is TransferrableOwnership { /// @param _signer the signer giving permission to transfer tokens /// @param _permit the Uniswap Permit2 parameters /// @param _signature the signature giving approval to transfer tokens - function diamondCallSingle( + function callDiamondUsingPermit2Single( address _diamondAddress, bytes calldata _diamondCalldata, address _signer, diff --git a/test/solidity/Periphery/Permit2Proxy.t.sol b/test/solidity/Periphery/Permit2Proxy.t.sol index 52ceae401..96a9596af 100644 --- a/test/solidity/Periphery/Permit2Proxy.t.sol +++ b/test/solidity/Periphery/Permit2Proxy.t.sol @@ -71,7 +71,7 @@ contract Permit2ProxyTest is TestBase { ) = _getPermitWitnessTransferFromParams(); // Execute - permit2Proxy.diamondCallSingle( + permit2Proxy.callDiamondUsingPermit2Single( DIAMOND_ADDRESS, diamondCalldata, PERMIT2_USER, @@ -111,7 +111,7 @@ contract Permit2ProxyTest is TestBase { // Execute vm.expectRevert(DiamondAddressNotWhitelisted.selector); - permit2Proxy.diamondCallSingle( + permit2Proxy.callDiamondUsingPermit2Single( DIAMOND_ADDRESS, diamondCalldata, PERMIT2_USER, @@ -135,7 +135,7 @@ contract Permit2ProxyTest is TestBase { ) = _getPermitWitnessTransferFromParams(); // Execute x2 - permit2Proxy.diamondCallSingle( + permit2Proxy.callDiamondUsingPermit2Single( DIAMOND_ADDRESS, diamondCalldata, PERMIT2_USER, @@ -143,7 +143,7 @@ contract Permit2ProxyTest is TestBase { signature ); vm.expectRevert(InvalidNonce.selector); - permit2Proxy.diamondCallSingle( + permit2Proxy.callDiamondUsingPermit2Single( DIAMOND_ADDRESS, diamondCalldata, PERMIT2_USER, @@ -170,7 +170,7 @@ contract Permit2ProxyTest is TestBase { // Execute vm.expectRevert(InvalidSigner.selector); - permit2Proxy.diamondCallSingle( + permit2Proxy.callDiamondUsingPermit2Single( MALICIOUS_CONTRACT, diamondCalldata, PERMIT2_USER, @@ -195,7 +195,7 @@ contract Permit2ProxyTest is TestBase { // Execute vm.expectRevert(InvalidSigner.selector); - permit2Proxy.diamondCallSingle( + permit2Proxy.callDiamondUsingPermit2Single( DIAMOND_ADDRESS, MALICIOUS_CALLDATA, PERMIT2_USER, @@ -220,7 +220,7 @@ contract Permit2ProxyTest is TestBase { // Execute vm.expectRevert(InvalidSigner.selector); - permit2Proxy.diamondCallSingle( + permit2Proxy.callDiamondUsingPermit2Single( DIAMOND_ADDRESS, diamondCalldata, PERMIT2_USER, @@ -247,7 +247,7 @@ contract Permit2ProxyTest is TestBase { // Execute vm.expectRevert(InvalidSigner.selector); - permit2Proxy.diamondCallSingle( + permit2Proxy.callDiamondUsingPermit2Single( DIAMOND_ADDRESS, diamondCalldata, PERMIT2_USER, From f36a67e2efb8f1a3f968b5d3e5ba65ad61f7168d Mon Sep 17 00:00:00 2001 From: Ed Zynda Date: Mon, 26 Aug 2024 16:00:06 +0300 Subject: [PATCH 09/69] Add Permit "v1" functionality --- src/Periphery/Permit2Proxy.sol | 47 +++++++++++++++++++++- test/solidity/Periphery/Permit2Proxy.t.sol | 22 +++++----- 2 files changed, 57 insertions(+), 12 deletions(-) diff --git a/src/Periphery/Permit2Proxy.sol b/src/Periphery/Permit2Proxy.sol index 5fb008b27..94915daf2 100644 --- a/src/Periphery/Permit2Proxy.sol +++ b/src/Periphery/Permit2Proxy.sol @@ -7,6 +7,7 @@ import { LibAsset, IERC20 } from "lifi/Libraries/LibAsset.sol"; import { PermitHash } from "permit2/libraries/PermitHash.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { ERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol"; contract Permit2Proxy is TransferrableOwnership { /// Storage /// @@ -57,6 +58,50 @@ contract Permit2Proxy is TransferrableOwnership { /// 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) + /// (in contrast to Permit2, calldata and diamondAddress are not signed by the user and could therefore be replaced) + /// @param tokenAddress Address of the token to be bridged + /// @param owner Owner of the tokens to be bridged + /// @param amount Amount of tokens to be bridged + /// @param deadline Transaction must be completed before this timestamp + /// @param v User signature (recovery ID) + /// @param r User signature (ECDSA output) + /// @param s User signature (ECDSA output) + /// @param diamondAddress Address of the token to be bridged + /// @param diamondCalldata Address of the token to be bridged + function callDiamondWithEIP2612Signature( + address tokenAddress, + address owner, + uint256 amount, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s, + address diamondAddress, + bytes calldata diamondCalldata + ) public payable { + // call permit function of token contract to register approval using signature + ERC20Permit(tokenAddress).permit( + owner, + address(this), + amount, + deadline, + v, + r, + s + ); + + // deposit assets + LibAsset.transferFromERC20(tokenAddress, owner, address(this), amount); + + // maxApprove token to diamond if current allowance is insufficient + LibAsset.maxApproveERC20(IERC20(tokenAddress), diamondAddress, amount); + + // call our diamond to execute calldata + _executeCalldata(diamondAddress, diamondCalldata); + } + /// @notice Forwards a call to a whitelisted LIFI diamond /// pulling tokens from the user using Uniswap Permit2 /// @param _diamondAddress the diamond contract to execute the call @@ -64,7 +109,7 @@ contract Permit2Proxy is TransferrableOwnership { /// @param _signer the signer giving permission to transfer tokens /// @param _permit the Uniswap Permit2 parameters /// @param _signature the signature giving approval to transfer tokens - function callDiamondUsingPermit2Single( + function callDiamondWithPermit2SignatureSingle( address _diamondAddress, bytes calldata _diamondCalldata, address _signer, diff --git a/test/solidity/Periphery/Permit2Proxy.t.sol b/test/solidity/Periphery/Permit2Proxy.t.sol index 96a9596af..8157f670a 100644 --- a/test/solidity/Periphery/Permit2Proxy.t.sol +++ b/test/solidity/Periphery/Permit2Proxy.t.sol @@ -71,7 +71,7 @@ contract Permit2ProxyTest is TestBase { ) = _getPermitWitnessTransferFromParams(); // Execute - permit2Proxy.callDiamondUsingPermit2Single( + permit2Proxy.callDiamondWithPermit2SignatureSingle( DIAMOND_ADDRESS, diamondCalldata, PERMIT2_USER, @@ -91,7 +91,7 @@ contract Permit2ProxyTest is TestBase { ADDRESS_USDC, defaultUSDCAmount, 0, - type(uint256).max + block.timestamp + 1000 ); assertEq(msgHash, generatedMsgHash); @@ -111,7 +111,7 @@ contract Permit2ProxyTest is TestBase { // Execute vm.expectRevert(DiamondAddressNotWhitelisted.selector); - permit2Proxy.callDiamondUsingPermit2Single( + permit2Proxy.callDiamondWithPermit2SignatureSingle( DIAMOND_ADDRESS, diamondCalldata, PERMIT2_USER, @@ -135,7 +135,7 @@ contract Permit2ProxyTest is TestBase { ) = _getPermitWitnessTransferFromParams(); // Execute x2 - permit2Proxy.callDiamondUsingPermit2Single( + permit2Proxy.callDiamondWithPermit2SignatureSingle( DIAMOND_ADDRESS, diamondCalldata, PERMIT2_USER, @@ -143,7 +143,7 @@ contract Permit2ProxyTest is TestBase { signature ); vm.expectRevert(InvalidNonce.selector); - permit2Proxy.callDiamondUsingPermit2Single( + permit2Proxy.callDiamondWithPermit2SignatureSingle( DIAMOND_ADDRESS, diamondCalldata, PERMIT2_USER, @@ -170,7 +170,7 @@ contract Permit2ProxyTest is TestBase { // Execute vm.expectRevert(InvalidSigner.selector); - permit2Proxy.callDiamondUsingPermit2Single( + permit2Proxy.callDiamondWithPermit2SignatureSingle( MALICIOUS_CONTRACT, diamondCalldata, PERMIT2_USER, @@ -195,7 +195,7 @@ contract Permit2ProxyTest is TestBase { // Execute vm.expectRevert(InvalidSigner.selector); - permit2Proxy.callDiamondUsingPermit2Single( + permit2Proxy.callDiamondWithPermit2SignatureSingle( DIAMOND_ADDRESS, MALICIOUS_CALLDATA, PERMIT2_USER, @@ -220,7 +220,7 @@ contract Permit2ProxyTest is TestBase { // Execute vm.expectRevert(InvalidSigner.selector); - permit2Proxy.callDiamondUsingPermit2Single( + permit2Proxy.callDiamondWithPermit2SignatureSingle( DIAMOND_ADDRESS, diamondCalldata, PERMIT2_USER, @@ -247,7 +247,7 @@ contract Permit2ProxyTest is TestBase { // Execute vm.expectRevert(InvalidSigner.selector); - permit2Proxy.callDiamondUsingPermit2Single( + permit2Proxy.callDiamondWithPermit2SignatureSingle( DIAMOND_ADDRESS, diamondCalldata, PERMIT2_USER, @@ -290,7 +290,7 @@ contract Permit2ProxyTest is TestBase { permit, address(permit2Proxy), 0, - type(uint256).max, + block.timestamp + 1000, witness ); @@ -299,7 +299,7 @@ contract Permit2ProxyTest is TestBase { permitTransferFrom = ISignatureTransfer.PermitTransferFrom( tokenPermissions, 0, - type(uint256).max + block.timestamp + 1000 ); } From 48de4770cc5aa4f78e88a1518e25d02e5848feb0 Mon Sep 17 00:00:00 2001 From: Ed Zynda Date: Mon, 26 Aug 2024 16:26:08 +0300 Subject: [PATCH 10/69] Add Permit "v1" tests --- test/solidity/Periphery/Permit2Proxy.t.sol | 217 +++++++++++++++++++++ 1 file changed, 217 insertions(+) diff --git a/test/solidity/Periphery/Permit2Proxy.t.sol b/test/solidity/Periphery/Permit2Proxy.t.sol index 8157f670a..a17fb46c5 100644 --- a/test/solidity/Periphery/Permit2Proxy.t.sol +++ b/test/solidity/Periphery/Permit2Proxy.t.sol @@ -7,6 +7,7 @@ import { ISignatureTransfer } from "permit2/interfaces/ISignatureTransfer.sol"; import { PermitHash } from "permit2/libraries/PermitHash.sol"; import { ERC20 } from "../utils/TestBase.sol"; import { PolygonBridgeFacet } from "lifi/Facets/PolygonBridgeFacet.sol"; +import { ERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol"; contract Permit2ProxyTest is TestBase { using PermitHash for ISignatureTransfer.PermitTransferFrom; @@ -26,6 +27,19 @@ contract Permit2ProxyTest is TestBase { ISignatureTransfer internal uniPermit2; address internal PERMIT2_USER; + /// Types /// + + struct TestDataEIP2612 { + address tokenAddress; + address userWallet; + uint256 nonce; + uint256 deadline; + bytes diamondCalldata; + uint8 v; + bytes32 r; + bytes32 s; + } + /// Errors /// error InvalidSigner(); @@ -59,6 +73,152 @@ contract Permit2ProxyTest is TestBase { ERC20(ADDRESS_USDC).approve(PERMIT2_ADDRESS, type(uint256).max); } + /// Tests /// + + /// EIP2612 (native permit) related test cases /// + + function test_can_execute_calldata_using_eip2612_signature_usdc() public { + vm.startPrank(PERMIT2_USER); + + // get token-specific domainSeparator + bytes32 domainSeparator = ERC20Permit(ADDRESS_USDC).DOMAIN_SEPARATOR(); + + // // using USDC on ETH for testing (implements EIP2612) + TestDataEIP2612 memory testdata = _getTestDataEIP2612( + ADDRESS_USDC, + domainSeparator, + block.timestamp + 1000 + ); + + // expect LifiTransferStarted event to be emitted by our diamond contract + vm.expectEmit(true, true, true, true, DIAMOND_ADDRESS); + emit LiFiTransferStarted(bridgeData); + + // call Permit2Proxy with signature + permit2Proxy.callDiamondWithEIP2612Signature( + ADDRESS_USDC, + PERMIT2_USER, + defaultUSDCAmount, + testdata.deadline, + testdata.v, + testdata.r, + testdata.s, + DIAMOND_ADDRESS, + testdata.diamondCalldata + ); + + vm.stopPrank(); + } + + function testRevertcannotUseEIP2612SignatureTwice() public { + vm.startPrank(PERMIT2_USER); + + // get token-specific domainSeparator + bytes32 domainSeparator = ERC20Permit(ADDRESS_USDC).DOMAIN_SEPARATOR(); + + // using USDC on ETH for testing (implements EIP2612) + TestDataEIP2612 memory testdata = _getTestDataEIP2612( + ADDRESS_USDC, + domainSeparator, + block.timestamp + 1000 + ); + + // call Permit2Proxy with signature + permit2Proxy.callDiamondWithEIP2612Signature( + ADDRESS_USDC, + PERMIT2_USER, + defaultUSDCAmount, + testdata.deadline, + testdata.v, + testdata.r, + testdata.s, + DIAMOND_ADDRESS, + testdata.diamondCalldata + ); + + // expect call to revert if same signature is used twice + vm.expectRevert("EIP2612: invalid signature"); + permit2Proxy.callDiamondWithEIP2612Signature( + ADDRESS_USDC, + PERMIT2_USER, + defaultUSDCAmount, + testdata.deadline, + testdata.v, + testdata.r, + testdata.s, + DIAMOND_ADDRESS, + testdata.diamondCalldata + ); + + vm.stopPrank(); + } + + function testRevertCannotUseExpiredEIP2612Signature() public { + vm.startPrank(PERMIT2_USER); + + // get token-specific domainSeparator + bytes32 domainSeparator = ERC20Permit(ADDRESS_USDC).DOMAIN_SEPARATOR(); + + // // using USDC on ETH for testing (implements EIP2612) + TestDataEIP2612 memory testdata = _getTestDataEIP2612( + ADDRESS_USDC, + domainSeparator, + block.timestamp - 1 // deadline in the past + ); + + // expect call to revert since signature deadline is in the past + vm.expectRevert("FiatTokenV2: permit is expired"); + + // call Permit2Proxy with signature + permit2Proxy.callDiamondWithEIP2612Signature( + ADDRESS_USDC, + PERMIT2_USER, + defaultUSDCAmount, + testdata.deadline, + testdata.v, + testdata.r, + testdata.s, + DIAMOND_ADDRESS, + testdata.diamondCalldata + ); + + vm.stopPrank(); + } + + function testRevertCannotUseInvalidEIP2612Signature() public { + vm.startPrank(PERMIT2_USER); + + // get token-specific domainSeparator + bytes32 domainSeparator = ERC20Permit(ADDRESS_USDC).DOMAIN_SEPARATOR(); + + // // using USDC on ETH for testing (implements EIP2612) + TestDataEIP2612 memory testdata = _getTestDataEIP2612( + ADDRESS_USDC, + domainSeparator, + block.timestamp + ); + + // expect call to revert since signature deadline is in the past + vm.expectRevert("EIP2612: invalid signature"); + + // call Permit2Proxy with signature + permit2Proxy.callDiamondWithEIP2612Signature( + ADDRESS_USDC, + PERMIT2_USER, + defaultUSDCAmount, + testdata.deadline, + testdata.v + 1, // invalid v value + testdata.r, + testdata.s, + DIAMOND_ADDRESS, + testdata.diamondCalldata + ); + + vm.stopPrank(); + } + + /// Permit2 specific tests /// + function test_can_call_diamond_single() public { bytes memory diamondCalldata; ISignatureTransfer.PermitTransferFrom memory permitTransferFrom; @@ -365,4 +525,61 @@ contract Permit2ProxyTest is TestBase { return keccak256(abi.encodePacked("\x19\x01", domainSeparator, dataHash)); } + + function _getTestDataEIP2612( + address tokenAddress, + bytes32 domainSeparator, + uint256 deadline + ) internal view returns (TestDataEIP2612 memory testdata) { + testdata.tokenAddress = tokenAddress; + testdata.userWallet = PERMIT2_USER; + testdata.nonce = ERC20Permit(tokenAddress).nonces(testdata.userWallet); + testdata.deadline = deadline; + + // generate approval data to be signed by user + bytes32 digest = _generateEIP2612MsgHash( + testdata.userWallet, + address(permit2Proxy), + defaultUSDCAmount, + testdata.nonce, + testdata.deadline, + domainSeparator + ); + + // sign digest and return signature + (testdata.v, testdata.r, testdata.s) = vm.sign(PRIVATE_KEY, digest); + + // get calldata for bridging (simple USDC bridging via PolygonBridge) + testdata.diamondCalldata = _getCalldataForBridging(); + } + + function _generateEIP2612MsgHash( + address owner, + address spender, + uint256 amount, + uint256 nonce, + uint256 deadline, + bytes32 domainSeparator + ) internal pure returns (bytes32 digest) { + digest = keccak256( + abi.encodePacked( + "\x19\x01", + // Domain separator + domainSeparator, + // Permit struct + keccak256( + abi.encode( + keccak256( + "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" + ), + owner, + spender, + amount, + nonce, + deadline + ) + ) + ) + ); + } } From d119ca9775533a16f22ac4b42c4232c45642a3f9 Mon Sep 17 00:00:00 2001 From: Ed Zynda Date: Mon, 26 Aug 2024 18:41:23 +0300 Subject: [PATCH 11/69] Add missing comments --- config/permit2.json | 4 ++ deployments/_deployments_log_file.json | 26 +++++---- deployments/arbitrum.staging.json | 3 +- script/deploy/facets/DeployPermit2Proxy.s.sol | 53 +++++++++++++++++++ src/Periphery/Permit2Proxy.sol | 28 ++++++---- 5 files changed, 94 insertions(+), 20 deletions(-) create mode 100644 config/permit2.json create mode 100644 script/deploy/facets/DeployPermit2Proxy.s.sol diff --git a/config/permit2.json b/config/permit2.json new file mode 100644 index 000000000..66e819726 --- /dev/null +++ b/config/permit2.json @@ -0,0 +1,4 @@ +{ + "mainnet": "0x000000000022D473030F116dDEE9F6B43aC78BA3", + "arbitrum": "0x000000000022D473030F116dDEE9F6B43aC78BA3" +} \ No newline at end of file diff --git a/deployments/_deployments_log_file.json b/deployments/_deployments_log_file.json index c72d137a0..a02e0eb63 100644 --- a/deployments/_deployments_log_file.json +++ b/deployments/_deployments_log_file.json @@ -21793,16 +21793,6 @@ "VERIFIED": "true" } ], - "1.0.1": [ - { - "ADDRESS": "0x6e378C84e657C57b2a8d183CFf30ee5CC8989b61", - "OPTIMIZER_RUNS": "1000000", - "TIMESTAMP": "2024-08-14 18:28:47", - "CONSTRUCTOR_ARGS": "0x0000000000000000000000006ce9bf8cdab780416ad1fd87b318a077d2f50eac", - "SALT": "", - "VERIFIED": "true" - } - ], "1.0.1": [ { "ADDRESS": "0x6e378C84e657C57b2a8d183CFf30ee5CC8989b61", @@ -22407,5 +22397,21 @@ ] } } + }, + "Permit2Proxy": { + "arbitrum": { + "staging": { + "\u001b[31m[error] '@custom:version' string not found in src/Periphery/Permit2Proxy.sol\u001b[0m": [ + { + "ADDRESS": "0x442BBFD6a4641B2b710DFfa4754081eC7502a3F7", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-08-26 18:34:52", + "CONSTRUCTOR_ARGS": "0x00000000000000000000000011f1022ca6adef6400e5677528a80d49a069c00c000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba3", + "SALT": "09072024", + "VERIFIED": "true" + } + ] + } + } } } diff --git a/deployments/arbitrum.staging.json b/deployments/arbitrum.staging.json index a6402499e..4a9f4e76d 100644 --- a/deployments/arbitrum.staging.json +++ b/deployments/arbitrum.staging.json @@ -44,5 +44,6 @@ "CircleBridgeFacet": "0xa73a8BC8d36472269138c3233e24D0Ee0c344bd8", "HopFacetOptimized": "0xf82135385765f1324257ffF74489F16382EBBb8A", "LiFuelFeeCollector": "0x94EA56D8049e93E0308B9c7d1418Baf6A7C68280", - "TokenWrapper": "0xF63b27AE2Dc887b88f82E2Cc597d07fBB2E78E70" + "TokenWrapper": "0xF63b27AE2Dc887b88f82E2Cc597d07fBB2E78E70", + "Permit2Proxy": "0x442BBFD6a4641B2b710DFfa4754081eC7502a3F7" } \ No newline at end of file diff --git a/script/deploy/facets/DeployPermit2Proxy.s.sol b/script/deploy/facets/DeployPermit2Proxy.s.sol new file mode 100644 index 000000000..14b54689f --- /dev/null +++ b/script/deploy/facets/DeployPermit2Proxy.s.sol @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.17; + +import { DeployScriptBase } from "./utils/DeployScriptBase.sol"; +import { Permit2Proxy } from "lifi/Periphery/Permit2Proxy.sol"; +import { stdJson } from "forge-std/Script.sol"; + +contract DeployScript is DeployScriptBase { + using stdJson for string; + + constructor() DeployScriptBase("Permit2Proxy") {} + + function run() + public + returns (Permit2Proxy deployed, bytes memory constructorArgs) + { + constructorArgs = getConstructorArgs(); + + deployed = Permit2Proxy(deploy(type(Permit2Proxy).creationCode)); + } + + function getConstructorArgs() internal override returns (bytes memory) { + // get path of global config file + string memory globalConfigPath = string.concat( + root, + "/config/global.json" + ); + + // read file into json variable + string memory globalConfigJson = vm.readFile(globalConfigPath); + + // extract refundWallet address + address deployWalletAddress = globalConfigJson.readAddress( + ".deployerWallet" + ); + + // get path of permit2 config file + string memory permit2ProxyConfig = string.concat( + root, + "/config/permit2.json" + ); + + // read file into json variable + string memory permit2ProxyConfigJSON = vm.readFile(permit2ProxyConfig); + + // extract wrapped token address for the given network + address permit2Address = permit2ProxyConfigJSON.readAddress( + string.concat(".", network) + ); + + return abi.encode(deployWalletAddress, permit2Address); + } +} diff --git a/src/Periphery/Permit2Proxy.sol b/src/Periphery/Permit2Proxy.sol index 94915daf2..310ec95f8 100644 --- a/src/Periphery/Permit2Proxy.sol +++ b/src/Periphery/Permit2Proxy.sol @@ -9,6 +9,11 @@ import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { ERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol"; +/// @title Permit2Proxy +/// @author LI.FI (https://li.fi) +/// @notice Proxy contract allowing gasless (Permit2-enabled) calls to our +/// diamond contract +/// @custom:version 1.0.0 contract Permit2Proxy is TransferrableOwnership { /// Storage /// @@ -58,9 +63,10 @@ contract Permit2Proxy is TransferrableOwnership { /// 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) - /// (in contrast to Permit2, calldata and diamondAddress are not signed by the user and could therefore be replaced) + /// @notice Allows to bridge tokens through a LI.FI diamond contract using + /// an EIP2612 gasless permit (only works with tokenAddresses that + /// implement EIP2612) (in contrast to Permit2, calldata and diamondAddress + /// are not signed by the user and could therefore be replaced) /// @param tokenAddress Address of the token to be bridged /// @param owner Owner of the tokens to be bridged /// @param amount Amount of tokens to be bridged @@ -81,7 +87,7 @@ contract Permit2Proxy is TransferrableOwnership { address diamondAddress, bytes calldata diamondCalldata ) public payable { - // call permit function of token contract to register approval using signature + // call permit on token contract to register approval using signature ERC20Permit(tokenAddress).permit( owner, address(this), @@ -102,8 +108,9 @@ contract Permit2Proxy is TransferrableOwnership { _executeCalldata(diamondAddress, diamondCalldata); } - /// @notice Forwards a call to a whitelisted LIFI diamond - /// pulling tokens from the user using Uniswap Permit2 + /// @notice Allows to bridge tokens of one type through a LI.FI diamond + /// contract using Uniswap's Permit2 contract and a user signature + /// that verifies allowance, diamondAddress and diamondCalldata /// @param _diamondAddress the diamond contract to execute the call /// @param _diamondCalldata the calldata to execute /// @param _signer the signer giving permission to transfer tokens @@ -147,7 +154,8 @@ contract Permit2Proxy is TransferrableOwnership { /// @notice Allows to update the whitelist of diamond contracts /// @dev Admin function - /// @param addresses Addresses to be added (true) or removed (false) from whitelist + /// @param addresses Addresses to be added (true) or removed (false) from + /// whitelist /// @param values Values for each address that should be updated function updateWhitelist( address[] calldata addresses, @@ -255,7 +263,8 @@ contract Permit2Proxy is TransferrableOwnership { bytes memory diamondCalldata ) internal { // make sure diamondAddress is whitelisted - // this limits the usage of this Permit2Proxy contracts to only work with our diamond contracts + // this limits the usage of this Permit2Proxy contracts to only work + // with our diamond contracts if (!diamondWhitelist[diamondAddress]) revert DiamondAddressNotWhitelisted(); @@ -264,7 +273,8 @@ contract Permit2Proxy is TransferrableOwnership { (bool success, bytes memory data) = diamondAddress.call{ value: msg.value }(diamondCalldata); - // throw error to make sure tx reverts if low-level call was unsuccessful + // throw error to make sure tx reverts if low-level call was + // unsuccessful if (!success) { revert CallToDiamondFailed(data); } From 484c91817f26bdcb21a02b92bc93fab0a33698b0 Mon Sep 17 00:00:00 2001 From: Ed Zynda Date: Mon, 26 Aug 2024 19:26:37 +0300 Subject: [PATCH 12/69] Flesh out demo script --- script/demoScripts/demoPermit2Proxy.ts | 44 ++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 script/demoScripts/demoPermit2Proxy.ts diff --git a/script/demoScripts/demoPermit2Proxy.ts b/script/demoScripts/demoPermit2Proxy.ts new file mode 100644 index 000000000..145ab661d --- /dev/null +++ b/script/demoScripts/demoPermit2Proxy.ts @@ -0,0 +1,44 @@ +import { http, createPublicClient, parseAbi } from 'viem' +import { arbitrum } from 'viem/chains' +import dotenv from 'dotenv' +dotenv.config() + +const PERMIT2_PROXY_ADDRESS = '0x442BBFD6a4641B2b710DFfa4754081eC7502a3F7' + +const main = async () => { + const abi = parseAbi([ + 'function getPermit2MsgHash(address,bytes,address,uint256,uint256,uint256)', + ]) + + const client = createPublicClient({ + chain: arbitrum, + transport: http(), + }) + + // Get calldata to bridge UNI from LIFI API + + // Get nonce + + // + + // Pass args and figure out msg hash + const msgHash = await client.readContract({ + address: PERMIT2_PROXY_ADDRESS, + abi, + functionName: 'getPermi2MsgHash', + args: [], + }) + + // Sign msg hash + + // Call proxy with signature +} + +main() + .then(() => { + console.log('Done!') + }) + .catch((err) => { + console.error(err) + process.exit(1) + }) From f582cbcd05202d0e33a2abfc57a306f02f041398 Mon Sep 17 00:00:00 2001 From: Ed Zynda Date: Tue, 27 Aug 2024 15:07:41 +0300 Subject: [PATCH 13/69] Finish demo script --- package.json | 1 + script/demoScripts/demoPermit2Proxy.ts | 137 +++++++++++++++++++------ tsconfig.json | 12 ++- yarn.lock | 8 ++ 4 files changed, 122 insertions(+), 36 deletions(-) diff --git a/package.json b/package.json index 01d99889d..4e42db18b 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "@types/pino": "^7.0.5", "@typescript-eslint/eslint-plugin": "^5.16.0", "@typescript-eslint/parser": "^7.10.0", + "@uniswap/permit2-sdk": "^1.3.0", "cross-env": "^7.0.2", "dotenv": "^16.0.0", "eslint": "^8.11.0", diff --git a/script/demoScripts/demoPermit2Proxy.ts b/script/demoScripts/demoPermit2Proxy.ts index 145ab661d..f3c7986e2 100644 --- a/script/demoScripts/demoPermit2Proxy.ts +++ b/script/demoScripts/demoPermit2Proxy.ts @@ -1,44 +1,117 @@ -import { http, createPublicClient, parseAbi } from 'viem' +import { + http, + createPublicClient, + parseAbi, + Hex, + parseUnits, + serializeSignature, + createWalletClient, +} from 'viem' +import { privateKeyToAccount, sign } from 'viem/accounts' import { arbitrum } from 'viem/chains' -import dotenv from 'dotenv' -dotenv.config() +import { defineCommand, runMain } from 'citty' +const DIAMOND_ADDRESS = '0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE' +const USDT_ADDRESS = '0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9' const PERMIT2_PROXY_ADDRESS = '0x442BBFD6a4641B2b710DFfa4754081eC7502a3F7' +const PERMIT2_ADDRESS = '0x000000000022D473030F116dDEE9F6B43aC78BA3' +const PRIVATE_KEY = `0x${process.env.PRIVATE_KEY}` -const main = async () => { - const abi = parseAbi([ - 'function getPermit2MsgHash(address,bytes,address,uint256,uint256,uint256)', - ]) +const main = defineCommand({ + meta: { + name: 'demo-permit2', + description: 'Demonstrate a Permit2 tx', + }, + args: { + signerKey: { + type: 'string', + description: 'Private key of signer', + }, + executorKey: { + type: 'string', + description: 'Private key of the executor', + }, + }, + async run({ args }) { + const SIGNER_PRIVATE_KEY = `0x${args.signerKey}` as Hex + const EXECUTOR_PRIVATE_KEY = `0x${args.executorKey}` as Hex - const client = createPublicClient({ - chain: arbitrum, - transport: http(), - }) + const permit2Abi = parseAbi([ + 'function nonceBitmap(address owner, uint256 index) external view returns (uint256 nonce)', + ]) + const permit2ProxyAbi = parseAbi([ + 'function getPermit2MsgHash(address,bytes,address,uint256,uint256,uint256) external view returns (bytes32)', + 'function callDiamondWithPermit2SignatureSingle(address,bytes,address,((address,uint256),uint256,uint256),bytes) external', + ]) - // Get calldata to bridge UNI from LIFI API + const client = createPublicClient({ + chain: arbitrum, + transport: http(), + }) - // Get nonce + // Account + const account = privateKeyToAccount(SIGNER_PRIVATE_KEY) - // + // Get calldata to bridge UNI from LIFI API + const url = + 'https://li.quest/v1/quote?fromChain=ARB&toChain=POL&fromToken=0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9&toToken=0xc2132D05D31c914a87C6611C10748AEb04B58e8F&fromAddress=0xb9c0dE368BECE5e76B52545a8E377a4C118f597B&toAddress=0xb9c0dE368BECE5e76B52545a8E377a4C118f597B&fromAmount=5000000' + const options = { method: 'GET', headers: { accept: 'application/json' } } - // Pass args and figure out msg hash - const msgHash = await client.readContract({ - address: PERMIT2_PROXY_ADDRESS, - abi, - functionName: 'getPermi2MsgHash', - args: [], - }) + const lifiResp = await fetch(url, options) - // Sign msg hash + const calldata = (await lifiResp.json()).transactionRequest.data - // Call proxy with signature -} + // Get nonce + const nonce = await client.readContract({ + address: PERMIT2_ADDRESS, + abi: permit2Abi, + functionName: 'nonceBitmap', + args: [account.address, 0n], + }) -main() - .then(() => { - console.log('Done!') - }) - .catch((err) => { - console.error(err) - process.exit(1) - }) + // Get block + const block = await client.getBlock() + + // Pass args and figure out msg hash + const msgHash = await client.readContract({ + address: PERMIT2_PROXY_ADDRESS, + abi: permit2ProxyAbi, + functionName: 'getPermit2MsgHash', + args: [ + DIAMOND_ADDRESS, + calldata, + USDT_ADDRESS, + parseUnits('5', 6), + nonce, + block.timestamp + 1200n, + ], + }) + + console.log(msgHash) + + // Sign msg hash + const rsvSig = await sign({ hash: msgHash, privateKey: SIGNER_PRIVATE_KEY }) + const signature = serializeSignature(rsvSig) + + // Call proxy with signature + console.log(signature) + + const tokenPermissions = [USDT_ADDRESS, parseUnits('5', 6)] + const permit = [tokenPermissions, nonce, block.timestamp + 1200n] + + const executorAccount = privateKeyToAccount(EXECUTOR_PRIVATE_KEY) + const walletClient = createWalletClient({ + account: executorAccount, + chain: arbitrum, + transport: http(), + }) + const tx = await walletClient.writeContract({ + address: PERMIT2_PROXY_ADDRESS, + abi: permit2ProxyAbi, + functionName: 'callDiamondWithPermit2SignatureSingle', + args: [DIAMOND_ADDRESS, calldata, account.address, permit, signature], + }) + }, +}) + +runMain(main) diff --git a/tsconfig.json b/tsconfig.json index 5fa2df4a1..5f60ec360 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "es5", + "target": "es2020", "module": "commonjs", "strict": true, "esModuleInterop": true, @@ -8,8 +8,12 @@ "forceConsistentCasingInFileNames": true, "outDir": "dist", "resolveJsonModule": true, - "lib": ["es2015"], - "types": ["node"] + "lib": [ + "es2020" + ], + "types": [ + "node" + ] }, "include": [ "hardhat.config.ts", @@ -19,4 +23,4 @@ "typechain/**/*", "config" ] -} +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 4b7829c2c..43ce87a74 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2065,6 +2065,14 @@ immer "^9.0.7" lodash-es "^4.17.21" +"@uniswap/permit2-sdk@^1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@uniswap/permit2-sdk/-/permit2-sdk-1.3.0.tgz#b54124e570f0adbaca9d39b2de3054fd7d3798a1" + integrity sha512-LstYQWP47dwpQrgqBJ+ysFstne9LgI5FGiKHc2ewjj91MTY8Mq1reocu6U/VDncdR5ef30TUOcZ7gPExRY8r6Q== + dependencies: + ethers "^5.7.0" + tiny-invariant "^1.1.0" + "@uniswap/sdk@^3.0.3": version "3.0.3" resolved "https://registry.yarnpkg.com/@uniswap/sdk/-/sdk-3.0.3.tgz#8201c7c72215d0030cb99acc7e661eff895c18a9" From 9785369b0ea4902d7c9d6c0bc0bbaf764a8e84a0 Mon Sep 17 00:00:00 2001 From: Ed Zynda Date: Tue, 27 Aug 2024 15:15:54 +0300 Subject: [PATCH 14/69] Cleanup and comments --- script/demoScripts/demoPermit2Proxy.ts | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/script/demoScripts/demoPermit2Proxy.ts b/script/demoScripts/demoPermit2Proxy.ts index f3c7986e2..4eade89ab 100644 --- a/script/demoScripts/demoPermit2Proxy.ts +++ b/script/demoScripts/demoPermit2Proxy.ts @@ -26,16 +26,19 @@ const main = defineCommand({ signerKey: { type: 'string', description: 'Private key of signer', + required: true, }, executorKey: { type: 'string', description: 'Private key of the executor', + required: true, }, }, async run({ args }) { const SIGNER_PRIVATE_KEY = `0x${args.signerKey}` as Hex const EXECUTOR_PRIVATE_KEY = `0x${args.executorKey}` as Hex + // Setup the required ABIs const permit2Abi = parseAbi([ 'function nonceBitmap(address owner, uint256 index) external view returns (uint256 nonce)', ]) @@ -44,24 +47,23 @@ const main = defineCommand({ 'function callDiamondWithPermit2SignatureSingle(address,bytes,address,((address,uint256),uint256,uint256),bytes) external', ]) + // Setup a READ-ONLY client const client = createPublicClient({ chain: arbitrum, transport: http(), }) - // Account + // Setup a signer account const account = privateKeyToAccount(SIGNER_PRIVATE_KEY) - // Get calldata to bridge UNI from LIFI API + // Get calldata to bridge USDT from LIFI API const url = 'https://li.quest/v1/quote?fromChain=ARB&toChain=POL&fromToken=0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9&toToken=0xc2132D05D31c914a87C6611C10748AEb04B58e8F&fromAddress=0xb9c0dE368BECE5e76B52545a8E377a4C118f597B&toAddress=0xb9c0dE368BECE5e76B52545a8E377a4C118f597B&fromAmount=5000000' const options = { method: 'GET', headers: { accept: 'application/json' } } - const lifiResp = await fetch(url, options) - const calldata = (await lifiResp.json()).transactionRequest.data - // Get nonce + // Get the nonce from the PERMIT2 contract const nonce = await client.readContract({ address: PERMIT2_ADDRESS, abi: permit2Abi, @@ -69,10 +71,10 @@ const main = defineCommand({ args: [account.address, 0n], }) - // Get block + // Get lastest block const block = await client.getBlock() - // Pass args and figure out msg hash + // Consturct a valid message hash to sign using Permit2Proxy's utility func const msgHash = await client.readContract({ address: PERMIT2_PROXY_ADDRESS, abi: permit2ProxyAbi, @@ -86,25 +88,26 @@ const main = defineCommand({ block.timestamp + 1200n, ], }) - console.log(msgHash) - // Sign msg hash + // Sign the message hash const rsvSig = await sign({ hash: msgHash, privateKey: SIGNER_PRIVATE_KEY }) const signature = serializeSignature(rsvSig) - - // Call proxy with signature console.log(signature) + // Setup the parameters for the executor to call const tokenPermissions = [USDT_ADDRESS, parseUnits('5', 6)] const permit = [tokenPermissions, nonce, block.timestamp + 1200n] + // Instantiate the executor account and a WRITE enabled client const executorAccount = privateKeyToAccount(EXECUTOR_PRIVATE_KEY) const walletClient = createWalletClient({ account: executorAccount, chain: arbitrum, transport: http(), }) + + // Execute using the Permit2 Proxy const tx = await walletClient.writeContract({ address: PERMIT2_PROXY_ADDRESS, abi: permit2ProxyAbi, From 4cd03e02c2622ed973b808c10eb9951184d86b7d Mon Sep 17 00:00:00 2001 From: Ed Zynda Date: Tue, 27 Aug 2024 15:27:06 +0300 Subject: [PATCH 15/69] Fix log --- deployments/_deployments_log_file.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deployments/_deployments_log_file.json b/deployments/_deployments_log_file.json index a02e0eb63..a312f04ce 100644 --- a/deployments/_deployments_log_file.json +++ b/deployments/_deployments_log_file.json @@ -22401,7 +22401,7 @@ "Permit2Proxy": { "arbitrum": { "staging": { - "\u001b[31m[error] '@custom:version' string not found in src/Periphery/Permit2Proxy.sol\u001b[0m": [ + "1.0.0": [ { "ADDRESS": "0x442BBFD6a4641B2b710DFfa4754081eC7502a3F7", "OPTIMIZER_RUNS": "1000000", @@ -22414,4 +22414,4 @@ } } } -} +} \ No newline at end of file From f18dab6e88a46c0e7d298eb57ec00d86e5e97a08 Mon Sep 17 00:00:00 2001 From: Ed Zynda Date: Tue, 27 Aug 2024 15:30:25 +0300 Subject: [PATCH 16/69] Remove extra files --- src/Interfaces/IEIP712.sol | 6 -- src/Interfaces/ISignatureTransfer.sol | 138 -------------------------- 2 files changed, 144 deletions(-) delete mode 100644 src/Interfaces/IEIP712.sol delete mode 100644 src/Interfaces/ISignatureTransfer.sol diff --git a/src/Interfaces/IEIP712.sol b/src/Interfaces/IEIP712.sol deleted file mode 100644 index 1a5be726b..000000000 --- a/src/Interfaces/IEIP712.sol +++ /dev/null @@ -1,6 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.17; - -interface IEIP712 { - function DOMAIN_SEPARATOR() external view returns (bytes32); -} diff --git a/src/Interfaces/ISignatureTransfer.sol b/src/Interfaces/ISignatureTransfer.sol deleted file mode 100644 index 9b379e881..000000000 --- a/src/Interfaces/ISignatureTransfer.sol +++ /dev/null @@ -1,138 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.17; - -import { IEIP712 } from "./IEIP712.sol"; - -/// @title SignatureTransfer -/// @notice Handles ERC20 token transfers through signature based actions -/// @dev Requires user's token approval on the Permit2 contract -interface ISignatureTransfer is IEIP712 { - /// @notice Thrown when the requested amount for a transfer is larger than the permissioned amount - /// @param maxAmount The maximum amount a spender can request to transfer - error InvalidAmount(uint256 maxAmount); - - /// @notice Thrown when the number of tokens permissioned to a spender does not match the number of tokens being transferred - /// @dev If the spender does not need to transfer the number of tokens permitted, the spender can request amount 0 to be transferred - error LengthMismatch(); - - /// @notice Emits an event when the owner successfully invalidates an unordered nonce. - event UnorderedNonceInvalidation( - address indexed owner, - uint256 word, - uint256 mask - ); - - /// @notice The token and amount details for a transfer signed in the permit transfer signature - struct TokenPermissions { - // ERC20 token address - address token; - // the maximum amount that can be spent - uint256 amount; - } - - /// @notice The signed permit message for a single token transfer - struct PermitTransferFrom { - TokenPermissions permitted; - // a unique value for every token owner's signature to prevent signature replays - uint256 nonce; - // deadline on the permit signature - uint256 deadline; - } - - /// @notice Specifies the recipient address and amount for batched transfers. - /// @dev Recipients and amounts correspond to the index of the signed token permissions array. - /// @dev Reverts if the requested amount is greater than the permitted signed amount. - struct SignatureTransferDetails { - // recipient address - address to; - // spender requested amount - uint256 requestedAmount; - } - - /// @notice Used to reconstruct the signed permit message for multiple token transfers - /// @dev Do not need to pass in spender address as it is required that it is msg.sender - /// @dev Note that a user still signs over a spender address - struct PermitBatchTransferFrom { - // the tokens and corresponding amounts permitted for a transfer - TokenPermissions[] permitted; - // a unique value for every token owner's signature to prevent signature replays - uint256 nonce; - // deadline on the permit signature - uint256 deadline; - } - - /// @notice A map from token owner address and a caller specified word index to a bitmap. Used to set bits in the bitmap to prevent against signature replay protection - /// @dev Uses unordered nonces so that permit messages do not need to be spent in a certain order - /// @dev The mapping is indexed first by the token owner, then by an index specified in the nonce - /// @dev It returns a uint256 bitmap - /// @dev The index, or wordPosition is capped at type(uint248).max - function nonceBitmap(address, uint256) external view returns (uint256); - - /// @notice Transfers a token using a signed permit message - /// @dev Reverts if the requested amount is greater than the permitted signed amount - /// @param permit The permit data signed over by the owner - /// @param owner The owner of the tokens to transfer - /// @param transferDetails The spender's requested transfer details for the permitted token - /// @param signature The signature to verify - function permitTransferFrom( - PermitTransferFrom memory permit, - SignatureTransferDetails calldata transferDetails, - address owner, - bytes calldata signature - ) external; - - /// @notice Transfers a token using a signed permit message - /// @notice Includes extra data provided by the caller to verify signature over - /// @dev The witness type string must follow EIP712 ordering of nested structs and must include the TokenPermissions type definition - /// @dev Reverts if the requested amount is greater than the permitted signed amount - /// @param permit The permit data signed over by the owner - /// @param owner The owner of the tokens to transfer - /// @param transferDetails The spender's requested transfer details for the permitted token - /// @param witness Extra data to include when checking the user signature - /// @param witnessTypeString The EIP-712 type definition for remaining string stub of the typehash - /// @param signature The signature to verify - function permitWitnessTransferFrom( - PermitTransferFrom memory permit, - SignatureTransferDetails calldata transferDetails, - address owner, - bytes32 witness, - string calldata witnessTypeString, - bytes calldata signature - ) external; - - /// @notice Transfers multiple tokens using a signed permit message - /// @param permit The permit data signed over by the owner - /// @param owner The owner of the tokens to transfer - /// @param transferDetails Specifies the recipient and requested amount for the token transfer - /// @param signature The signature to verify - function permitTransferFrom( - PermitBatchTransferFrom memory permit, - SignatureTransferDetails[] calldata transferDetails, - address owner, - bytes calldata signature - ) external; - - /// @notice Transfers multiple tokens using a signed permit message - /// @dev The witness type string must follow EIP712 ordering of nested structs and must include the TokenPermissions type definition - /// @notice Includes extra data provided by the caller to verify signature over - /// @param permit The permit data signed over by the owner - /// @param owner The owner of the tokens to transfer - /// @param transferDetails Specifies the recipient and requested amount for the token transfer - /// @param witness Extra data to include when checking the user signature - /// @param witnessTypeString The EIP-712 type definition for remaining string stub of the typehash - /// @param signature The signature to verify - function permitWitnessTransferFrom( - PermitBatchTransferFrom memory permit, - SignatureTransferDetails[] calldata transferDetails, - address owner, - bytes32 witness, - string calldata witnessTypeString, - bytes calldata signature - ) external; - - /// @notice Invalidates the bits specified in mask for the bitmap at the word position - /// @dev The wordPos is maxed at type(uint248).max - /// @param wordPos A number to index the nonceBitmap at - /// @param mask A bitmap masked against msg.sender's current bitmap at the word position - function invalidateUnorderedNonces(uint256 wordPos, uint256 mask) external; -} From 0eaa7c867b979e59f8e720cc8cde7d231ce15f60 Mon Sep 17 00:00:00 2001 From: Ed Zynda Date: Tue, 27 Aug 2024 15:32:01 +0300 Subject: [PATCH 17/69] Remove extra remapping --- remappings.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/remappings.txt b/remappings.txt index ed6769e68..d4a7a4506 100644 --- a/remappings.txt +++ b/remappings.txt @@ -9,7 +9,6 @@ celer-network/=lib/sgn-v2-contracts/ create3-factory/=lib/create3-factory/src/ solmate/=lib/solmate/src/ solady/=lib/solady/src/ -permit2-test-utils/=lib/Permit2/test/utils/ permit2/=lib/Permit2/src/ ds-test/=lib/ds-test/src/ forge-std/=lib/forge-std/src/ From a3e6f5dffdad0d273cc99997142ebacf736d3d3f Mon Sep 17 00:00:00 2001 From: Ed Zynda Date: Tue, 27 Aug 2024 15:33:41 +0300 Subject: [PATCH 18/69] Remove unneeded lib --- package.json | 1 - yarn.lock | 8 -------- 2 files changed, 9 deletions(-) diff --git a/package.json b/package.json index 4e42db18b..01d99889d 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,6 @@ "@types/pino": "^7.0.5", "@typescript-eslint/eslint-plugin": "^5.16.0", "@typescript-eslint/parser": "^7.10.0", - "@uniswap/permit2-sdk": "^1.3.0", "cross-env": "^7.0.2", "dotenv": "^16.0.0", "eslint": "^8.11.0", diff --git a/yarn.lock b/yarn.lock index 43ce87a74..4b7829c2c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2065,14 +2065,6 @@ immer "^9.0.7" lodash-es "^4.17.21" -"@uniswap/permit2-sdk@^1.3.0": - version "1.3.0" - resolved "https://registry.yarnpkg.com/@uniswap/permit2-sdk/-/permit2-sdk-1.3.0.tgz#b54124e570f0adbaca9d39b2de3054fd7d3798a1" - integrity sha512-LstYQWP47dwpQrgqBJ+ysFstne9LgI5FGiKHc2ewjj91MTY8Mq1reocu6U/VDncdR5ef30TUOcZ7gPExRY8r6Q== - dependencies: - ethers "^5.7.0" - tiny-invariant "^1.1.0" - "@uniswap/sdk@^3.0.3": version "3.0.3" resolved "https://registry.yarnpkg.com/@uniswap/sdk/-/sdk-3.0.3.tgz#8201c7c72215d0030cb99acc7e661eff895c18a9" From cfd19775d2c467b3cbc44ad19a45662770efd72b Mon Sep 17 00:00:00 2001 From: Ed Zynda Date: Tue, 27 Aug 2024 15:50:48 +0300 Subject: [PATCH 19/69] Add official Permit2 addresses --- config/permit2.json | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/config/permit2.json b/config/permit2.json index 66e819726..c4ac60ac2 100644 --- a/config/permit2.json +++ b/config/permit2.json @@ -1,4 +1,31 @@ { "mainnet": "0x000000000022D473030F116dDEE9F6B43aC78BA3", - "arbitrum": "0x000000000022D473030F116dDEE9F6B43aC78BA3" + "arbitrum": "0x000000000022D473030F116dDEE9F6B43aC78BA3", + "aurora": "", + "avalanche": "0x000000000022D473030F116dDEE9F6B43aC78BA3", + "base": "0x000000000022D473030F116dDEE9F6B43aC78BA3", + "blast": "0x000000000022d473030f116ddee9f6b43ac78ba3", + "boba": "", + "bsc": "0x000000000022D473030F116dDEE9F6B43aC78BA3", + "celo": "0x000000000022D473030F116dDEE9F6B43aC78BA3", + "fantom": "", + "fraxtal": "", + "fuse": "", + "gnosis": "", + "gravity": "", + "immutablezkevm": "", + "linea": "", + "mantle": "", + "metis": "", + "mode": "", + "moonbeam": "", + "moonriver": "", + "optimism": "0x000000000022D473030F116dDEE9F6B43aC78BA3", + "polygon": "0x000000000022D473030F116dDEE9F6B43aC78BA3", + "polygonzkevm": "", + "rootstock": "", + "scroll": "", + "sei": "", + "taiko": "", + "zksync": "0x0000000000225e31d15943971f47ad3022f714fa" } \ No newline at end of file From e7395b226c41d506f2360ab79d2ae150f6abd077 Mon Sep 17 00:00:00 2001 From: Ed Zynda Date: Thu, 29 Aug 2024 12:58:25 +0300 Subject: [PATCH 20/69] Allow only signer to call using EIP2612 --- src/Periphery/Permit2Proxy.sol | 11 +++--- test/solidity/Periphery/Permit2Proxy.t.sol | 41 +++++++++++++++++----- 2 files changed, 40 insertions(+), 12 deletions(-) diff --git a/src/Periphery/Permit2Proxy.sol b/src/Periphery/Permit2Proxy.sol index 310ec95f8..f04763700 100644 --- a/src/Periphery/Permit2Proxy.sol +++ b/src/Periphery/Permit2Proxy.sol @@ -68,7 +68,6 @@ contract Permit2Proxy is TransferrableOwnership { /// implement EIP2612) (in contrast to Permit2, calldata and diamondAddress /// are not signed by the user and could therefore be replaced) /// @param tokenAddress Address of the token to be bridged - /// @param owner Owner of the tokens to be bridged /// @param amount Amount of tokens to be bridged /// @param deadline Transaction must be completed before this timestamp /// @param v User signature (recovery ID) @@ -78,7 +77,6 @@ contract Permit2Proxy is TransferrableOwnership { /// @param diamondCalldata Address of the token to be bridged function callDiamondWithEIP2612Signature( address tokenAddress, - address owner, uint256 amount, uint256 deadline, uint8 v, @@ -89,7 +87,7 @@ contract Permit2Proxy is TransferrableOwnership { ) public payable { // call permit on token contract to register approval using signature ERC20Permit(tokenAddress).permit( - owner, + msg.sender, address(this), amount, deadline, @@ -99,7 +97,12 @@ contract Permit2Proxy is TransferrableOwnership { ); // deposit assets - LibAsset.transferFromERC20(tokenAddress, owner, address(this), amount); + LibAsset.transferFromERC20( + tokenAddress, + msg.sender, + address(this), + amount + ); // maxApprove token to diamond if current allowance is insufficient LibAsset.maxApproveERC20(IERC20(tokenAddress), diamondAddress, amount); diff --git a/test/solidity/Periphery/Permit2Proxy.t.sol b/test/solidity/Periphery/Permit2Proxy.t.sol index a17fb46c5..0fa1d99f3 100644 --- a/test/solidity/Periphery/Permit2Proxy.t.sol +++ b/test/solidity/Periphery/Permit2Proxy.t.sol @@ -97,7 +97,6 @@ contract Permit2ProxyTest is TestBase { // call Permit2Proxy with signature permit2Proxy.callDiamondWithEIP2612Signature( ADDRESS_USDC, - PERMIT2_USER, defaultUSDCAmount, testdata.deadline, testdata.v, @@ -110,7 +109,7 @@ contract Permit2ProxyTest is TestBase { vm.stopPrank(); } - function testRevertcannotUseEIP2612SignatureTwice() public { + function testRevert_cannot_use_eip2612_signature_twice() public { vm.startPrank(PERMIT2_USER); // get token-specific domainSeparator @@ -126,7 +125,6 @@ contract Permit2ProxyTest is TestBase { // call Permit2Proxy with signature permit2Proxy.callDiamondWithEIP2612Signature( ADDRESS_USDC, - PERMIT2_USER, defaultUSDCAmount, testdata.deadline, testdata.v, @@ -140,7 +138,6 @@ contract Permit2ProxyTest is TestBase { vm.expectRevert("EIP2612: invalid signature"); permit2Proxy.callDiamondWithEIP2612Signature( ADDRESS_USDC, - PERMIT2_USER, defaultUSDCAmount, testdata.deadline, testdata.v, @@ -153,7 +150,7 @@ contract Permit2ProxyTest is TestBase { vm.stopPrank(); } - function testRevertCannotUseExpiredEIP2612Signature() public { + function testRevert_cannot_use_expired_eip2612_signature() public { vm.startPrank(PERMIT2_USER); // get token-specific domainSeparator @@ -172,7 +169,6 @@ contract Permit2ProxyTest is TestBase { // call Permit2Proxy with signature permit2Proxy.callDiamondWithEIP2612Signature( ADDRESS_USDC, - PERMIT2_USER, defaultUSDCAmount, testdata.deadline, testdata.v, @@ -185,7 +181,7 @@ contract Permit2ProxyTest is TestBase { vm.stopPrank(); } - function testRevertCannotUseInvalidEIP2612Signature() public { + function testRevert_cannot_use_invalid_eip2612_signature() public { vm.startPrank(PERMIT2_USER); // get token-specific domainSeparator @@ -204,7 +200,6 @@ contract Permit2ProxyTest is TestBase { // call Permit2Proxy with signature permit2Proxy.callDiamondWithEIP2612Signature( ADDRESS_USDC, - PERMIT2_USER, defaultUSDCAmount, testdata.deadline, testdata.v + 1, // invalid v value @@ -217,6 +212,36 @@ contract Permit2ProxyTest is TestBase { vm.stopPrank(); } + function testRevert_sign_and_call_using_different_addresses() public { + vm.startPrank(USER_SENDER); + + // get token-specific domainSeparator + bytes32 domainSeparator = ERC20Permit(ADDRESS_USDC).DOMAIN_SEPARATOR(); + + // // using USDC on ETH for testing (implements EIP2612) + TestDataEIP2612 memory testdata = _getTestDataEIP2612( + ADDRESS_USDC, + domainSeparator, + block.timestamp + ); + + // expect call to revert since signature deadline is in the past + vm.expectRevert("EIP2612: invalid signature"); + // call Permit2Proxy with signature + permit2Proxy.callDiamondWithEIP2612Signature( + ADDRESS_USDC, + defaultUSDCAmount, + testdata.deadline, + testdata.v, + testdata.r, + testdata.s, + DIAMOND_ADDRESS, + testdata.diamondCalldata + ); + + vm.stopPrank(); + } + /// Permit2 specific tests /// function test_can_call_diamond_single() public { From 5d772b0a79052e77fb646f4ef998e66aacc3ab43 Mon Sep 17 00:00:00 2001 From: Ed Zynda Date: Thu, 29 Aug 2024 13:15:14 +0300 Subject: [PATCH 21/69] Bind Permit2Proxy to a single diamond --- src/Periphery/Permit2Proxy.sol | 61 ++++--------------- test/solidity/Periphery/Permit2Proxy.t.sol | 70 +--------------------- 2 files changed, 13 insertions(+), 118 deletions(-) diff --git a/src/Periphery/Permit2Proxy.sol b/src/Periphery/Permit2Proxy.sol index f04763700..afb333149 100644 --- a/src/Periphery/Permit2Proxy.sol +++ b/src/Periphery/Permit2Proxy.sol @@ -14,9 +14,10 @@ import { ERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/ERC2 /// @notice Proxy contract allowing gasless (Permit2-enabled) calls to our /// diamond contract /// @custom:version 1.0.0 -contract Permit2Proxy is TransferrableOwnership { +contract Permit2Proxy { /// Storage /// + address public immutable LIFI_DIAMOND; ISignatureTransfer public immutable PERMIT2; mapping(address => bool) public diamondWhitelist; @@ -47,10 +48,8 @@ contract Permit2Proxy is TransferrableOwnership { /// Constructor /// - constructor( - address _owner, - ISignatureTransfer _permit2 - ) TransferrableOwnership(_owner) { + constructor(address _lifiDiamond, ISignatureTransfer _permit2) { + LIFI_DIAMOND = _lifiDiamond; PERMIT2 = _permit2; PERMIT_WITH_WITNESS_TYPEHASH = keccak256( @@ -73,7 +72,6 @@ contract Permit2Proxy is TransferrableOwnership { /// @param v User signature (recovery ID) /// @param r User signature (ECDSA output) /// @param s User signature (ECDSA output) - /// @param diamondAddress Address of the token to be bridged /// @param diamondCalldata Address of the token to be bridged function callDiamondWithEIP2612Signature( address tokenAddress, @@ -82,7 +80,6 @@ contract Permit2Proxy is TransferrableOwnership { uint8 v, bytes32 r, bytes32 s, - address diamondAddress, bytes calldata diamondCalldata ) public payable { // call permit on token contract to register approval using signature @@ -105,29 +102,27 @@ contract Permit2Proxy is TransferrableOwnership { ); // maxApprove token to diamond if current allowance is insufficient - LibAsset.maxApproveERC20(IERC20(tokenAddress), diamondAddress, amount); + LibAsset.maxApproveERC20(IERC20(tokenAddress), LIFI_DIAMOND, amount); // call our diamond to execute calldata - _executeCalldata(diamondAddress, diamondCalldata); + _executeCalldata(diamondCalldata); } /// @notice Allows to bridge tokens of one type through a LI.FI diamond /// contract using Uniswap's Permit2 contract and a user signature /// that verifies allowance, diamondAddress and diamondCalldata - /// @param _diamondAddress the diamond contract to execute the call /// @param _diamondCalldata the calldata to execute /// @param _signer the signer giving permission to transfer tokens /// @param _permit the Uniswap Permit2 parameters /// @param _signature the signature giving approval to transfer tokens function callDiamondWithPermit2SignatureSingle( - address _diamondAddress, bytes calldata _diamondCalldata, address _signer, ISignatureTransfer.PermitTransferFrom calldata _permit, bytes calldata _signature ) external payable { LIFICall memory lifiCall = LIFICall( - _diamondAddress, + LIFI_DIAMOND, keccak256(_diamondCalldata) ); @@ -148,43 +143,20 @@ contract Permit2Proxy is TransferrableOwnership { // maxApprove token to diamond if current allowance is insufficient LibAsset.maxApproveERC20( IERC20(_permit.permitted.token), - _diamondAddress, + LIFI_DIAMOND, _permit.permitted.amount ); - _executeCalldata(_diamondAddress, _diamondCalldata); - } - - /// @notice Allows to update the whitelist of diamond contracts - /// @dev Admin function - /// @param addresses Addresses to be added (true) or removed (false) from - /// whitelist - /// @param values Values for each address that should be updated - function updateWhitelist( - address[] calldata addresses, - bool[] calldata values - ) external onlyOwner { - for (uint i; i < addresses.length; ) { - // update whitelist address value - diamondWhitelist[addresses[i]] = values[i]; - - // gas-efficient way to increase the loop counter - unchecked { - ++i; - } - } - emit WhitelistUpdated(addresses, values); + _executeCalldata(_diamondCalldata); } /// @notice utitlity method for constructing a valid Permit2 message hash - /// @param _diamondAddress the diamond address to call /// @param _diamondCalldata the calldata to execute /// @param _assetId the address of the token to approve /// @param _amount amount of tokens to approve /// @param _nonce the nonce to use /// @param _deadline the expiration deadline function getPermit2MsgHash( - address _diamondAddress, bytes calldata _diamondCalldata, address _assetId, uint256 _amount, @@ -201,7 +173,7 @@ contract Permit2Proxy is TransferrableOwnership { // Witness Permit2Proxy.LIFICall memory lifiCall = LIFICall( - _diamondAddress, + LIFI_DIAMOND, keccak256(_diamondCalldata) ); bytes32 witness = _getWitnessHash(lifiCall); @@ -261,19 +233,10 @@ contract Permit2Proxy is TransferrableOwnership { keccak256(abi.encodePacked("\x19\x01", domainSeparator, dataHash)); } - function _executeCalldata( - address diamondAddress, - bytes memory diamondCalldata - ) internal { - // make sure diamondAddress is whitelisted - // this limits the usage of this Permit2Proxy contracts to only work - // with our diamond contracts - if (!diamondWhitelist[diamondAddress]) - revert DiamondAddressNotWhitelisted(); - + function _executeCalldata(bytes memory diamondCalldata) internal { // call diamond with provided calldata // solhint-disable-next-line avoid-low-level-calls - (bool success, bytes memory data) = diamondAddress.call{ + (bool success, bytes memory data) = LIFI_DIAMOND.call{ value: msg.value }(diamondCalldata); // throw error to make sure tx reverts if low-level call was diff --git a/test/solidity/Periphery/Permit2Proxy.t.sol b/test/solidity/Periphery/Permit2Proxy.t.sol index 0fa1d99f3..64eff6226 100644 --- a/test/solidity/Periphery/Permit2Proxy.t.sol +++ b/test/solidity/Periphery/Permit2Proxy.t.sol @@ -51,7 +51,7 @@ contract Permit2ProxyTest is TestBase { initTestBase(); uniPermit2 = ISignatureTransfer(PERMIT2_ADDRESS); - permit2Proxy = new Permit2Proxy(address(this), uniPermit2); + permit2Proxy = new Permit2Proxy(DIAMOND_ADDRESS, uniPermit2); PERMIT_WITH_WITNESS_TYPEHASH = keccak256( abi.encodePacked( PermitHash._PERMIT_TRANSFER_FROM_WITNESS_TYPEHASH_STUB, @@ -59,11 +59,6 @@ contract Permit2ProxyTest is TestBase { ) ); - address[] memory whitelist = new address[](1); - whitelist[0] = DIAMOND_ADDRESS; - bool[] memory allowed = new bool[](1); - allowed[0] = true; - permit2Proxy.updateWhitelist(whitelist, allowed); PERMIT2_USER = vm.addr(PRIVATE_KEY); vm.label(PERMIT2_USER, "Permit2 User"); deal(ADDRESS_USDC, PERMIT2_USER, 10000 ether); @@ -102,7 +97,6 @@ contract Permit2ProxyTest is TestBase { testdata.v, testdata.r, testdata.s, - DIAMOND_ADDRESS, testdata.diamondCalldata ); @@ -130,7 +124,6 @@ contract Permit2ProxyTest is TestBase { testdata.v, testdata.r, testdata.s, - DIAMOND_ADDRESS, testdata.diamondCalldata ); @@ -143,7 +136,6 @@ contract Permit2ProxyTest is TestBase { testdata.v, testdata.r, testdata.s, - DIAMOND_ADDRESS, testdata.diamondCalldata ); @@ -174,7 +166,6 @@ contract Permit2ProxyTest is TestBase { testdata.v, testdata.r, testdata.s, - DIAMOND_ADDRESS, testdata.diamondCalldata ); @@ -205,7 +196,6 @@ contract Permit2ProxyTest is TestBase { testdata.v + 1, // invalid v value testdata.r, testdata.s, - DIAMOND_ADDRESS, testdata.diamondCalldata ); @@ -235,7 +225,6 @@ contract Permit2ProxyTest is TestBase { testdata.v, testdata.r, testdata.s, - DIAMOND_ADDRESS, testdata.diamondCalldata ); @@ -257,7 +246,6 @@ contract Permit2ProxyTest is TestBase { // Execute permit2Proxy.callDiamondWithPermit2SignatureSingle( - DIAMOND_ADDRESS, diamondCalldata, PERMIT2_USER, permitTransferFrom, @@ -271,7 +259,6 @@ contract Permit2ProxyTest is TestBase { (, , msgHash, ) = _getPermitWitnessTransferFromParams(); generatedMsgHash = permit2Proxy.getPermit2MsgHash( - DIAMOND_ADDRESS, _getCalldataForBridging(), ADDRESS_USDC, defaultUSDCAmount, @@ -282,29 +269,6 @@ contract Permit2ProxyTest is TestBase { assertEq(msgHash, generatedMsgHash); } - function testRevery_cannot_call_unwhitelisted_diamond() public { - DIAMOND_ADDRESS = address(0x11f1); // Not whitelisted - bytes memory diamondCalldata; - ISignatureTransfer.PermitTransferFrom memory permitTransferFrom; - bytes memory signature; - ( - diamondCalldata, - permitTransferFrom, - , - signature - ) = _getPermitWitnessTransferFromParams(); - - // Execute - vm.expectRevert(DiamondAddressNotWhitelisted.selector); - permit2Proxy.callDiamondWithPermit2SignatureSingle( - DIAMOND_ADDRESS, - diamondCalldata, - PERMIT2_USER, - permitTransferFrom, - signature - ); - } - function testRevert_cannot_call_diamond_single_with_same_signature_more_than_once() public { @@ -321,7 +285,6 @@ contract Permit2ProxyTest is TestBase { // Execute x2 permit2Proxy.callDiamondWithPermit2SignatureSingle( - DIAMOND_ADDRESS, diamondCalldata, PERMIT2_USER, permitTransferFrom, @@ -329,34 +292,6 @@ contract Permit2ProxyTest is TestBase { ); vm.expectRevert(InvalidNonce.selector); permit2Proxy.callDiamondWithPermit2SignatureSingle( - DIAMOND_ADDRESS, - diamondCalldata, - PERMIT2_USER, - permitTransferFrom, - signature - ); - } - - function testRevert_cannot_set_different_diamond_address_than_intended() - public - { - deal(ADDRESS_USDC, PERMIT2_USER, 10000 ether); - bytes memory diamondCalldata; - ISignatureTransfer.PermitTransferFrom memory permitTransferFrom; - bytes memory signature; - ( - diamondCalldata, - permitTransferFrom, - , - signature - ) = _getPermitWitnessTransferFromParams(); - - address MALICIOUS_CONTRACT; - - // Execute - vm.expectRevert(InvalidSigner.selector); - permit2Proxy.callDiamondWithPermit2SignatureSingle( - MALICIOUS_CONTRACT, diamondCalldata, PERMIT2_USER, permitTransferFrom, @@ -381,7 +316,6 @@ contract Permit2ProxyTest is TestBase { // Execute vm.expectRevert(InvalidSigner.selector); permit2Proxy.callDiamondWithPermit2SignatureSingle( - DIAMOND_ADDRESS, MALICIOUS_CALLDATA, PERMIT2_USER, permitTransferFrom, @@ -406,7 +340,6 @@ contract Permit2ProxyTest is TestBase { // Execute vm.expectRevert(InvalidSigner.selector); permit2Proxy.callDiamondWithPermit2SignatureSingle( - DIAMOND_ADDRESS, diamondCalldata, PERMIT2_USER, permitTransferFrom, @@ -433,7 +366,6 @@ contract Permit2ProxyTest is TestBase { // Execute vm.expectRevert(InvalidSigner.selector); permit2Proxy.callDiamondWithPermit2SignatureSingle( - DIAMOND_ADDRESS, diamondCalldata, PERMIT2_USER, permitTransferFrom, From 2f8e953c13f7758f0f392953d7ef10822d50f21e Mon Sep 17 00:00:00 2001 From: Ed Zynda Date: Thu, 29 Aug 2024 13:20:25 +0300 Subject: [PATCH 22/69] Update deploy script --- script/deploy/facets/DeployPermit2Proxy.s.sol | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/script/deploy/facets/DeployPermit2Proxy.s.sol b/script/deploy/facets/DeployPermit2Proxy.s.sol index 14b54689f..4513f3b31 100644 --- a/script/deploy/facets/DeployPermit2Proxy.s.sol +++ b/script/deploy/facets/DeployPermit2Proxy.s.sol @@ -20,19 +20,17 @@ contract DeployScript is DeployScriptBase { } function getConstructorArgs() internal override returns (bytes memory) { - // get path of global config file - string memory globalConfigPath = string.concat( + string memory deployments = string.concat( root, - "/config/global.json" + "/deployments/", + network, + ".", + fileSuffix, + "json" ); + string memory deploymentsJSON = vm.readFile(deployments); - // read file into json variable - string memory globalConfigJson = vm.readFile(globalConfigPath); - - // extract refundWallet address - address deployWalletAddress = globalConfigJson.readAddress( - ".deployerWallet" - ); + address diamond = deploymentsJSON.readAddress(".LiFiDiamond"); // get path of permit2 config file string memory permit2ProxyConfig = string.concat( @@ -48,6 +46,6 @@ contract DeployScript is DeployScriptBase { string.concat(".", network) ); - return abi.encode(deployWalletAddress, permit2Address); + return abi.encode(diamond, permit2Address); } } From 3d39c319c1850064e974801f08cd71e19bfaa5cc Mon Sep 17 00:00:00 2001 From: Ed Zynda Date: Thu, 29 Aug 2024 13:56:05 +0300 Subject: [PATCH 23/69] Implement non-gasless Permit2 flow --- src/Periphery/Permit2Proxy.sol | 33 +++++- test/solidity/Periphery/Permit2Proxy.t.sol | 117 +++++++++++++++++++-- 2 files changed, 142 insertions(+), 8 deletions(-) diff --git a/src/Periphery/Permit2Proxy.sol b/src/Periphery/Permit2Proxy.sol index afb333149..225ce200f 100644 --- a/src/Periphery/Permit2Proxy.sol +++ b/src/Periphery/Permit2Proxy.sol @@ -108,6 +108,37 @@ contract Permit2Proxy { _executeCalldata(diamondCalldata); } + /// @notice Allows to bridge tokens of one type through a LI.FI diamond + /// contract using Uniswap's Permit2 contract and a user signature + /// that verifies allowance, diamondAddress and diamondCalldata + /// @param _diamondCalldata the calldata to execute + /// @param _permit the Uniswap Permit2 parameters + /// @param _signature the signature giving approval to transfer tokens + function callDiamondWithPermit2( + bytes calldata _diamondCalldata, + ISignatureTransfer.PermitTransferFrom calldata _permit, + bytes calldata _signature + ) external payable { + PERMIT2.permitTransferFrom( + _permit, + ISignatureTransfer.SignatureTransferDetails({ + to: address(this), + requestedAmount: _permit.permitted.amount + }), + msg.sender, + _signature + ); + + // maxApprove token to diamond if current allowance is insufficient + LibAsset.maxApproveERC20( + IERC20(_permit.permitted.token), + LIFI_DIAMOND, + _permit.permitted.amount + ); + + _executeCalldata(_diamondCalldata); + } + /// @notice Allows to bridge tokens of one type through a LI.FI diamond /// contract using Uniswap's Permit2 contract and a user signature /// that verifies allowance, diamondAddress and diamondCalldata @@ -115,7 +146,7 @@ contract Permit2Proxy { /// @param _signer the signer giving permission to transfer tokens /// @param _permit the Uniswap Permit2 parameters /// @param _signature the signature giving approval to transfer tokens - function callDiamondWithPermit2SignatureSingle( + function callDiamondWithPermit2Witness( bytes calldata _diamondCalldata, address _signer, ISignatureTransfer.PermitTransferFrom calldata _permit, diff --git a/test/solidity/Periphery/Permit2Proxy.t.sol b/test/solidity/Periphery/Permit2Proxy.t.sol index 64eff6226..5ad586725 100644 --- a/test/solidity/Periphery/Permit2Proxy.t.sol +++ b/test/solidity/Periphery/Permit2Proxy.t.sol @@ -233,7 +233,50 @@ contract Permit2ProxyTest is TestBase { /// Permit2 specific tests /// - function test_can_call_diamond_single() public { + function test_can_call_diamond_with_permit2() public { + bytes memory diamondCalldata; + ISignatureTransfer.PermitTransferFrom memory permitTransferFrom; + bytes memory signature; + ( + diamondCalldata, + permitTransferFrom, + , + signature + ) = _getPermitTransferFromParams(); + + // Execute + vm.prank(PERMIT2_USER); + permit2Proxy.callDiamondWithPermit2( + diamondCalldata, + permitTransferFrom, + signature + ); + } + + function testRevert_cannot_call_diamond_with_permit2_using_different_addresses() + public + { + bytes memory diamondCalldata; + ISignatureTransfer.PermitTransferFrom memory permitTransferFrom; + bytes memory signature; + ( + diamondCalldata, + permitTransferFrom, + , + signature + ) = _getPermitTransferFromParams(); + + // Execute + vm.prank(USER_SENDER); + vm.expectRevert(InvalidSigner.selector); + permit2Proxy.callDiamondWithPermit2( + diamondCalldata, + permitTransferFrom, + signature + ); + } + + function test_can_call_diamond_with_permit2_plus_witness() public { bytes memory diamondCalldata; ISignatureTransfer.PermitTransferFrom memory permitTransferFrom; bytes memory signature; @@ -245,7 +288,7 @@ contract Permit2ProxyTest is TestBase { ) = _getPermitWitnessTransferFromParams(); // Execute - permit2Proxy.callDiamondWithPermit2SignatureSingle( + permit2Proxy.callDiamondWithPermit2Witness( diamondCalldata, PERMIT2_USER, permitTransferFrom, @@ -284,14 +327,14 @@ contract Permit2ProxyTest is TestBase { ) = _getPermitWitnessTransferFromParams(); // Execute x2 - permit2Proxy.callDiamondWithPermit2SignatureSingle( + permit2Proxy.callDiamondWithPermit2Witness( diamondCalldata, PERMIT2_USER, permitTransferFrom, signature ); vm.expectRevert(InvalidNonce.selector); - permit2Proxy.callDiamondWithPermit2SignatureSingle( + permit2Proxy.callDiamondWithPermit2Witness( diamondCalldata, PERMIT2_USER, permitTransferFrom, @@ -315,7 +358,7 @@ contract Permit2ProxyTest is TestBase { // Execute vm.expectRevert(InvalidSigner.selector); - permit2Proxy.callDiamondWithPermit2SignatureSingle( + permit2Proxy.callDiamondWithPermit2Witness( MALICIOUS_CALLDATA, PERMIT2_USER, permitTransferFrom, @@ -339,7 +382,7 @@ contract Permit2ProxyTest is TestBase { // Execute vm.expectRevert(InvalidSigner.selector); - permit2Proxy.callDiamondWithPermit2SignatureSingle( + permit2Proxy.callDiamondWithPermit2Witness( diamondCalldata, PERMIT2_USER, permitTransferFrom, @@ -365,7 +408,7 @@ contract Permit2ProxyTest is TestBase { // Execute vm.expectRevert(InvalidSigner.selector); - permit2Proxy.callDiamondWithPermit2SignatureSingle( + permit2Proxy.callDiamondWithPermit2Witness( diamondCalldata, PERMIT2_USER, permitTransferFrom, @@ -375,6 +418,45 @@ contract Permit2ProxyTest is TestBase { /// Helper Functions /// + function _getPermitTransferFromParams() + internal + view + returns ( + bytes memory diamondCalldata, + ISignatureTransfer.PermitTransferFrom memory permitTransferFrom, + bytes32 msgHash, + bytes memory signature + ) + { + // Calldata + diamondCalldata = _getCalldataForBridging(); + + // Token Permissions + ISignatureTransfer.TokenPermissions + memory tokenPermissions = ISignatureTransfer.TokenPermissions( + ADDRESS_USDC, + defaultUSDCAmount + ); + bytes32 permit = _getTokenPermissionsHash(tokenPermissions); + + // PermitTransferFrom + msgHash = _getPermitTransferFromHash( + uniPermit2.DOMAIN_SEPARATOR(), + permit, + address(permit2Proxy), + 0, + block.timestamp + 1000 + ); + + signature = _signMsgHash(msgHash, PRIVATE_KEY); + + permitTransferFrom = ISignatureTransfer.PermitTransferFrom( + tokenPermissions, + 0, + block.timestamp + 1000 + ); + } + function _getPermitWitnessTransferFromParams() internal view @@ -460,6 +542,27 @@ contract Permit2ProxyTest is TestBase { keccak256(abi.encode(permit2Proxy.WITNESS_TYPEHASH(), lifiCall)); } + function _getPermitTransferFromHash( + bytes32 domainSeparator, + bytes32 permit, + address spender, + uint256 nonce, + uint256 deadline + ) internal pure returns (bytes32) { + bytes32 dataHash = keccak256( + abi.encode( + PermitHash._PERMIT_TRANSFER_FROM_TYPEHASH, + permit, + spender, + nonce, + deadline + ) + ); + + return + keccak256(abi.encodePacked("\x19\x01", domainSeparator, dataHash)); + } + function _getPermitWitnessTransferFromHash( bytes32 domainSeparator, bytes32 permit, From af38e51f1376b81ba904a59bf67715ac353bc95d Mon Sep 17 00:00:00 2001 From: Ed Zynda Date: Thu, 29 Aug 2024 17:34:48 +0300 Subject: [PATCH 24/69] Redeploy to staging and update demo script --- deployments/_deployments_log_file.json | 10 +++++----- deployments/arbitrum.staging.json | 2 +- script/demoScripts/demoPermit2Proxy.ts | 11 +++++------ 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/deployments/_deployments_log_file.json b/deployments/_deployments_log_file.json index ea7d6657d..1828bb27c 100644 --- a/deployments/_deployments_log_file.json +++ b/deployments/_deployments_log_file.json @@ -22459,15 +22459,15 @@ "staging": { "1.0.0": [ { - "ADDRESS": "0x442BBFD6a4641B2b710DFfa4754081eC7502a3F7", + "ADDRESS": "0x30252Fd1C12d240F7d63F24e54390F796F2EAF37", "OPTIMIZER_RUNS": "1000000", - "TIMESTAMP": "2024-08-26 18:34:52", - "CONSTRUCTOR_ARGS": "0x00000000000000000000000011f1022ca6adef6400e5677528a80d49a069c00c000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba3", + "TIMESTAMP": "2024-08-29 17:03:09", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000d3b2b0ac0afdd0d166a495f5e9fca4ecc715a782000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba3", "SALT": "09072024", - "VERIFIED": "true" + "VERIFIED": "false" } ] } } } -} \ No newline at end of file +} diff --git a/deployments/arbitrum.staging.json b/deployments/arbitrum.staging.json index 4a9f4e76d..5f517619f 100644 --- a/deployments/arbitrum.staging.json +++ b/deployments/arbitrum.staging.json @@ -45,5 +45,5 @@ "HopFacetOptimized": "0xf82135385765f1324257ffF74489F16382EBBb8A", "LiFuelFeeCollector": "0x94EA56D8049e93E0308B9c7d1418Baf6A7C68280", "TokenWrapper": "0xF63b27AE2Dc887b88f82E2Cc597d07fBB2E78E70", - "Permit2Proxy": "0x442BBFD6a4641B2b710DFfa4754081eC7502a3F7" + "Permit2Proxy": "0x30252Fd1C12d240F7d63F24e54390F796F2EAF37" } \ No newline at end of file diff --git a/script/demoScripts/demoPermit2Proxy.ts b/script/demoScripts/demoPermit2Proxy.ts index 4eade89ab..67ac24b72 100644 --- a/script/demoScripts/demoPermit2Proxy.ts +++ b/script/demoScripts/demoPermit2Proxy.ts @@ -13,7 +13,7 @@ import { defineCommand, runMain } from 'citty' const DIAMOND_ADDRESS = '0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE' const USDT_ADDRESS = '0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9' -const PERMIT2_PROXY_ADDRESS = '0x442BBFD6a4641B2b710DFfa4754081eC7502a3F7' +const PERMIT2_PROXY_ADDRESS = '0x30252Fd1C12d240F7d63F24e54390F796F2EAF37' const PERMIT2_ADDRESS = '0x000000000022D473030F116dDEE9F6B43aC78BA3' const PRIVATE_KEY = `0x${process.env.PRIVATE_KEY}` @@ -43,8 +43,8 @@ const main = defineCommand({ 'function nonceBitmap(address owner, uint256 index) external view returns (uint256 nonce)', ]) const permit2ProxyAbi = parseAbi([ - 'function getPermit2MsgHash(address,bytes,address,uint256,uint256,uint256) external view returns (bytes32)', - 'function callDiamondWithPermit2SignatureSingle(address,bytes,address,((address,uint256),uint256,uint256),bytes) external', + 'function getPermit2MsgHash(bytes,address,uint256,uint256,uint256) external view returns (bytes32)', + 'function callDiamondWithPermit2Witness(bytes,address,((address,uint256),uint256,uint256),bytes) external', ]) // Setup a READ-ONLY client @@ -80,7 +80,6 @@ const main = defineCommand({ abi: permit2ProxyAbi, functionName: 'getPermit2MsgHash', args: [ - DIAMOND_ADDRESS, calldata, USDT_ADDRESS, parseUnits('5', 6), @@ -111,8 +110,8 @@ const main = defineCommand({ const tx = await walletClient.writeContract({ address: PERMIT2_PROXY_ADDRESS, abi: permit2ProxyAbi, - functionName: 'callDiamondWithPermit2SignatureSingle', - args: [DIAMOND_ADDRESS, calldata, account.address, permit, signature], + functionName: 'callDiamondWithPermit2Witness', + args: [calldata, account.address, permit, signature], }) }, }) From 056a8c2c1f28c3a856c94f643c97f2551470d522 Mon Sep 17 00:00:00 2001 From: Ed Zynda Date: Fri, 30 Aug 2024 13:45:46 +0300 Subject: [PATCH 25/69] Update comments and remove unneeded events/errors --- src/Periphery/Permit2Proxy.sol | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/Periphery/Permit2Proxy.sol b/src/Periphery/Permit2Proxy.sol index 225ce200f..139186bfe 100644 --- a/src/Periphery/Permit2Proxy.sol +++ b/src/Periphery/Permit2Proxy.sol @@ -40,11 +40,6 @@ contract Permit2Proxy { /// Errors /// error CallToDiamondFailed(bytes); - error DiamondAddressNotWhitelisted(); - - /// Events /// - - event WhitelistUpdated(address[] addresses, bool[] values); /// Constructor /// @@ -65,7 +60,8 @@ contract Permit2Proxy { /// @notice Allows to bridge tokens through a LI.FI diamond contract using /// an EIP2612 gasless permit (only works with tokenAddresses that /// implement EIP2612) (in contrast to Permit2, calldata and diamondAddress - /// are not signed by the user and could therefore be replaced) + /// are not signed by the user and could therefore be replaced by the user) + /// 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 @@ -84,7 +80,7 @@ contract Permit2Proxy { ) public payable { // call permit on token contract to register approval using signature ERC20Permit(tokenAddress).permit( - msg.sender, + msg.sender, // Ensure msg.sender is same wallet that signed permit address(this), amount, deadline, @@ -110,7 +106,9 @@ contract Permit2Proxy { /// @notice Allows to bridge tokens of one type through a LI.FI diamond /// contract using Uniswap's Permit2 contract and a user signature - /// that verifies allowance, diamondAddress and diamondCalldata + /// that verifies allowance. The calldata can be changed by the + /// user. Can only be called by the permit signer to prevent + /// front-running. /// @param _diamondCalldata the calldata to execute /// @param _permit the Uniswap Permit2 parameters /// @param _signature the signature giving approval to transfer tokens @@ -125,7 +123,7 @@ contract Permit2Proxy { to: address(this), requestedAmount: _permit.permitted.amount }), - msg.sender, + msg.sender, // Ensure msg.sender is same wallet that signed permit _signature ); From 1755f86a341a7c61615be32d6fd1143da3b80a0b Mon Sep 17 00:00:00 2001 From: Ed Zynda Date: Fri, 30 Aug 2024 13:57:35 +0300 Subject: [PATCH 26/69] Add utility methods for determining the next valid nonce --- src/Periphery/Permit2Proxy.sol | 79 ++++++++++++++++++++++ test/solidity/Periphery/Permit2Proxy.t.sol | 14 ++-- 2 files changed, 89 insertions(+), 4 deletions(-) diff --git a/src/Periphery/Permit2Proxy.sol b/src/Periphery/Permit2Proxy.sol index 139186bfe..fa987d42d 100644 --- a/src/Periphery/Permit2Proxy.sol +++ b/src/Periphery/Permit2Proxy.sol @@ -274,4 +274,83 @@ contract Permit2Proxy { revert CallToDiamondFailed(data); } } + + /// The following code was adapted from https://github.com/flood-protocol/permit2-nonce-finder/blob/7a4ac8a58d0b499308000b75ddb2384834f31fac/src/Permit2NonceFinder.sol + /// Provides utility functions for determining the next valid Permit2 nonce + + /// @notice Finds the next valid nonce for a user, starting from 0. + /// @param owner The owner of the nonces + /// @return nonce The first valid nonce starting from 0 + function nextNonce(address owner) external view returns (uint256 nonce) { + nonce = _nextNonce(owner, 0, 0); + } + + /// @notice Finds the next valid nonce for a user, after from a given nonce. + /// @dev This can be helpful if you're signing multiple nonces in a row and need the next nonce to sign but the start one is still valid. + /// @param owner The owner of the nonces + /// @param start The nonce to start from + /// @return nonce The first valid nonce after the given nonce + function nextNonceAfter( + address owner, + uint256 start + ) external view returns (uint256 nonce) { + uint248 word = uint248(start >> 8); + uint8 pos = uint8(start); + if (pos == type(uint8).max) { + // If the position is 255, we need to move to the next word + word++; + pos = 0; + } else { + // Otherwise, we just move to the next position + pos++; + } + nonce = _nextNonce(owner, word, pos); + } + + /// @notice Finds the next valid nonce for a user, starting from a given word and position. + /// @param owner The owner of the nonces + /// @param word Word to start looking from + /// @param pos Position inside the word to start looking from + function _nextNonce( + address owner, + uint248 word, + uint8 pos + ) internal view returns (uint256 nonce) { + while (true) { + uint256 bitmap = PERMIT2.nonceBitmap(owner, word); + + // Check if the bitmap is completely full + if (bitmap == type(uint256).max) { + // If so, move to the next word + ++word; + pos = 0; + continue; + } + if (pos != 0) { + // If the position is not 0, we need to shift the bitmap to ignore the bits before position + bitmap = bitmap >> pos; + } + // Find the first zero bit in the bitmap + while (bitmap & 1 == 1) { + bitmap = bitmap >> 1; + ++pos; + } + + return _nonceFromWordAndPos(word, pos); + } + } + + /// @notice Constructs a nonce from a word and a position inside the word + /// @param word The word containing the nonce + /// @param pos The position of the nonce inside the word + /// @return nonce The nonce constructed from the word and position + function _nonceFromWordAndPos( + uint248 word, + uint8 pos + ) internal pure returns (uint256 nonce) { + // The last 248 bits of the word are the nonce bits + nonce = uint256(word) << 8; + // The first 8 bits of the word are the position inside the word + nonce |= pos; + } } diff --git a/test/solidity/Periphery/Permit2Proxy.t.sol b/test/solidity/Periphery/Permit2Proxy.t.sol index 5ad586725..5b5df609b 100644 --- a/test/solidity/Periphery/Permit2Proxy.t.sol +++ b/test/solidity/Periphery/Permit2Proxy.t.sol @@ -439,12 +439,15 @@ contract Permit2ProxyTest is TestBase { ); bytes32 permit = _getTokenPermissionsHash(tokenPermissions); + // Nonce + uint256 nonce = permit2Proxy.nextNonce(PERMIT2_USER); + // PermitTransferFrom msgHash = _getPermitTransferFromHash( uniPermit2.DOMAIN_SEPARATOR(), permit, address(permit2Proxy), - 0, + nonce, block.timestamp + 1000 ); @@ -452,7 +455,7 @@ contract Permit2ProxyTest is TestBase { permitTransferFrom = ISignatureTransfer.PermitTransferFrom( tokenPermissions, - 0, + nonce, block.timestamp + 1000 ); } @@ -483,12 +486,15 @@ contract Permit2ProxyTest is TestBase { ); bytes32 witness = _getWitnessHash(lifiCall); + // Nonce + uint256 nonce = permit2Proxy.nextNonce(PERMIT2_USER); + // PermitTransferWithWitness msgHash = _getPermitWitnessTransferFromHash( uniPermit2.DOMAIN_SEPARATOR(), permit, address(permit2Proxy), - 0, + nonce, block.timestamp + 1000, witness ); @@ -497,7 +503,7 @@ contract Permit2ProxyTest is TestBase { permitTransferFrom = ISignatureTransfer.PermitTransferFrom( tokenPermissions, - 0, + nonce, block.timestamp + 1000 ); } From 17e5da3ba9cf6390e1995d3478014e0274cfd2b4 Mon Sep 17 00:00:00 2001 From: Ed Zynda Date: Fri, 30 Aug 2024 14:02:59 +0300 Subject: [PATCH 27/69] Redeploy and update demo script --- deployments/_deployments_log_file.json | 6 +++--- deployments/arbitrum.staging.json | 2 +- script/demoScripts/demoPermit2Proxy.ts | 16 +++++++--------- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/deployments/_deployments_log_file.json b/deployments/_deployments_log_file.json index 1828bb27c..3498f0c68 100644 --- a/deployments/_deployments_log_file.json +++ b/deployments/_deployments_log_file.json @@ -22459,12 +22459,12 @@ "staging": { "1.0.0": [ { - "ADDRESS": "0x30252Fd1C12d240F7d63F24e54390F796F2EAF37", + "ADDRESS": "0xA3C7a31a2A97b847D967e0B755921D084C46a742", "OPTIMIZER_RUNS": "1000000", - "TIMESTAMP": "2024-08-29 17:03:09", + "TIMESTAMP": "2024-08-30 14:01:34", "CONSTRUCTOR_ARGS": "0x000000000000000000000000d3b2b0ac0afdd0d166a495f5e9fca4ecc715a782000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba3", "SALT": "09072024", - "VERIFIED": "false" + "VERIFIED": "true" } ] } diff --git a/deployments/arbitrum.staging.json b/deployments/arbitrum.staging.json index 5f517619f..be96f4622 100644 --- a/deployments/arbitrum.staging.json +++ b/deployments/arbitrum.staging.json @@ -45,5 +45,5 @@ "HopFacetOptimized": "0xf82135385765f1324257ffF74489F16382EBBb8A", "LiFuelFeeCollector": "0x94EA56D8049e93E0308B9c7d1418Baf6A7C68280", "TokenWrapper": "0xF63b27AE2Dc887b88f82E2Cc597d07fBB2E78E70", - "Permit2Proxy": "0x30252Fd1C12d240F7d63F24e54390F796F2EAF37" + "Permit2Proxy": "0xA3C7a31a2A97b847D967e0B755921D084C46a742" } \ No newline at end of file diff --git a/script/demoScripts/demoPermit2Proxy.ts b/script/demoScripts/demoPermit2Proxy.ts index 67ac24b72..d27587796 100644 --- a/script/demoScripts/demoPermit2Proxy.ts +++ b/script/demoScripts/demoPermit2Proxy.ts @@ -13,7 +13,7 @@ import { defineCommand, runMain } from 'citty' const DIAMOND_ADDRESS = '0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE' const USDT_ADDRESS = '0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9' -const PERMIT2_PROXY_ADDRESS = '0x30252Fd1C12d240F7d63F24e54390F796F2EAF37' +const PERMIT2_PROXY_ADDRESS = '0xA3C7a31a2A97b847D967e0B755921D084C46a742' const PERMIT2_ADDRESS = '0x000000000022D473030F116dDEE9F6B43aC78BA3' const PRIVATE_KEY = `0x${process.env.PRIVATE_KEY}` @@ -38,12 +38,10 @@ const main = defineCommand({ const SIGNER_PRIVATE_KEY = `0x${args.signerKey}` as Hex const EXECUTOR_PRIVATE_KEY = `0x${args.executorKey}` as Hex - // Setup the required ABIs - const permit2Abi = parseAbi([ - 'function nonceBitmap(address owner, uint256 index) external view returns (uint256 nonce)', - ]) + // Setup the required ABI const permit2ProxyAbi = parseAbi([ 'function getPermit2MsgHash(bytes,address,uint256,uint256,uint256) external view returns (bytes32)', + 'function nextNonce(address owner) external view returns (uint256)', 'function callDiamondWithPermit2Witness(bytes,address,((address,uint256),uint256,uint256),bytes) external', ]) @@ -65,10 +63,10 @@ const main = defineCommand({ // Get the nonce from the PERMIT2 contract const nonce = await client.readContract({ - address: PERMIT2_ADDRESS, - abi: permit2Abi, - functionName: 'nonceBitmap', - args: [account.address, 0n], + address: PERMIT2_PROXY_ADDRESS, + abi: permit2ProxyAbi, + functionName: 'nextNonce', + args: [account.address], }) // Get lastest block From 476ba04143ca67a627f9f4be8eb47de15eb912bf Mon Sep 17 00:00:00 2001 From: Ed Zynda Date: Fri, 30 Aug 2024 15:32:32 +0300 Subject: [PATCH 28/69] Add documentation --- docs/Permit2Proxy.md | 126 +++++++++++++++++++++++++++++++++++++++++++ docs/README.md | 1 + 2 files changed, 127 insertions(+) create mode 100644 docs/Permit2Proxy.md diff --git a/docs/Permit2Proxy.md b/docs/Permit2Proxy.md new file mode 100644 index 000000000..9938a4ece --- /dev/null +++ b/docs/Permit2Proxy.md @@ -0,0 +1,126 @@ +# Permit2 Proxy + +## Description + +Periphery contract which enables gasless and semi-gasless transaction flows +enabled through ERC20 Permit and Uniswap's Permit2 + +## How To Use + +The contract has a number of methods for making gasless and semi-gasless calls +as well as a few helpful utility methods. + +The following methods are available: + +This method is used to execute a transaction where the approval is granted +using an ERC20 Permit signature. It can only be called by the signer in order +to prevent front-running attacks. + +```solidity +/// @notice Allows to bridge tokens through a LI.FI diamond contract using +/// an EIP2612 gasless permit (only works with tokenAddresses that +/// implement EIP2612) (in contrast to Permit2, calldata and diamondAddress +/// are not signed by the user and could therefore be replaced by the user) +/// 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 v User signature (recovery ID) +/// @param r User signature (ECDSA output) +/// @param s User signature (ECDSA output) +/// @param diamondCalldata Address of the token to be bridged +function callDiamondWithEIP2612Signature( + address tokenAddress, + uint256 amount, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s, + bytes calldata diamondCalldata +) public payable +```` + +This method is used to execute a transaction where the approval is granted via +Uniswap's Permit2 contract. It can only be called by the signer in order to +prevent front-running attacks. + +```solidity +/// @notice Allows to bridge tokens of one type through a LI.FI diamond +/// contract using Uniswap's Permit2 contract and a user signature +/// that verifies allowance. The calldata can be changed by the +/// user. Can only be called by the permit signer to prevent +/// front-running. +/// @param _diamondCalldata the calldata to execute +/// @param _permit the Uniswap Permit2 parameters +/// @param _signature the signature giving approval to transfer tokens +function callDiamondWithPermit2( + bytes calldata _diamondCalldata, + ISignatureTransfer.PermitTransferFrom calldata _permit, + bytes calldata _signature +) external payable +``` + +This method enables a gasless flow by allowing a user to sign a Uniswap Permit2 +message hash which includes a "witness" type. This extra type restricts which +calldata can be called during execution and cannot be changed. Anyone with the +signature can execute the transaction on behalf of the signer. + +```solidity +/// @notice Allows to bridge tokens of one type through a LI.FI diamond +/// contract using Uniswap's Permit2 contract and a user signature +/// that verifies allowance, diamondAddress and diamondCalldata +/// @param _diamondCalldata the calldata to execute +/// @param _signer the signer giving permission to transfer tokens +/// @param _permit the Uniswap Permit2 parameters +/// @param _signature the signature giving approval to transfer tokens +function callDiamondWithPermit2Witness( + bytes calldata _diamondCalldata, + address _signer, + ISignatureTransfer.PermitTransferFrom calldata _permit, + bytes calldata _signature +) external payable +``` + +There are a few utility methods to make it easier to generate the necessary +signature for the gasless flow. + +Calling this method will return a valid message hash that can then be signed +in order to be executed later by another wallet. + +```solidity +/// @notice utitlity method for constructing a valid Permit2 message hash +/// @param _diamondCalldata the calldata to execute +/// @param _assetId the address of the token to approve +/// @param _amount amount of tokens to approve +/// @param _nonce the nonce to use +/// @param _deadline the expiration deadline +function getPermit2MsgHash( + bytes calldata _diamondCalldata, + address _assetId, + uint256 _amount, + uint256 _nonce, + uint256 _deadline +) external view returns (bytes32 msgHash) +``` + +Permit2 nonces are non-sequential and are a bit complicated to work with the +following utility methods allow you to fetch the next valid nonce or sequence +of nonces for use when generating Permit2 signatures. + +```solidity +/// @notice Finds the next valid nonce for a user, starting from 0. +/// @param owner The owner of the nonces +/// @return nonce The first valid nonce starting from 0 +function nextNonce(address owner) external view returns (uint256 nonce) + +/// @notice Finds the next valid nonce for a user, after from a given nonce. +/// @dev This can be helpful if you're signing multiple nonces in a row and +/// need the next nonce to sign but the start one is still valid. +/// @param owner The owner of the nonces +/// @param start The nonce to start from +/// @return nonce The first valid nonce after the given nonce +function nextNonceAfter( + address owner, + uint256 start +) external view returns (uint256 nonce) +``` diff --git a/docs/README.md b/docs/README.md index 55948f4ab..0de0d4b03 100644 --- a/docs/README.md +++ b/docs/README.md @@ -56,3 +56,4 @@ - [FeeCollector](./FeeCollector.md) - [Receiver](./Receiver.md) - [RelayerCelerIM](./RelayerCelerIM.md) +- [Permi2Proxy](./Permit2Proxy.md) From 10b620493dc663dec8236cd31ebb87c5de827213 Mon Sep 17 00:00:00 2001 From: Ed Zynda Date: Mon, 2 Sep 2024 10:10:05 +0300 Subject: [PATCH 29/69] Fixes --- docs/Permit2Proxy.md | 2 +- docs/README.md | 2 +- script/demoScripts/demoPermit2Proxy.ts | 4 ++-- src/Periphery/Permit2Proxy.sol | 7 ++++--- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/docs/Permit2Proxy.md b/docs/Permit2Proxy.md index 9938a4ece..fa248bf27 100644 --- a/docs/Permit2Proxy.md +++ b/docs/Permit2Proxy.md @@ -103,7 +103,7 @@ function getPermit2MsgHash( ) external view returns (bytes32 msgHash) ``` -Permit2 nonces are non-sequential and are a bit complicated to work with the +Permit2 nonces are non-sequential and are a bit complicated to work with. The following utility methods allow you to fetch the next valid nonce or sequence of nonces for use when generating Permit2 signatures. diff --git a/docs/README.md b/docs/README.md index 0de0d4b03..9bf554527 100644 --- a/docs/README.md +++ b/docs/README.md @@ -56,4 +56,4 @@ - [FeeCollector](./FeeCollector.md) - [Receiver](./Receiver.md) - [RelayerCelerIM](./RelayerCelerIM.md) -- [Permi2Proxy](./Permit2Proxy.md) +- [Permit2Proxy](./Permit2Proxy.md) diff --git a/script/demoScripts/demoPermit2Proxy.ts b/script/demoScripts/demoPermit2Proxy.ts index d27587796..9a6731150 100644 --- a/script/demoScripts/demoPermit2Proxy.ts +++ b/script/demoScripts/demoPermit2Proxy.ts @@ -69,10 +69,10 @@ const main = defineCommand({ args: [account.address], }) - // Get lastest block + // Get latest block const block = await client.getBlock() - // Consturct a valid message hash to sign using Permit2Proxy's utility func + // Construct a valid message hash to sign using Permit2Proxy's utility func const msgHash = await client.readContract({ address: PERMIT2_PROXY_ADDRESS, abi: permit2ProxyAbi, diff --git a/src/Periphery/Permit2Proxy.sol b/src/Periphery/Permit2Proxy.sol index fa987d42d..f088babd8 100644 --- a/src/Periphery/Permit2Proxy.sol +++ b/src/Periphery/Permit2Proxy.sol @@ -11,8 +11,8 @@ import { ERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/ERC2 /// @title Permit2Proxy /// @author LI.FI (https://li.fi) -/// @notice Proxy contract allowing gasless (Permit2-enabled) calls to our -/// diamond contract +/// @notice Proxy contract allowing gasless calls via Permit2 as well as making +/// token approvals via ERC20 Permit (EIP-2612) to our diamond contract /// @custom:version 1.0.0 contract Permit2Proxy { /// Storage /// @@ -31,7 +31,8 @@ contract Permit2Proxy { /// Types /// - // @dev LIFI Specific Witness to verify + // @dev LIFI Specific Witness which verifies the correct calldata and + // diamond address struct LIFICall { address diamondAddress; bytes32 diamondCalldataHash; From f5f566d0f17e212517b988e93d6a50ec88ea0fb1 Mon Sep 17 00:00:00 2001 From: Ed Zynda Date: Tue, 3 Sep 2024 12:58:33 +0300 Subject: [PATCH 30/69] Change witness type to be consistent with the rest of the codebase --- src/Periphery/Permit2Proxy.sol | 12 ++++++------ test/solidity/Periphery/Permit2Proxy.t.sol | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Periphery/Permit2Proxy.sol b/src/Periphery/Permit2Proxy.sol index f088babd8..da12c330c 100644 --- a/src/Periphery/Permit2Proxy.sol +++ b/src/Periphery/Permit2Proxy.sol @@ -22,10 +22,10 @@ contract Permit2Proxy { mapping(address => bool) public diamondWhitelist; string public constant WITNESS_TYPE_STRING = - "LIFICall witness)LIFICall(address tokenReceiver,address diamondAddress,bytes32 diamondCalldataHash)TokenPermissions(address token,uint256 amount)"; + "LiFiCall witness)LiFiCall(address tokenReceiver,address diamondAddress,bytes32 diamondCalldataHash)TokenPermissions(address token,uint256 amount)"; bytes32 public constant WITNESS_TYPEHASH = keccak256( - "LIFICall(address tokenReceiver,address diamondAddress,bytes32 diamondCalldataHash)" + "LiFiCall(address tokenReceiver,address diamondAddress,bytes32 diamondCalldataHash)" ); bytes32 public immutable PERMIT_WITH_WITNESS_TYPEHASH; @@ -33,7 +33,7 @@ contract Permit2Proxy { // @dev LIFI Specific Witness which verifies the correct calldata and // diamond address - struct LIFICall { + struct LiFiCall { address diamondAddress; bytes32 diamondCalldataHash; } @@ -151,7 +151,7 @@ contract Permit2Proxy { ISignatureTransfer.PermitTransferFrom calldata _permit, bytes calldata _signature ) external payable { - LIFICall memory lifiCall = LIFICall( + LiFiCall memory lifiCall = LiFiCall( LIFI_DIAMOND, keccak256(_diamondCalldata) ); @@ -202,7 +202,7 @@ contract Permit2Proxy { bytes32 permit = _getTokenPermissionsHash(tokenPermissions); // Witness - Permit2Proxy.LIFICall memory lifiCall = LIFICall( + Permit2Proxy.LiFiCall memory lifiCall = LiFiCall( LIFI_DIAMOND, keccak256(_diamondCalldata) ); @@ -235,7 +235,7 @@ contract Permit2Proxy { } function _getWitnessHash( - Permit2Proxy.LIFICall memory lifiCall + Permit2Proxy.LiFiCall memory lifiCall ) internal pure returns (bytes32) { return keccak256(abi.encode(WITNESS_TYPEHASH, lifiCall)); } diff --git a/test/solidity/Periphery/Permit2Proxy.t.sol b/test/solidity/Periphery/Permit2Proxy.t.sol index 5b5df609b..8647c5dbc 100644 --- a/test/solidity/Periphery/Permit2Proxy.t.sol +++ b/test/solidity/Periphery/Permit2Proxy.t.sol @@ -480,7 +480,7 @@ contract Permit2ProxyTest is TestBase { // Witness diamondCalldata = _getCalldataForBridging(); - Permit2Proxy.LIFICall memory lifiCall = Permit2Proxy.LIFICall( + Permit2Proxy.LiFiCall memory lifiCall = Permit2Proxy.LiFiCall( DIAMOND_ADDRESS, keccak256(diamondCalldata) ); @@ -542,7 +542,7 @@ contract Permit2ProxyTest is TestBase { } function _getWitnessHash( - Permit2Proxy.LIFICall memory lifiCall + Permit2Proxy.LiFiCall memory lifiCall ) internal view returns (bytes32) { return keccak256(abi.encode(permit2Proxy.WITNESS_TYPEHASH(), lifiCall)); From be50801cc412bc779d7a2421a2e00a0e515a994c Mon Sep 17 00:00:00 2001 From: Ed Zynda Date: Tue, 3 Sep 2024 13:02:19 +0300 Subject: [PATCH 31/69] Remove unneeded whitelist --- src/Periphery/Permit2Proxy.sol | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Periphery/Permit2Proxy.sol b/src/Periphery/Permit2Proxy.sol index da12c330c..ea75f5375 100644 --- a/src/Periphery/Permit2Proxy.sol +++ b/src/Periphery/Permit2Proxy.sol @@ -19,7 +19,6 @@ contract Permit2Proxy { address public immutable LIFI_DIAMOND; ISignatureTransfer public immutable PERMIT2; - mapping(address => bool) public diamondWhitelist; string public constant WITNESS_TYPE_STRING = "LiFiCall witness)LiFiCall(address tokenReceiver,address diamondAddress,bytes32 diamondCalldataHash)TokenPermissions(address token,uint256 amount)"; From 671c0489cbbfef64e4bfb805b646376ef8c23ba5 Mon Sep 17 00:00:00 2001 From: Ed Zynda Date: Wed, 4 Sep 2024 14:59:28 +0300 Subject: [PATCH 32/69] Fixes --- config/{permit2.json => permit2Proxy.json} | 0 script/demoScripts/demoPermit2Proxy.ts | 2 +- script/deploy/facets/DeployPermit2Proxy.s.sol | 4 +- test/solidity/Periphery/Permit2Proxy.t.sol | 87 ++++++++++--------- 4 files changed, 48 insertions(+), 45 deletions(-) rename config/{permit2.json => permit2Proxy.json} (100%) diff --git a/config/permit2.json b/config/permit2Proxy.json similarity index 100% rename from config/permit2.json rename to config/permit2Proxy.json diff --git a/script/demoScripts/demoPermit2Proxy.ts b/script/demoScripts/demoPermit2Proxy.ts index 9a6731150..af37ab52d 100644 --- a/script/demoScripts/demoPermit2Proxy.ts +++ b/script/demoScripts/demoPermit2Proxy.ts @@ -82,7 +82,7 @@ const main = defineCommand({ USDT_ADDRESS, parseUnits('5', 6), nonce, - block.timestamp + 1200n, + block.timestamp + 1200n, // 20 min deadline ], }) console.log(msgHash) diff --git a/script/deploy/facets/DeployPermit2Proxy.s.sol b/script/deploy/facets/DeployPermit2Proxy.s.sol index 4513f3b31..72183428a 100644 --- a/script/deploy/facets/DeployPermit2Proxy.s.sol +++ b/script/deploy/facets/DeployPermit2Proxy.s.sol @@ -35,13 +35,13 @@ contract DeployScript is DeployScriptBase { // get path of permit2 config file string memory permit2ProxyConfig = string.concat( root, - "/config/permit2.json" + "/config/permit2Proxy.json" ); // read file into json variable string memory permit2ProxyConfigJSON = vm.readFile(permit2ProxyConfig); - // extract wrapped token address for the given network + // extract Permit2 contract address for the given network address permit2Address = permit2ProxyConfigJSON.readAddress( string.concat(".", network) ); diff --git a/test/solidity/Periphery/Permit2Proxy.t.sol b/test/solidity/Periphery/Permit2Proxy.t.sol index 8647c5dbc..b613a738e 100644 --- a/test/solidity/Periphery/Permit2Proxy.t.sol +++ b/test/solidity/Periphery/Permit2Proxy.t.sol @@ -72,7 +72,13 @@ contract Permit2ProxyTest is TestBase { /// EIP2612 (native permit) related test cases /// - function test_can_execute_calldata_using_eip2612_signature_usdc() public { + function test_can_execute_calldata_using_eip2612_signature_usdc() + public + returns (TestDataEIP2612 memory) + { + uint256 startingUSDCBalance = ERC20(ADDRESS_USDC).balanceOf( + PERMIT2_USER + ); vm.startPrank(PERMIT2_USER); // get token-specific domainSeparator @@ -99,35 +105,22 @@ contract Permit2ProxyTest is TestBase { testdata.s, testdata.diamondCalldata ); - + uint256 endingUSDCBalance = ERC20(ADDRESS_USDC).balanceOf( + PERMIT2_USER + ); + uint256 delta = startingUSDCBalance - endingUSDCBalance; + assertEq(defaultUSDCAmount, delta); vm.stopPrank(); + return testdata; } function testRevert_cannot_use_eip2612_signature_twice() public { - vm.startPrank(PERMIT2_USER); - - // get token-specific domainSeparator - bytes32 domainSeparator = ERC20Permit(ADDRESS_USDC).DOMAIN_SEPARATOR(); + TestDataEIP2612 + memory testdata = test_can_execute_calldata_using_eip2612_signature_usdc(); - // using USDC on ETH for testing (implements EIP2612) - TestDataEIP2612 memory testdata = _getTestDataEIP2612( - ADDRESS_USDC, - domainSeparator, - block.timestamp + 1000 - ); - - // call Permit2Proxy with signature - permit2Proxy.callDiamondWithEIP2612Signature( - ADDRESS_USDC, - defaultUSDCAmount, - testdata.deadline, - testdata.v, - testdata.r, - testdata.s, - testdata.diamondCalldata - ); + vm.startPrank(PERMIT2_USER); - // expect call to revert if same signature is used twice + // // expect call to revert if same signature is used twice vm.expectRevert("EIP2612: invalid signature"); permit2Proxy.callDiamondWithEIP2612Signature( ADDRESS_USDC, @@ -185,7 +178,7 @@ contract Permit2ProxyTest is TestBase { block.timestamp ); - // expect call to revert since signature deadline is in the past + // expect call to revert since signature is invalid vm.expectRevert("EIP2612: invalid signature"); // call Permit2Proxy with signature @@ -215,7 +208,7 @@ contract Permit2ProxyTest is TestBase { block.timestamp ); - // expect call to revert since signature deadline is in the past + // expect call to revert since signature was created by a different address vm.expectRevert("EIP2612: invalid signature"); // call Permit2Proxy with signature permit2Proxy.callDiamondWithEIP2612Signature( @@ -233,7 +226,7 @@ contract Permit2ProxyTest is TestBase { /// Permit2 specific tests /// - function test_can_call_diamond_with_permit2() public { + function test_user_can_call_diamond_with_own_permit2_signature() public { bytes memory diamondCalldata; ISignatureTransfer.PermitTransferFrom memory permitTransferFrom; bytes memory signature; @@ -242,7 +235,7 @@ contract Permit2ProxyTest is TestBase { permitTransferFrom, , signature - ) = _getPermitTransferFromParams(); + ) = _getPermit2TransferFromParamsSignedByPERMIT2_USER(); // Execute vm.prank(PERMIT2_USER); @@ -253,7 +246,7 @@ contract Permit2ProxyTest is TestBase { ); } - function testRevert_cannot_call_diamond_with_permit2_using_different_addresses() + function testRevert_cannot_call_diamond_with_permit2_using_different_wallet_address() public { bytes memory diamondCalldata; @@ -264,10 +257,10 @@ contract Permit2ProxyTest is TestBase { permitTransferFrom, , signature - ) = _getPermitTransferFromParams(); + ) = _getPermit2TransferFromParamsSignedByPERMIT2_USER(); // Execute - vm.prank(USER_SENDER); + vm.prank(USER_SENDER); // Not the original signer vm.expectRevert(InvalidSigner.selector); permit2Proxy.callDiamondWithPermit2( diamondCalldata, @@ -285,9 +278,10 @@ contract Permit2ProxyTest is TestBase { permitTransferFrom, , signature - ) = _getPermitWitnessTransferFromParams(); + ) = _getPermit2WitnessTransferFromParamsSignedByPERMIT2_USER(); // Execute + vm.prank(USER_SENDER); // Can be executed by anyone permit2Proxy.callDiamondWithPermit2Witness( diamondCalldata, PERMIT2_USER, @@ -296,10 +290,15 @@ contract Permit2ProxyTest is TestBase { ); } - function test_can_generrate_a_valid_msg_hash_for_signing() public { + function test_can_generate_a_valid_msg_hash_for_signing() public { bytes32 msgHash; bytes32 generatedMsgHash; - (, , msgHash, ) = _getPermitWitnessTransferFromParams(); + ( + , + , + msgHash, + + ) = _getPermit2WitnessTransferFromParamsSignedByPERMIT2_USER(); generatedMsgHash = permit2Proxy.getPermit2MsgHash( _getCalldataForBridging(), @@ -324,7 +323,7 @@ contract Permit2ProxyTest is TestBase { permitTransferFrom, , signature - ) = _getPermitWitnessTransferFromParams(); + ) = _getPermit2WitnessTransferFromParamsSignedByPERMIT2_USER(); // Execute x2 permit2Proxy.callDiamondWithPermit2Witness( @@ -352,9 +351,9 @@ contract Permit2ProxyTest is TestBase { permitTransferFrom, , signature - ) = _getPermitWitnessTransferFromParams(); + ) = _getPermit2WitnessTransferFromParamsSignedByPERMIT2_USER(); - bytes memory MALICIOUS_CALLDATA; + bytes memory MALICIOUS_CALLDATA = hex"1337c0d3"; // Execute vm.expectRevert(InvalidSigner.selector); @@ -366,7 +365,9 @@ contract Permit2ProxyTest is TestBase { ); } - function testRevert_cannot_use_signature_from_another_wallet() public { + function testRevert_cannot_use_permit2_signature_from_another_wallet() + public + { deal(ADDRESS_USDC, PERMIT2_USER, 10000 ether); bytes memory diamondCalldata; ISignatureTransfer.PermitTransferFrom memory permitTransferFrom; @@ -376,8 +377,9 @@ contract Permit2ProxyTest is TestBase { permitTransferFrom, msgHash, - ) = _getPermitWitnessTransferFromParams(); + ) = _getPermit2WitnessTransferFromParamsSignedByPERMIT2_USER(); + // Sign with a random key bytes memory signature = _signMsgHash(msgHash, 987654321); // Execute @@ -400,8 +402,9 @@ contract Permit2ProxyTest is TestBase { permitTransferFrom, msgHash, - ) = _getPermitWitnessTransferFromParams(); + ) = _getPermit2WitnessTransferFromParamsSignedByPERMIT2_USER(); + // Sign with a random key bytes memory signature = _signMsgHash(msgHash, 987654321); permitTransferFrom.permitted.amount = 500 ether; @@ -418,7 +421,7 @@ contract Permit2ProxyTest is TestBase { /// Helper Functions /// - function _getPermitTransferFromParams() + function _getPermit2TransferFromParamsSignedByPERMIT2_USER() internal view returns ( @@ -460,7 +463,7 @@ contract Permit2ProxyTest is TestBase { ); } - function _getPermitWitnessTransferFromParams() + function _getPermit2WitnessTransferFromParamsSignedByPERMIT2_USER() internal view returns ( From 54ece210e85d040a103a1fd6eed87f489c989d43 Mon Sep 17 00:00:00 2001 From: Ed Zynda Date: Wed, 4 Sep 2024 15:04:51 +0300 Subject: [PATCH 33/69] Fixes --- src/Periphery/Permit2Proxy.sol | 4 +- test/solidity/Periphery/Permit2Proxy.t.sol | 50 ++++++++++++---------- 2 files changed, 29 insertions(+), 25 deletions(-) diff --git a/src/Periphery/Permit2Proxy.sol b/src/Periphery/Permit2Proxy.sol index ea75f5375..fb15f5e9d 100644 --- a/src/Periphery/Permit2Proxy.sol +++ b/src/Periphery/Permit2Proxy.sol @@ -1,5 +1,5 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.17; +// SPDX-License-Identifier: MIT +pragma solidity 0.8.17; import { ISignatureTransfer } from "permit2/interfaces/ISignatureTransfer.sol"; import { TransferrableOwnership } from "lifi/Helpers/TransferrableOwnership.sol"; diff --git a/test/solidity/Periphery/Permit2Proxy.t.sol b/test/solidity/Periphery/Permit2Proxy.t.sol index b613a738e..531483f3e 100644 --- a/test/solidity/Periphery/Permit2Proxy.t.sol +++ b/test/solidity/Periphery/Permit2Proxy.t.sol @@ -1,5 +1,5 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.17; +// SPDX-License-Identifier: MIT +pragma solidity 0.8.17; import { Test, TestBase, DSTest, ILiFi, console, ERC20 } from "../utils/TestBase.sol"; import { Permit2Proxy } from "lifi/Periphery/Permit2Proxy.sol"; @@ -85,11 +85,12 @@ contract Permit2ProxyTest is TestBase { bytes32 domainSeparator = ERC20Permit(ADDRESS_USDC).DOMAIN_SEPARATOR(); // // using USDC on ETH for testing (implements EIP2612) - TestDataEIP2612 memory testdata = _getTestDataEIP2612( - ADDRESS_USDC, - domainSeparator, - block.timestamp + 1000 - ); + TestDataEIP2612 + memory testdata = _getTestDataEIP2612SignedByPERMIT2_USER( + ADDRESS_USDC, + domainSeparator, + block.timestamp + 1000 + ); // expect LifiTransferStarted event to be emitted by our diamond contract vm.expectEmit(true, true, true, true, DIAMOND_ADDRESS); @@ -142,11 +143,12 @@ contract Permit2ProxyTest is TestBase { bytes32 domainSeparator = ERC20Permit(ADDRESS_USDC).DOMAIN_SEPARATOR(); // // using USDC on ETH for testing (implements EIP2612) - TestDataEIP2612 memory testdata = _getTestDataEIP2612( - ADDRESS_USDC, - domainSeparator, - block.timestamp - 1 // deadline in the past - ); + TestDataEIP2612 + memory testdata = _getTestDataEIP2612SignedByPERMIT2_USER( + ADDRESS_USDC, + domainSeparator, + block.timestamp - 1 // deadline in the past + ); // expect call to revert since signature deadline is in the past vm.expectRevert("FiatTokenV2: permit is expired"); @@ -172,11 +174,12 @@ contract Permit2ProxyTest is TestBase { bytes32 domainSeparator = ERC20Permit(ADDRESS_USDC).DOMAIN_SEPARATOR(); // // using USDC on ETH for testing (implements EIP2612) - TestDataEIP2612 memory testdata = _getTestDataEIP2612( - ADDRESS_USDC, - domainSeparator, - block.timestamp - ); + TestDataEIP2612 + memory testdata = _getTestDataEIP2612SignedByPERMIT2_USER( + ADDRESS_USDC, + domainSeparator, + block.timestamp + ); // expect call to revert since signature is invalid vm.expectRevert("EIP2612: invalid signature"); @@ -202,11 +205,12 @@ contract Permit2ProxyTest is TestBase { bytes32 domainSeparator = ERC20Permit(ADDRESS_USDC).DOMAIN_SEPARATOR(); // // using USDC on ETH for testing (implements EIP2612) - TestDataEIP2612 memory testdata = _getTestDataEIP2612( - ADDRESS_USDC, - domainSeparator, - block.timestamp - ); + TestDataEIP2612 + memory testdata = _getTestDataEIP2612SignedByPERMIT2_USER( + ADDRESS_USDC, + domainSeparator, + block.timestamp + ); // expect call to revert since signature was created by a different address vm.expectRevert("EIP2612: invalid signature"); @@ -595,7 +599,7 @@ contract Permit2ProxyTest is TestBase { keccak256(abi.encodePacked("\x19\x01", domainSeparator, dataHash)); } - function _getTestDataEIP2612( + function _getTestDataEIP2612SignedByPERMIT2_USER( address tokenAddress, bytes32 domainSeparator, uint256 deadline From f277666559ce95e74f71978440889d0fb23a770b Mon Sep 17 00:00:00 2001 From: Ed Zynda Date: Wed, 4 Sep 2024 15:34:53 +0300 Subject: [PATCH 34/69] Boost test coverage --- test/solidity/Periphery/Permit2Proxy.t.sol | 63 ++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/test/solidity/Periphery/Permit2Proxy.t.sol b/test/solidity/Periphery/Permit2Proxy.t.sol index 531483f3e..f4bc8c78f 100644 --- a/test/solidity/Periphery/Permit2Proxy.t.sol +++ b/test/solidity/Periphery/Permit2Proxy.t.sol @@ -45,6 +45,7 @@ contract Permit2ProxyTest is TestBase { error InvalidSigner(); error InvalidNonce(); error DiamondAddressNotWhitelisted(); + error CallToDiamondFailed(bytes); function setUp() public { customBlockNumberForForking = 20261175; @@ -115,6 +116,38 @@ contract Permit2ProxyTest is TestBase { return testdata; } + function testRevert_when_called_with_invalid_calldata() public { + vm.startPrank(PERMIT2_USER); + + // get token-specific domainSeparator + bytes32 domainSeparator = ERC20Permit(ADDRESS_USDC).DOMAIN_SEPARATOR(); + + // // using USDC on ETH for testing (implements EIP2612) + TestDataEIP2612 + memory testdata = _getTestDataEIP2612SignedByPERMIT2_USER( + ADDRESS_USDC, + domainSeparator, + block.timestamp + 1000 + ); + + // call Permit2Proxy with signature + vm.expectRevert( + abi.encodeWithSignature( + "CallToDiamondFailed(bytes)", + hex"a9ad62f8" // Function does not exist + ) + ); + permit2Proxy.callDiamondWithEIP2612Signature( + ADDRESS_USDC, + defaultUSDCAmount, + testdata.deadline, + testdata.v, + testdata.r, + testdata.s, + hex"1337c0d3" // This should revert + ); + } + function testRevert_cannot_use_eip2612_signature_twice() public { TestDataEIP2612 memory testdata = test_can_execute_calldata_using_eip2612_signature_usdc(); @@ -423,6 +456,36 @@ contract Permit2ProxyTest is TestBase { ); } + /// The following test code was adapted from https://github.com/flood-protocol/permit2-nonce-finder/blob/7a4ac8a58d0b499308000b75ddb2384834f31fac/test/Permit2NonceFinder.t.sol + + function test_can_find_nonce() public { + // We invalidate the first nonce to make sure it's not returned. + // We pass a mask of 0...0011 to invalidate nonce 0 and 1. + uniPermit2.invalidateUnorderedNonces(0, 3); + assertEq(permit2Proxy.nextNonce(address(this)), 2); + + // Invalidate the first word minus 1 nonce + uniPermit2.invalidateUnorderedNonces(0, type(uint256).max >> 1); + // We should find the last nonce in the first word + assertEq(permit2Proxy.nextNonce(address(this)), 255); + } + + function test_can_find_nonce_after() public { + // We want to start from the second word + uint256 start = 256; + // We invalidate the whole next word to make sure it's not returned. + uniPermit2.invalidateUnorderedNonces(1, type(uint256).max); + assertEq(permit2Proxy.nextNonceAfter(address(this), start), 512); + + // Invalidate the next word minus 1 nonce + uniPermit2.invalidateUnorderedNonces(2, type(uint256).max >> 1); + // We should find the first nonce in the third word + assertEq(permit2Proxy.nextNonceAfter(address(this), 767), 768); + + // The first word is still accessible if we start from a lower nonce + assertEq(permit2Proxy.nextNonceAfter(address(this), 1), 2); + } + /// Helper Functions /// function _getPermit2TransferFromParamsSignedByPERMIT2_USER() From 010c923900f16ebbcff560eea865a1d0a1cb1860 Mon Sep 17 00:00:00 2001 From: Ed Zynda Date: Fri, 6 Sep 2024 12:06:00 +0300 Subject: [PATCH 35/69] More fixes --- script/deploy/resources/deployRequirements.json | 11 ++++++++++- test/solidity/Periphery/Permit2Proxy.t.sol | 15 ++++++--------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/script/deploy/resources/deployRequirements.json b/script/deploy/resources/deployRequirements.json index 3c3585753..59588fce9 100644 --- a/script/deploy/resources/deployRequirements.json +++ b/script/deploy/resources/deployRequirements.json @@ -462,5 +462,14 @@ "allowToDeployWithZeroAddress": "false" } } + }, + "Permit2Proxy": { + "configData": { + "permit2Address": { + "configFileName": "permit2Proxy.json", + "keyInConfigFile": ".", + "allowToDeployWithZeroAddress": "false" + } + } } -} +} \ No newline at end of file diff --git a/test/solidity/Periphery/Permit2Proxy.t.sol b/test/solidity/Periphery/Permit2Proxy.t.sol index f4bc8c78f..36879f3a9 100644 --- a/test/solidity/Periphery/Permit2Proxy.t.sol +++ b/test/solidity/Periphery/Permit2Proxy.t.sol @@ -75,11 +75,13 @@ contract Permit2ProxyTest is TestBase { function test_can_execute_calldata_using_eip2612_signature_usdc() public + assertBalanceChange( + ADDRESS_USDC, + PERMIT2_USER, + -int256(defaultUSDCAmount) + ) returns (TestDataEIP2612 memory) { - uint256 startingUSDCBalance = ERC20(ADDRESS_USDC).balanceOf( - PERMIT2_USER - ); vm.startPrank(PERMIT2_USER); // get token-specific domainSeparator @@ -107,11 +109,6 @@ contract Permit2ProxyTest is TestBase { testdata.s, testdata.diamondCalldata ); - uint256 endingUSDCBalance = ERC20(ADDRESS_USDC).balanceOf( - PERMIT2_USER - ); - uint256 delta = startingUSDCBalance - endingUSDCBalance; - assertEq(defaultUSDCAmount, delta); vm.stopPrank(); return testdata; } @@ -144,7 +141,7 @@ contract Permit2ProxyTest is TestBase { testdata.v, testdata.r, testdata.s, - hex"1337c0d3" // This should revert + hex"1337c0d3" // This should revert as the method does not exist ); } From 4397f5915d1f96b2bc624fafe65ae09e9e47a6bf Mon Sep 17 00:00:00 2001 From: Ed Zynda Date: Fri, 6 Sep 2024 12:23:02 +0300 Subject: [PATCH 36/69] More fixes --- src/Periphery/Permit2Proxy.sol | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/Periphery/Permit2Proxy.sol b/src/Periphery/Permit2Proxy.sol index fb15f5e9d..5d673e518 100644 --- a/src/Periphery/Permit2Proxy.sol +++ b/src/Periphery/Permit2Proxy.sol @@ -68,7 +68,7 @@ contract Permit2Proxy { /// @param v User signature (recovery ID) /// @param r User signature (ECDSA output) /// @param s User signature (ECDSA output) - /// @param diamondCalldata Address of the token to be bridged + /// @param diamondCalldata calldata to execute function callDiamondWithEIP2612Signature( address tokenAddress, uint256 amount, @@ -77,7 +77,7 @@ contract Permit2Proxy { bytes32 r, bytes32 s, bytes calldata diamondCalldata - ) public payable { + ) public payable returns (bytes memory) { // call permit on token contract to register approval using signature ERC20Permit(tokenAddress).permit( msg.sender, // Ensure msg.sender is same wallet that signed permit @@ -101,7 +101,7 @@ contract Permit2Proxy { LibAsset.maxApproveERC20(IERC20(tokenAddress), LIFI_DIAMOND, amount); // call our diamond to execute calldata - _executeCalldata(diamondCalldata); + return _executeCalldata(diamondCalldata); } /// @notice Allows to bridge tokens of one type through a LI.FI diamond @@ -116,7 +116,7 @@ contract Permit2Proxy { bytes calldata _diamondCalldata, ISignatureTransfer.PermitTransferFrom calldata _permit, bytes calldata _signature - ) external payable { + ) external payable returns (bytes memory) { PERMIT2.permitTransferFrom( _permit, ISignatureTransfer.SignatureTransferDetails({ @@ -134,7 +134,7 @@ contract Permit2Proxy { _permit.permitted.amount ); - _executeCalldata(_diamondCalldata); + return _executeCalldata(_diamondCalldata); } /// @notice Allows to bridge tokens of one type through a LI.FI diamond @@ -149,7 +149,7 @@ contract Permit2Proxy { address _signer, ISignatureTransfer.PermitTransferFrom calldata _permit, bytes calldata _signature - ) external payable { + ) external payable returns (bytes memory) { LiFiCall memory lifiCall = LiFiCall( LIFI_DIAMOND, keccak256(_diamondCalldata) @@ -176,7 +176,7 @@ contract Permit2Proxy { _permit.permitted.amount ); - _executeCalldata(_diamondCalldata); + return _executeCalldata(_diamondCalldata); } /// @notice utitlity method for constructing a valid Permit2 message hash @@ -262,7 +262,9 @@ contract Permit2Proxy { keccak256(abi.encodePacked("\x19\x01", domainSeparator, dataHash)); } - function _executeCalldata(bytes memory diamondCalldata) internal { + function _executeCalldata( + bytes memory diamondCalldata + ) internal returns (bytes memory) { // call diamond with provided calldata // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory data) = LIFI_DIAMOND.call{ @@ -273,6 +275,7 @@ contract Permit2Proxy { if (!success) { revert CallToDiamondFailed(data); } + return data; } /// The following code was adapted from https://github.com/flood-protocol/permit2-nonce-finder/blob/7a4ac8a58d0b499308000b75ddb2384834f31fac/src/Permit2NonceFinder.sol From 3eaf8ba32972a9bfd78ab0c16d6998bfa97a46c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Thu, 19 Sep 2024 10:37:41 +0700 Subject: [PATCH 37/69] removes unused imports (audit issue#2) --- src/Periphery/Permit2Proxy.sol | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Periphery/Permit2Proxy.sol b/src/Periphery/Permit2Proxy.sol index 5d673e518..8febb7e4b 100644 --- a/src/Periphery/Permit2Proxy.sol +++ b/src/Periphery/Permit2Proxy.sol @@ -2,10 +2,8 @@ pragma solidity 0.8.17; import { ISignatureTransfer } from "permit2/interfaces/ISignatureTransfer.sol"; -import { TransferrableOwnership } from "lifi/Helpers/TransferrableOwnership.sol"; import { LibAsset, IERC20 } from "lifi/Libraries/LibAsset.sol"; import { PermitHash } from "permit2/libraries/PermitHash.sol"; -import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { ERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol"; From 976966de7ba14d1782904ebe7bad1b3fd2e79281 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Mon, 30 Sep 2024 12:11:58 +0700 Subject: [PATCH 38/69] adds WithdrawablePeriphery base contract to token withdrawals --- src/Helpers/WithdrawablePeriphery.sol | 35 ++++++ src/Periphery/Permit2Proxy.sol | 13 +- .../Helpers/WithdrawablePeriphery.sol | 116 ++++++++++++++++++ test/solidity/Periphery/Permit2Proxy.t.sol | 8 +- 4 files changed, 167 insertions(+), 5 deletions(-) create mode 100644 src/Helpers/WithdrawablePeriphery.sol create mode 100644 test/solidity/Helpers/WithdrawablePeriphery.sol diff --git a/src/Helpers/WithdrawablePeriphery.sol b/src/Helpers/WithdrawablePeriphery.sol new file mode 100644 index 000000000..66d201cb1 --- /dev/null +++ b/src/Helpers/WithdrawablePeriphery.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.17; + +import { TransferrableOwnership } from "./TransferrableOwnership.sol"; +import { LibAsset } from "../Libraries/LibAsset.sol"; +import { ExternalCallFailed } from "../Errors/GenericErrors.sol"; +import { SafeTransferLib } from "solady/utils/SafeTransferLib.sol"; + +abstract contract WithdrawablePeriphery is TransferrableOwnership { + using SafeTransferLib for address; + + event TokensWithdrawn( + address assetId, + address payable receiver, + uint256 amount + ); + + constructor(address _owner) TransferrableOwnership(_owner) {} + + function withdrawToken( + address assetId, + address payable receiver, + uint256 amount + ) external onlyOwner { + if (LibAsset.isNativeAsset(assetId)) { + // solhint-disable-next-line avoid-low-level-calls + (bool success, ) = receiver.call{ value: amount }(""); + if (!success) revert ExternalCallFailed(); + } else { + assetId.safeTransfer(receiver, amount); + } + + emit TokensWithdrawn(assetId, receiver, amount); + } +} diff --git a/src/Periphery/Permit2Proxy.sol b/src/Periphery/Permit2Proxy.sol index 8febb7e4b..b83ccda71 100644 --- a/src/Periphery/Permit2Proxy.sol +++ b/src/Periphery/Permit2Proxy.sol @@ -4,15 +4,15 @@ pragma solidity 0.8.17; import { ISignatureTransfer } from "permit2/interfaces/ISignatureTransfer.sol"; import { LibAsset, IERC20 } from "lifi/Libraries/LibAsset.sol"; import { PermitHash } from "permit2/libraries/PermitHash.sol"; -import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { ERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol"; +import { WithdrawablePeriphery } from "lifi/Helpers/WithdrawablePeriphery.sol"; /// @title Permit2Proxy /// @author LI.FI (https://li.fi) /// @notice Proxy contract allowing gasless calls via Permit2 as well as making /// token approvals via ERC20 Permit (EIP-2612) to our diamond contract /// @custom:version 1.0.0 -contract Permit2Proxy { +contract Permit2Proxy is WithdrawablePeriphery { /// Storage /// address public immutable LIFI_DIAMOND; @@ -41,7 +41,11 @@ contract Permit2Proxy { /// Constructor /// - constructor(address _lifiDiamond, ISignatureTransfer _permit2) { + constructor( + address _lifiDiamond, + ISignatureTransfer _permit2, + address _owner + ) WithdrawablePeriphery(_owner) { LIFI_DIAMOND = _lifiDiamond; PERMIT2 = _permit2; @@ -354,4 +358,7 @@ contract Permit2Proxy { // The first 8 bits of the word are the position inside the word nonce |= pos; } + + // required to be able to receive native token refunds from the diamond + receive() external payable {} } diff --git a/test/solidity/Helpers/WithdrawablePeriphery.sol b/test/solidity/Helpers/WithdrawablePeriphery.sol new file mode 100644 index 000000000..43e8c5e85 --- /dev/null +++ b/test/solidity/Helpers/WithdrawablePeriphery.sol @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: MIT +/// @custom:version 1.0.0 +pragma solidity 0.8.17; + +import { WithdrawablePeriphery } from "lifi/Helpers/WithdrawablePeriphery.sol"; +import { TestBase } from "../utils/TestBase.sol"; +import { NonETHReceiver } from "../utils/TestHelpers.sol"; + +contract TestContract is WithdrawablePeriphery { + constructor(address _owner) WithdrawablePeriphery(_owner) {} +} + +contract WithdrawablePeripheryTest is TestBase { + WithdrawablePeriphery internal withdrawable; + event TokensWithdrawn( + address assetId, + address payable receiver, + uint256 amount + ); + error UnAuthorized(); + + function setUp() public { + initTestBase(); + + // deploy contract + withdrawable = new TestContract(USER_DIAMOND_OWNER); + + // fund contract with native and ERC20 + deal( + ADDRESS_USDC, + address(withdrawable), + 100_000 * 10 ** usdc.decimals() + ); + deal(address(withdrawable), 1 ether); + } + + function test_AllowsOwnerToWithdrawNative() public { + uint256 withdrawAmount = 0.1 ether; + + vm.startPrank(USER_DIAMOND_OWNER); + + vm.expectEmit(true, true, true, true, address(withdrawable)); + emit TokensWithdrawn( + address(0), + payable(USER_RECEIVER), + withdrawAmount + ); + + withdrawable.withdrawToken( + address(0), + payable(USER_RECEIVER), + withdrawAmount + ); + } + + function test_AllowsOwnerToWithdrawERC20() public { + uint256 withdrawAmount = 10 * 10 ** usdc.decimals(); + vm.startPrank(USER_DIAMOND_OWNER); + + vm.expectEmit(true, true, true, true, address(withdrawable)); + emit TokensWithdrawn( + ADDRESS_USDC, + payable(USER_RECEIVER), + withdrawAmount + ); + + withdrawable.withdrawToken( + ADDRESS_USDC, + payable(USER_RECEIVER), + withdrawAmount + ); + } + + function testRevert_FailsIfNonOwnerTriesToWithdrawNative() public { + uint256 withdrawAmount = 0.1 ether; + + vm.startPrank(USER_SENDER); + + vm.expectRevert(UnAuthorized.selector); + + withdrawable.withdrawToken( + address(0), + payable(USER_RECEIVER), + withdrawAmount + ); + } + + function testRevert_FailsIfNonOwnerTriesToWithdrawERC20() public { + uint256 withdrawAmount = 10 * 10 ** usdc.decimals(); + vm.startPrank(USER_SENDER); + + vm.expectRevert(UnAuthorized.selector); + + withdrawable.withdrawToken( + ADDRESS_USDC, + payable(USER_RECEIVER), + withdrawAmount + ); + } + + function testRevert_FailsIfNativeTokenTransferFails() public { + uint256 withdrawAmount = 10 * 10 ** usdc.decimals(); + + address nonETHReceiver = address(new NonETHReceiver()); + + vm.startPrank(USER_SENDER); + + vm.expectRevert(UnAuthorized.selector); + + withdrawable.withdrawToken( + ADDRESS_USDC, + payable(nonETHReceiver), + withdrawAmount + ); + } +} diff --git a/test/solidity/Periphery/Permit2Proxy.t.sol b/test/solidity/Periphery/Permit2Proxy.t.sol index 36879f3a9..bc6e55251 100644 --- a/test/solidity/Periphery/Permit2Proxy.t.sol +++ b/test/solidity/Periphery/Permit2Proxy.t.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.17; -import { Test, TestBase, DSTest, ILiFi, console, ERC20 } from "../utils/TestBase.sol"; +import { TestBase, ILiFi, console, ERC20 } from "../utils/TestBase.sol"; import { Permit2Proxy } from "lifi/Periphery/Permit2Proxy.sol"; import { ISignatureTransfer } from "permit2/interfaces/ISignatureTransfer.sol"; import { PermitHash } from "permit2/libraries/PermitHash.sol"; @@ -52,7 +52,11 @@ contract Permit2ProxyTest is TestBase { initTestBase(); uniPermit2 = ISignatureTransfer(PERMIT2_ADDRESS); - permit2Proxy = new Permit2Proxy(DIAMOND_ADDRESS, uniPermit2); + permit2Proxy = new Permit2Proxy( + DIAMOND_ADDRESS, + uniPermit2, + USER_DIAMOND_OWNER + ); PERMIT_WITH_WITNESS_TYPEHASH = keccak256( abi.encodePacked( PermitHash._PERMIT_TRANSFER_FROM_WITNESS_TYPEHASH_STUB, From b1ebb1c930053f8162f427c7383e85072861cce9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Mon, 7 Oct 2024 08:48:45 +0700 Subject: [PATCH 39/69] audit report added --- audit/auditLog.json | 10 ++++++++++ audit/reports/2024.10.03_Permit2Proxy.pdf | Bin 0 -> 67118 bytes 2 files changed, 10 insertions(+) create mode 100644 audit/reports/2024.10.03_Permit2Proxy.pdf diff --git a/audit/auditLog.json b/audit/auditLog.json index af918a2bb..a15d62c1e 100644 --- a/audit/auditLog.json +++ b/audit/auditLog.json @@ -6,9 +6,19 @@ "auditorGitHandle": "sujithsomraaj", "auditReportPath": "./audit/reports/2024.08.14_StargateFacetV2_ReAudit.pdf", "auditCommitHash": "d622002440317580b5d0fb90ef22b839d84957e2" + }, + "audit20241003": { + "auditCompletedOn": "10.03.2024", + "auditedBy": "Sujith Somraaj (individual security researcher)", + "auditorGitHandle": "sujithsomraaj", + "auditReportPath": "./audit/reports/2024.10.03_Permit2Proxy.pdf", + "auditCommitHash": "0e3debb78abcdf9a9f934115338b611e16b039a0" } }, "auditedContracts": { + "Permit2Proxy": { + "1.0.0": ["audit20241003"] + }, "StargateFacetV2": { "1.0.1": ["audit20240814"] } diff --git a/audit/reports/2024.10.03_Permit2Proxy.pdf b/audit/reports/2024.10.03_Permit2Proxy.pdf new file mode 100644 index 0000000000000000000000000000000000000000..0ba9ef6d1f77cd15e0684958e586f344fe134bdf GIT binary patch literal 67118 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 V59Q1VASVZvQ3PDSjx^8&{0nLMMPvW~ literal 0 HcmV?d00001 From 59a356c4e74f689e3beef97e2ff596c0418d0020 Mon Sep 17 00:00:00 2001 From: Daniel <77058885+0xDEnYO@users.noreply.github.com> Date: Mon, 7 Oct 2024 09:45:20 +0700 Subject: [PATCH 40/69] Update src/Periphery/Permit2Proxy.sol Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- src/Periphery/Permit2Proxy.sol | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Periphery/Permit2Proxy.sol b/src/Periphery/Permit2Proxy.sol index b83ccda71..62f11663c 100644 --- a/src/Periphery/Permit2Proxy.sol +++ b/src/Periphery/Permit2Proxy.sol @@ -61,8 +61,9 @@ contract Permit2Proxy is WithdrawablePeriphery { /// @notice Allows to bridge tokens through a LI.FI diamond contract using /// an EIP2612 gasless permit (only works with tokenAddresses that - /// implement EIP2612) (in contrast to Permit2, calldata and diamondAddress - /// are not signed by the user and could therefore be replaced by the user) + /// 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 From a40f709f7e6b0253adddbfdbe6aa18c37a8f01d7 Mon Sep 17 00:00:00 2001 From: Daniel <77058885+0xDEnYO@users.noreply.github.com> Date: Mon, 7 Oct 2024 09:45:50 +0700 Subject: [PATCH 41/69] Update src/Periphery/Permit2Proxy.sol Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- src/Periphery/Permit2Proxy.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Periphery/Permit2Proxy.sol b/src/Periphery/Permit2Proxy.sol index 62f11663c..8adc21613 100644 --- a/src/Periphery/Permit2Proxy.sol +++ b/src/Periphery/Permit2Proxy.sol @@ -109,8 +109,8 @@ contract Permit2Proxy is WithdrawablePeriphery { /// @notice Allows to bridge tokens of one type through a LI.FI diamond /// contract using Uniswap's Permit2 contract and a user signature - /// that verifies allowance. The calldata can be changed by the - /// user. Can only be called by the permit signer to prevent + /// that verifies allowance. 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 _diamondCalldata the calldata to execute /// @param _permit the Uniswap Permit2 parameters From 9772ba19afa0dfe4ef8e7ba04a33eb1c0a1b1139 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Thu, 10 Oct 2024 09:29:05 +0700 Subject: [PATCH 42/69] rename test file to match naming convention --- .../{WithdrawablePeriphery.sol => WithdrawablePeriphery.t.sol} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename test/solidity/Helpers/{WithdrawablePeriphery.sol => WithdrawablePeriphery.t.sol} (100%) diff --git a/test/solidity/Helpers/WithdrawablePeriphery.sol b/test/solidity/Helpers/WithdrawablePeriphery.t.sol similarity index 100% rename from test/solidity/Helpers/WithdrawablePeriphery.sol rename to test/solidity/Helpers/WithdrawablePeriphery.t.sol From b03e658b359f1696388e2aaeaf24a7c107ba462a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Thu, 10 Oct 2024 09:46:48 +0700 Subject: [PATCH 43/69] adds WithdrawablePeriphery base contract for token withdrawals --- src/Helpers/WithdrawablePeriphery.sol | 35 ++++++ .../Helpers/WithdrawablePeriphery.t.sol | 116 ++++++++++++++++++ 2 files changed, 151 insertions(+) create mode 100644 src/Helpers/WithdrawablePeriphery.sol create mode 100644 test/solidity/Helpers/WithdrawablePeriphery.t.sol diff --git a/src/Helpers/WithdrawablePeriphery.sol b/src/Helpers/WithdrawablePeriphery.sol new file mode 100644 index 000000000..66d201cb1 --- /dev/null +++ b/src/Helpers/WithdrawablePeriphery.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.17; + +import { TransferrableOwnership } from "./TransferrableOwnership.sol"; +import { LibAsset } from "../Libraries/LibAsset.sol"; +import { ExternalCallFailed } from "../Errors/GenericErrors.sol"; +import { SafeTransferLib } from "solady/utils/SafeTransferLib.sol"; + +abstract contract WithdrawablePeriphery is TransferrableOwnership { + using SafeTransferLib for address; + + event TokensWithdrawn( + address assetId, + address payable receiver, + uint256 amount + ); + + constructor(address _owner) TransferrableOwnership(_owner) {} + + function withdrawToken( + address assetId, + address payable receiver, + uint256 amount + ) external onlyOwner { + if (LibAsset.isNativeAsset(assetId)) { + // solhint-disable-next-line avoid-low-level-calls + (bool success, ) = receiver.call{ value: amount }(""); + if (!success) revert ExternalCallFailed(); + } else { + assetId.safeTransfer(receiver, amount); + } + + emit TokensWithdrawn(assetId, receiver, amount); + } +} diff --git a/test/solidity/Helpers/WithdrawablePeriphery.t.sol b/test/solidity/Helpers/WithdrawablePeriphery.t.sol new file mode 100644 index 000000000..43e8c5e85 --- /dev/null +++ b/test/solidity/Helpers/WithdrawablePeriphery.t.sol @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: MIT +/// @custom:version 1.0.0 +pragma solidity 0.8.17; + +import { WithdrawablePeriphery } from "lifi/Helpers/WithdrawablePeriphery.sol"; +import { TestBase } from "../utils/TestBase.sol"; +import { NonETHReceiver } from "../utils/TestHelpers.sol"; + +contract TestContract is WithdrawablePeriphery { + constructor(address _owner) WithdrawablePeriphery(_owner) {} +} + +contract WithdrawablePeripheryTest is TestBase { + WithdrawablePeriphery internal withdrawable; + event TokensWithdrawn( + address assetId, + address payable receiver, + uint256 amount + ); + error UnAuthorized(); + + function setUp() public { + initTestBase(); + + // deploy contract + withdrawable = new TestContract(USER_DIAMOND_OWNER); + + // fund contract with native and ERC20 + deal( + ADDRESS_USDC, + address(withdrawable), + 100_000 * 10 ** usdc.decimals() + ); + deal(address(withdrawable), 1 ether); + } + + function test_AllowsOwnerToWithdrawNative() public { + uint256 withdrawAmount = 0.1 ether; + + vm.startPrank(USER_DIAMOND_OWNER); + + vm.expectEmit(true, true, true, true, address(withdrawable)); + emit TokensWithdrawn( + address(0), + payable(USER_RECEIVER), + withdrawAmount + ); + + withdrawable.withdrawToken( + address(0), + payable(USER_RECEIVER), + withdrawAmount + ); + } + + function test_AllowsOwnerToWithdrawERC20() public { + uint256 withdrawAmount = 10 * 10 ** usdc.decimals(); + vm.startPrank(USER_DIAMOND_OWNER); + + vm.expectEmit(true, true, true, true, address(withdrawable)); + emit TokensWithdrawn( + ADDRESS_USDC, + payable(USER_RECEIVER), + withdrawAmount + ); + + withdrawable.withdrawToken( + ADDRESS_USDC, + payable(USER_RECEIVER), + withdrawAmount + ); + } + + function testRevert_FailsIfNonOwnerTriesToWithdrawNative() public { + uint256 withdrawAmount = 0.1 ether; + + vm.startPrank(USER_SENDER); + + vm.expectRevert(UnAuthorized.selector); + + withdrawable.withdrawToken( + address(0), + payable(USER_RECEIVER), + withdrawAmount + ); + } + + function testRevert_FailsIfNonOwnerTriesToWithdrawERC20() public { + uint256 withdrawAmount = 10 * 10 ** usdc.decimals(); + vm.startPrank(USER_SENDER); + + vm.expectRevert(UnAuthorized.selector); + + withdrawable.withdrawToken( + ADDRESS_USDC, + payable(USER_RECEIVER), + withdrawAmount + ); + } + + function testRevert_FailsIfNativeTokenTransferFails() public { + uint256 withdrawAmount = 10 * 10 ** usdc.decimals(); + + address nonETHReceiver = address(new NonETHReceiver()); + + vm.startPrank(USER_SENDER); + + vm.expectRevert(UnAuthorized.selector); + + withdrawable.withdrawToken( + ADDRESS_USDC, + payable(nonETHReceiver), + withdrawAmount + ); + } +} From bcf24aadc1ae01664f06b1f3088335d584c57f88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Thu, 10 Oct 2024 10:22:56 +0700 Subject: [PATCH 44/69] fix test --- test/solidity/Helpers/WithdrawablePeriphery.t.sol | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/solidity/Helpers/WithdrawablePeriphery.t.sol b/test/solidity/Helpers/WithdrawablePeriphery.t.sol index 43e8c5e85..c6624bf3d 100644 --- a/test/solidity/Helpers/WithdrawablePeriphery.t.sol +++ b/test/solidity/Helpers/WithdrawablePeriphery.t.sol @@ -99,16 +99,16 @@ contract WithdrawablePeripheryTest is TestBase { } function testRevert_FailsIfNativeTokenTransferFails() public { - uint256 withdrawAmount = 10 * 10 ** usdc.decimals(); + uint256 withdrawAmount = 0.1 ether; address nonETHReceiver = address(new NonETHReceiver()); - vm.startPrank(USER_SENDER); + vm.startPrank(USER_DIAMOND_OWNER); - vm.expectRevert(UnAuthorized.selector); + vm.expectRevert(); withdrawable.withdrawToken( - ADDRESS_USDC, + address(0), payable(nonETHReceiver), withdrawAmount ); From 40bf7ee5ca7cb71048f14cb183d8244afec786c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Thu, 10 Oct 2024 10:30:08 +0700 Subject: [PATCH 45/69] adds version tag to contract --- src/Helpers/WithdrawablePeriphery.sol | 1 + test/solidity/Helpers/WithdrawablePeriphery.t.sol | 3 +++ 2 files changed, 4 insertions(+) diff --git a/src/Helpers/WithdrawablePeriphery.sol b/src/Helpers/WithdrawablePeriphery.sol index 66d201cb1..9e4a08203 100644 --- a/src/Helpers/WithdrawablePeriphery.sol +++ b/src/Helpers/WithdrawablePeriphery.sol @@ -1,4 +1,5 @@ // SPDX-License-Identifier: MIT +/// @custom:version 1.0.0 pragma solidity 0.8.17; import { TransferrableOwnership } from "./TransferrableOwnership.sol"; diff --git a/test/solidity/Helpers/WithdrawablePeriphery.t.sol b/test/solidity/Helpers/WithdrawablePeriphery.t.sol index c6624bf3d..bba1a0f12 100644 --- a/test/solidity/Helpers/WithdrawablePeriphery.t.sol +++ b/test/solidity/Helpers/WithdrawablePeriphery.t.sol @@ -3,6 +3,7 @@ pragma solidity 0.8.17; import { WithdrawablePeriphery } from "lifi/Helpers/WithdrawablePeriphery.sol"; + import { TestBase } from "../utils/TestBase.sol"; import { NonETHReceiver } from "../utils/TestHelpers.sol"; @@ -12,11 +13,13 @@ contract TestContract is WithdrawablePeriphery { contract WithdrawablePeripheryTest is TestBase { WithdrawablePeriphery internal withdrawable; + event TokensWithdrawn( address assetId, address payable receiver, uint256 amount ); + error UnAuthorized(); function setUp() public { From 6200a1b95ab307b56ba1baffbb1a6143ae87b695 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Mon, 14 Oct 2024 06:41:09 +0700 Subject: [PATCH 46/69] target state updated --- script/deploy/_targetState.json | 298 ++++++++++++++++++++++---------- 1 file changed, 205 insertions(+), 93 deletions(-) diff --git a/script/deploy/_targetState.json b/script/deploy/_targetState.json index bad1ec2b6..9b6df9a30 100644 --- a/script/deploy/_targetState.json +++ b/script/deploy/_targetState.json @@ -22,8 +22,12 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", "AcrossFacet": "2.0.0", "AcrossFacetPacked": "1.0.0", + "AcrossFacetV3": "1.0.0", + "AcrossFacetPackedV3": "1.0.0", + "ReceiverAcrossV3": "1.0.0", "AllBridgeFacet": "2.0.0", "AmarokFacet": "3.0.0", "AmarokFacetPacked": "1.0.0", @@ -33,7 +37,6 @@ "CelerCircleBridgeFacet": "1.0.1", "RelayerCelerIM": "2.0.0", "CelerIMFacetMutable": "2.0.0", - "CircleBridgeFacet": "1.0.0", "GnosisBridgeFacet": "1.0.0", "HopFacet": "2.0.0", "HopFacetPacked": "1.0.6", @@ -71,8 +74,12 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", "AcrossFacet": "2.0.0", "AcrossFacetPacked": "1.0.0", + "AcrossFacetV3": "1.0.0", + "AcrossFacetPackedV3": "1.0.0", + "ReceiverAcrossV3": "1.0.0", "AllBridgeFacet": "2.0.0", "AmarokFacet": "3.0.0", "AmarokFacetPacked": "1.0.0", @@ -81,8 +88,6 @@ "CBridgeFacetPacked": "1.0.3", "CelerCircleBridgeFacet": "1.0.1", "RelayerCelerIM": "2.0.0", - "CelerIMFacetImmutable": "2.0.0", - "CircleBridgeFacet": "1.0.0", "GnosisBridgeFacet": "1.0.0", "HopFacet": "2.0.0", "HopFacetPacked": "1.0.6", @@ -124,8 +129,12 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", "AcrossFacet": "2.0.0", "AcrossFacetPacked": "1.0.0", + "AcrossFacetV3": "1.0.0", + "AcrossFacetPackedV3": "1.0.0", + "ReceiverAcrossV3": "1.0.0", "AllBridgeFacet": "2.0.0", "AmarokFacet": "3.0.0", "AmarokFacetPacked": "1.0.0", @@ -143,8 +152,7 @@ "StargateFacet": "2.2.0", "StargateFacetV2": "1.0.1", "ReceiverStargateV2": "1.0.0", - "SymbiosisFacet": "1.0.0", - "Permit2Proxy": "1.0.0" + "SymbiosisFacet": "1.0.0" }, "LiFiDiamondImmutable": { "DiamondCutFacet": "1.0.0", @@ -167,8 +175,12 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", "AcrossFacet": "2.0.0", "AcrossFacetPacked": "1.0.0", + "AcrossFacetV3": "1.0.0", + "AcrossFacetPackedV3": "1.0.0", + "ReceiverAcrossV3": "1.0.0", "AllBridgeFacet": "2.0.0", "AmarokFacet": "3.0.0", "AmarokFacetPacked": "1.0.0", @@ -176,7 +188,6 @@ "CBridgeFacetPacked": "1.0.3", "CelerCircleBridgeFacet": "1.0.1", "RelayerCelerIM": "2.0.0", - "CelerIMFacetImmutable": "2.0.0", "HopFacet": "2.0.0", "HopFacetPacked": "1.0.6", "HopFacetOptimized": "2.0.0", @@ -213,6 +224,7 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", "AllBridgeFacet": "2.0.0", "AmarokFacet": "3.0.0", "AmarokFacetPacked": "1.0.0", @@ -222,6 +234,7 @@ "CelerIMFacetMutable": "2.0.0", "HyphenFacet": "1.0.0", "MayanFacet": "1.0.0", + "OpBNBBridgeFacet": "\u001b[31m[error] the following filepath is invalid: \u001b[31m[error] could not find src FILE path for contract OpBNBBridgeFacet\u001b[0m\u001b[0m", "SquidFacet": "1.0.0", "StargateFacet": "2.2.0", "StargateFacetV2": "1.0.1", @@ -250,15 +263,16 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", "AllBridgeFacet": "2.0.0", "AmarokFacet": "3.0.0", "AmarokFacetPacked": "1.0.0", "CBridgeFacet": "1.0.0", "CBridgeFacetPacked": "1.0.3", "RelayerCelerIM": "2.0.0", - "CelerIMFacetImmutable": "2.0.0", "HyphenFacet": "1.0.0", "MayanFacet": "1.0.0", + "OpBNBBridgeFacet": "\u001b[31m[error] the following filepath is invalid: \u001b[31m[error] could not find src FILE path for contract OpBNBBridgeFacet\u001b[0m\u001b[0m", "SquidFacet": "1.0.0", "StargateFacet": "2.2.0", "StargateFacetV2": "1.0.1", @@ -291,10 +305,10 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", "AmarokFacet": "3.0.0", "AmarokFacetPacked": "1.0.0", "CBridgeFacet": "1.0.0", - "GnosisBridgeL2Facet": "1.0.0", "HopFacet": "2.0.0", "HopFacetPacked": "1.0.6", "HopFacetOptimized": "2.0.0", @@ -321,10 +335,10 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", "AmarokFacet": "3.0.0", "AmarokFacetPacked": "1.0.0", "CBridgeFacet": "1.0.0", - "GnosisBridgeL2Facet": "1.0.0", "HopFacet": "2.0.0", "HopFacetPacked": "1.0.6", "HopFacetOptimized": "2.0.0", @@ -355,6 +369,7 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", "CBridgeFacet": "1.0.0", "RelayerCelerIM": "2.0.0", "CelerIMFacetMutable": "2.0.0", @@ -383,9 +398,9 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", "CBridgeFacet": "1.0.0", "RelayerCelerIM": "2.0.0", - "CelerIMFacetImmutable": "2.0.0", "HyphenFacet": "1.0.0", "SquidFacet": "1.0.0", "StargateFacet": "2.2.0" @@ -416,13 +431,13 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", "AllBridgeFacet": "2.0.0", "CBridgeFacet": "1.0.0", "CBridgeFacetPacked": "1.0.3", "CelerCircleBridgeFacet": "1.0.1", "RelayerCelerIM": "2.0.0", "CelerIMFacetMutable": "2.0.0", - "CircleBridgeFacet": "1.0.0", "HyphenFacet": "1.0.0", "MayanFacet": "1.0.0", "SquidFacet": "1.0.0", @@ -453,13 +468,12 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", "AllBridgeFacet": "2.0.0", "CBridgeFacet": "1.0.0", "CBridgeFacetPacked": "1.0.3", "CelerCircleBridgeFacet": "1.0.1", "RelayerCelerIM": "2.0.0", - "CelerIMFacetImmutable": "2.0.0", - "CircleBridgeFacet": "1.0.0", "HyphenFacet": "1.0.0", "MayanFacet": "1.0.0", "SquidFacet": "1.0.0", @@ -494,8 +508,12 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", "AcrossFacet": "2.0.0", "AcrossFacetPacked": "1.0.0", + "AcrossFacetV3": "1.0.0", + "AcrossFacetPackedV3": "1.0.0", + "ReceiverAcrossV3": "1.0.0", "AllBridgeFacet": "2.0.0", "AmarokFacet": "3.0.0", "AmarokFacetPacked": "1.0.0", @@ -504,7 +522,6 @@ "CelerCircleBridgeFacet": "1.0.1", "RelayerCelerIM": "2.0.0", "CelerIMFacetMutable": "2.0.0", - "CircleBridgeFacet": "1.0.0", "HopFacet": "2.0.0", "HopFacetPacked": "1.0.6", "HopFacetOptimized": "2.0.0", @@ -514,8 +531,7 @@ "StargateFacet": "2.2.0", "StargateFacetV2": "1.0.1", "ReceiverStargateV2": "1.0.0", - "SymbiosisFacet": "1.0.0", - "Permit2Proxy": "1.0.0" + "SymbiosisFacet": "1.0.0" }, "LiFiDiamondImmutable": { "DiamondCutFacet": "1.0.0", @@ -538,8 +554,12 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", "AcrossFacet": "2.0.0", "AcrossFacetPacked": "1.0.0", + "AcrossFacetV3": "1.0.0", + "AcrossFacetPackedV3": "1.0.0", + "ReceiverAcrossV3": "1.0.0", "AllBridgeFacet": "2.0.0", "AmarokFacet": "3.0.0", "AmarokFacetPacked": "1.0.0", @@ -547,8 +567,6 @@ "CBridgeFacetPacked": "1.0.3", "CelerCircleBridgeFacet": "1.0.1", "RelayerCelerIM": "2.0.0", - "CelerIMFacetImmutable": "2.0.0", - "CircleBridgeFacet": "1.0.0", "HopFacet": "2.0.0", "HopFacetPacked": "1.0.6", "HopFacetOptimized": "2.0.0", @@ -585,8 +603,12 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", "AcrossFacet": "2.0.0", "AcrossFacetPacked": "1.0.0", + "AcrossFacetV3": "1.0.0", + "AcrossFacetPackedV3": "1.0.0", + "ReceiverAcrossV3": "1.0.0", "AllBridgeFacet": "2.0.0", "AmarokFacet": "3.0.0", "AmarokFacetPacked": "1.0.0", @@ -595,7 +617,6 @@ "CelerCircleBridgeFacet": "1.0.1", "RelayerCelerIM": "2.0.0", "CelerIMFacetMutable": "2.0.0", - "CircleBridgeFacet": "1.0.0", "HopFacet": "2.0.0", "HopFacetPacked": "1.0.6", "HopFacetOptimized": "2.0.0", @@ -603,8 +624,7 @@ "StargateFacet": "2.2.0", "StargateFacetV2": "1.0.1", "ReceiverStargateV2": "1.0.0", - "SymbiosisFacet": "1.0.0", - "Permit2Proxy": "1.0.0" + "SymbiosisFacet": "1.0.0" }, "LiFiDiamondImmutable": { "DiamondCutFacet": "1.0.0", @@ -627,8 +647,12 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", "AcrossFacet": "2.0.0", "AcrossFacetPacked": "1.0.0", + "AcrossFacetV3": "1.0.0", + "AcrossFacetPackedV3": "1.0.0", + "ReceiverAcrossV3": "1.0.0", "AllBridgeFacet": "2.0.0", "AmarokFacet": "3.0.0", "AmarokFacetPacked": "1.0.0", @@ -636,8 +660,6 @@ "CBridgeFacetPacked": "1.0.3", "CelerCircleBridgeFacet": "1.0.1", "RelayerCelerIM": "2.0.0", - "CelerIMFacetImmutable": "2.0.0", - "CircleBridgeFacet": "1.0.0", "HopFacet": "2.0.0", "HopFacetPacked": "1.0.6", "HopFacetOptimized": "2.0.0", @@ -672,6 +694,7 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", "CBridgeFacet": "1.0.0", "RelayerCelerIM": "2.0.0", "CelerIMFacetMutable": "2.0.0" @@ -697,9 +720,9 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", "CBridgeFacet": "1.0.0", - "RelayerCelerIM": "2.0.0", - "CelerIMFacetImmutable": "2.0.0" + "RelayerCelerIM": "2.0.0" } } }, @@ -726,6 +749,7 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", "CBridgeFacet": "1.0.0", "SquidFacet": "1.0.0" }, @@ -750,6 +774,7 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", "CBridgeFacet": "1.0.0", "SquidFacet": "1.0.0" } @@ -777,7 +802,8 @@ "Receiver": "2.0.2", "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", - "LiFiDEXAggregator": "1.0.0" + "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0" }, "LiFiDiamondImmutable": { "DiamondCutFacet": "1.0.0", @@ -799,7 +825,8 @@ "Receiver": "2.0.2", "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", - "LiFiDEXAggregator": "1.0.0" + "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0" } } }, @@ -829,6 +856,7 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", "CBridgeFacet": "1.0.0", "RelayerCelerIM": "2.0.0", "CelerIMFacetMutable": "2.0.0", @@ -856,9 +884,9 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", "CBridgeFacet": "1.0.0", "RelayerCelerIM": "2.0.0", - "CelerIMFacetImmutable": "2.0.0", "StargateFacetV2": "1.0.1", "ReceiverStargateV2": "1.0.0" } @@ -887,6 +915,7 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", "CBridgeFacet": "1.0.0", "SymbiosisFacet": "1.0.0" }, @@ -911,6 +940,7 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", "CBridgeFacet": "1.0.0", "SymbiosisFacet": "1.0.0" } @@ -939,6 +969,7 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", "RelayerCelerIM": "2.0.0", "CelerIMFacetMutable": "2.0.0", "SymbiosisFacet": "1.0.0" @@ -964,8 +995,8 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", "RelayerCelerIM": "2.0.0", - "CelerIMFacetImmutable": "2.0.0", "SymbiosisFacet": "1.0.0" } } @@ -993,8 +1024,12 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", "AcrossFacet": "2.0.0", "AcrossFacetPacked": "1.0.0", + "AcrossFacetV3": "1.0.0", + "AcrossFacetPackedV3": "1.0.0", + "ReceiverAcrossV3": "1.0.0", "CBridgeFacet": "1.0.0", "CBridgeFacetPacked": "1.0.3", "CelerIMFacetMutable": "2.0.0", @@ -1021,11 +1056,14 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", "AcrossFacet": "2.0.0", "AcrossFacetPacked": "1.0.0", + "AcrossFacetV3": "1.0.0", + "AcrossFacetPackedV3": "1.0.0", + "ReceiverAcrossV3": "1.0.0", "CBridgeFacet": "1.0.0", "CBridgeFacetPacked": "1.0.3", - "CelerIMFacetImmutable": "2.0.0", "SymbiosisFacet": "1.0.0" } } @@ -1053,6 +1091,7 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", "CBridgeFacet": "1.0.0", "RelayerCelerIM": "2.0.0", "CelerIMFacetMutable": "2.0.0", @@ -1081,9 +1120,9 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", "CBridgeFacet": "1.0.0", "RelayerCelerIM": "2.0.0", - "CelerIMFacetImmutable": "2.0.0", "HopFacet": "2.0.0", "HopFacetPacked": "1.0.6", "SymbiosisFacet": "1.0.0" @@ -1113,6 +1152,7 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", "AmarokFacet": "3.0.0", "CBridgeFacet": "1.0.0", "CBridgeFacetPacked": "1.0.3", @@ -1145,6 +1185,7 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", "AmarokFacet": "3.0.0", "CBridgeFacet": "1.0.0", "CBridgeFacetPacked": "1.0.3", @@ -1181,14 +1222,17 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", "AcrossFacet": "2.0.0", "AcrossFacetPacked": "1.0.0", + "AcrossFacetV3": "1.0.0", + "AcrossFacetPackedV3": "1.0.0", + "ReceiverAcrossV3": "1.0.0", "AllBridgeFacet": "2.0.0", "AmarokFacet": "3.0.0", "CBridgeFacet": "1.0.0", "CBridgeFacetPacked": "1.0.3", "CelerCircleBridgeFacet": "1.0.1", - "CircleBridgeFacet": "1.0.0", "HopFacet": "2.0.0", "HopFacetPacked": "1.0.6", "HopFacetOptimized": "2.0.0", @@ -1218,14 +1262,17 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", "AcrossFacet": "2.0.0", "AcrossFacetPacked": "1.0.0", + "AcrossFacetV3": "1.0.0", + "AcrossFacetPackedV3": "1.0.0", + "ReceiverAcrossV3": "1.0.0", "AllBridgeFacet": "2.0.0", "AmarokFacet": "3.0.0", "CBridgeFacet": "1.0.0", "CBridgeFacetPacked": "1.0.3", "CelerCircleBridgeFacet": "1.0.1", - "CircleBridgeFacet": "1.0.0", "HopFacet": "2.0.0", "HopFacetPacked": "1.0.6", "HopFacetOptimized": "2.0.0", @@ -1258,7 +1305,8 @@ "Receiver": "2.0.2", "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", - "LiFiDEXAggregator": "1.0.0" + "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0" }, "LiFiDiamondImmutable": { "DiamondCutFacet": "1.0.0", @@ -1280,7 +1328,8 @@ "Receiver": "2.0.2", "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", - "LiFiDEXAggregator": "1.0.0" + "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0" } } }, @@ -1307,6 +1356,7 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", "AmarokFacet": "3.0.0", "StargateFacet": "2.2.0", "StargateFacetV2": "1.0.1", @@ -1334,6 +1384,7 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", "AmarokFacet": "3.0.0", "StargateFacet": "2.2.0", "StargateFacetV2": "1.0.1", @@ -1365,8 +1416,12 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", "AcrossFacet": "2.0.0", "AcrossFacetPacked": "1.0.0", + "AcrossFacetV3": "1.0.0", + "AcrossFacetPackedV3": "1.0.0", + "ReceiverAcrossV3": "1.0.0", "CBridgeFacet": "1.0.0", "CBridgeFacetPacked": "1.0.3", "StargateFacet": "2.2.0", @@ -1395,8 +1450,12 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", "AcrossFacet": "2.0.0", "AcrossFacetPacked": "1.0.0", + "AcrossFacetV3": "1.0.0", + "AcrossFacetPackedV3": "1.0.0", + "ReceiverAcrossV3": "1.0.0", "CBridgeFacet": "1.0.0", "CBridgeFacetPacked": "1.0.3", "StargateFacet": "2.2.0", @@ -1429,8 +1488,12 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", "AcrossFacet": "2.0.0", "AcrossFacetPacked": "1.0.0", + "AcrossFacetV3": "1.0.0", + "AcrossFacetPackedV3": "1.0.0", + "ReceiverAcrossV3": "1.0.0", "AmarokFacet": "3.0.0", "SymbiosisFacet": "1.0.0" }, @@ -1455,8 +1518,12 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", "AcrossFacet": "2.0.0", "AcrossFacetPacked": "1.0.0", + "AcrossFacetV3": "1.0.0", + "AcrossFacetPackedV3": "1.0.0", + "ReceiverAcrossV3": "1.0.0", "AmarokFacet": "3.0.0", "SymbiosisFacet": "1.0.0" } @@ -1485,6 +1552,7 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", "SquidFacet": "1.0.0", "StargateFacet": "2.2.0", "StargateFacetV2": "1.0.1", @@ -1512,6 +1580,7 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", "SquidFacet": "1.0.0", "StargateFacet": "2.2.0", "StargateFacetV2": "1.0.1", @@ -1543,6 +1612,7 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", "AllBridgeFacet": "2.0.0", "SquidFacet": "1.0.0" }, @@ -1567,6 +1637,7 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", "AllBridgeFacet": "2.0.0", "SquidFacet": "1.0.0" } @@ -1595,8 +1666,12 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", "AcrossFacet": "2.0.0", "AcrossFacetPacked": "1.0.0", + "AcrossFacetV3": "1.0.0", + "AcrossFacetPackedV3": "1.0.0", + "ReceiverAcrossV3": "1.0.0", "CBridgeFacet": "1.0.0", "CBridgeFacetPacked": "1.0.3", "SquidFacet": "1.0.0", @@ -1623,8 +1698,12 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", "AcrossFacet": "2.0.0", "AcrossFacetPacked": "1.0.0", + "AcrossFacetV3": "1.0.0", + "AcrossFacetPackedV3": "1.0.0", + "ReceiverAcrossV3": "1.0.0", "CBridgeFacet": "1.0.0", "CBridgeFacetPacked": "1.0.3", "SquidFacet": "1.0.0", @@ -1632,7 +1711,8 @@ } } }, - "rootstock": { + "rootstock": {}, + "sei": { "production": { "LiFiDiamond": { "DiamondCutFacet": "1.0.0", @@ -1655,6 +1735,10 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", + "SquidFacet": "1.0.0", + "StargateFacetV2": "1.0.1", + "ReceiverStargateV2": "1.0.0", "SymbiosisFacet": "1.0.0" }, "LiFiDiamondImmutable": { @@ -1678,11 +1762,15 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", + "SquidFacet": "1.0.0", + "StargateFacetV2": "1.0.1", + "ReceiverStargateV2": "1.0.0", "SymbiosisFacet": "1.0.0" } } }, - "sei": { + "fraxtal": { "production": { "LiFiDiamond": { "DiamondCutFacet": "1.0.0", @@ -1705,9 +1793,8 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", "SquidFacet": "1.0.0", - "StargateFacetV2": "1.0.1", - "ReceiverStargateV2": "1.0.0", "SymbiosisFacet": "1.0.0" }, "LiFiDiamondImmutable": { @@ -1731,14 +1818,13 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", "SquidFacet": "1.0.0", - "StargateFacetV2": "1.0.1", - "ReceiverStargateV2": "1.0.0", "SymbiosisFacet": "1.0.0" } } }, - "fraxtal": { + "taiko": { "production": { "LiFiDiamond": { "DiamondCutFacet": "1.0.0", @@ -1761,7 +1847,9 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", - "SquidFacet": "1.0.0", + "Permit2Proxy": "1.0.0", + "StargateFacetV2": "1.0.1", + "ReceiverStargateV2": "1.0.0", "SymbiosisFacet": "1.0.0" }, "LiFiDiamondImmutable": { @@ -1785,12 +1873,14 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", - "SquidFacet": "1.0.0", + "Permit2Proxy": "1.0.0", + "StargateFacetV2": "1.0.1", + "ReceiverStargateV2": "1.0.0", "SymbiosisFacet": "1.0.0" } } }, - "taiko": { + "gravity": { "production": { "LiFiDiamond": { "DiamondCutFacet": "1.0.0", @@ -1813,6 +1903,10 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", + "CBridgeFacet": "1.0.0", + "CBridgeFacetPacked": "1.0.3", + "RelayerCelerIM": "2.0.0", "StargateFacetV2": "1.0.1", "ReceiverStargateV2": "1.0.0", "SymbiosisFacet": "1.0.0" @@ -1838,13 +1932,17 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", + "CBridgeFacet": "1.0.0", + "CBridgeFacetPacked": "1.0.3", + "RelayerCelerIM": "2.0.0", "StargateFacetV2": "1.0.1", "ReceiverStargateV2": "1.0.0", "SymbiosisFacet": "1.0.0" } } }, - "gravity": { + "immutablezkevm": { "production": { "LiFiDiamond": { "DiamondCutFacet": "1.0.0", @@ -1867,8 +1965,8 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", - "StargateFacetV2": "1.0.1", - "ReceiverStargateV2": "1.0.0" + "Permit2Proxy": "1.0.0", + "SquidFacet": "1.0.0" }, "LiFiDiamondImmutable": { "DiamondCutFacet": "1.0.0", @@ -1891,12 +1989,12 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", - "StargateFacetV2": "1.0.1", - "ReceiverStargateV2": "1.0.0" + "Permit2Proxy": "1.0.0", + "SquidFacet": "1.0.0" } } }, - "immutablezkevm": { + "bsc-testnet": { "production": { "LiFiDiamond": { "DiamondCutFacet": "1.0.0", @@ -1919,7 +2017,9 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", - "SquidFacet": "1.0.0" + "Permit2Proxy": "1.0.0", + "RelayerCelerIM": "2.0.0", + "CelerIMFacetMutable": "2.0.0" }, "LiFiDiamondImmutable": { "DiamondCutFacet": "1.0.0", @@ -1942,11 +2042,12 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", - "SquidFacet": "1.0.0" + "Permit2Proxy": "1.0.0", + "RelayerCelerIM": "2.0.0" } } }, - "bsc-testnet": { + "sepolia": { "production": { "LiFiDiamond": { "DiamondCutFacet": "1.0.0", @@ -1969,8 +2070,7 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", - "RelayerCelerIM": "2.0.0", - "CelerIMFacetMutable": "2.0.0" + "Permit2Proxy": "1.0.0" }, "LiFiDiamondImmutable": { "DiamondCutFacet": "1.0.0", @@ -1993,12 +2093,11 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", - "RelayerCelerIM": "2.0.0", - "CelerIMFacetImmutable": "2.0.0" + "Permit2Proxy": "1.0.0" } } }, - "sepolia": { + "mumbai": { "production": { "LiFiDiamond": { "DiamondCutFacet": "1.0.0", @@ -2020,7 +2119,10 @@ "Receiver": "2.0.2", "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", - "LiFiDEXAggregator": "1.0.0" + "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", + "RelayerCelerIM": "2.0.0", + "CelerIMFacetMutable": "2.0.0" }, "LiFiDiamondImmutable": { "DiamondCutFacet": "1.0.0", @@ -2042,11 +2144,13 @@ "Receiver": "2.0.2", "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", - "LiFiDEXAggregator": "1.0.0" + "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", + "RelayerCelerIM": "2.0.0" } } }, - "mumbai": { + "lineatest": { "production": { "LiFiDiamond": { "DiamondCutFacet": "1.0.0", @@ -2069,6 +2173,7 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", "RelayerCelerIM": "2.0.0", "CelerIMFacetMutable": "2.0.0" }, @@ -2093,12 +2198,12 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", - "RelayerCelerIM": "2.0.0", - "CelerIMFacetImmutable": "2.0.0" + "Permit2Proxy": "1.0.0", + "RelayerCelerIM": "2.0.0" } } }, - "lineatest": { + "zksync-testnet": { "production": { "LiFiDiamond": { "DiamondCutFacet": "1.0.0", @@ -2121,8 +2226,7 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", - "RelayerCelerIM": "2.0.0", - "CelerIMFacetMutable": "2.0.0" + "Permit2Proxy": "1.0.0" }, "LiFiDiamondImmutable": { "DiamondCutFacet": "1.0.0", @@ -2145,12 +2249,11 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", - "RelayerCelerIM": "2.0.0", - "CelerIMFacetImmutable": "2.0.0" + "Permit2Proxy": "1.0.0" } } }, - "zksync-testnet": { + "kaia": { "production": { "LiFiDiamond": { "DiamondCutFacet": "1.0.0", @@ -2172,7 +2275,14 @@ "Receiver": "2.0.2", "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", - "LiFiDEXAggregator": "1.0.0" + "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", + "CBridgeFacet": "1.0.0", + "CBridgeFacetPacked": "1.0.3", + "RelayerCelerIM": "2.0.0", + "CelerIMFacetMutable": "2.0.0", + "StargateFacetV2": "1.0.1", + "ReceiverStargateV2": "1.0.0" }, "LiFiDiamondImmutable": { "DiamondCutFacet": "1.0.0", @@ -2194,11 +2304,17 @@ "Receiver": "2.0.2", "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", - "LiFiDEXAggregator": "1.0.0" + "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", + "CBridgeFacet": "1.0.0", + "CBridgeFacetPacked": "1.0.3", + "RelayerCelerIM": "2.0.0", + "StargateFacetV2": "1.0.1", + "ReceiverStargateV2": "1.0.0" } } }, - "kaia": { + "xlayer": { "production": { "LiFiDiamond": { "DiamondCutFacet": "1.0.0", @@ -2221,12 +2337,9 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", "CBridgeFacet": "1.0.0", - "CBridgeFacetPacked": "1.0.3", - "RelayerCelerIM": "2.0.0", - "CelerIMFacetMutable": "2.0.0", - "StargateFacetV2": "1.0.1", - "ReceiverStargateV2": "1.0.0" + "CBridgeFacetPacked": "1.0.3" }, "LiFiDiamondImmutable": { "DiamondCutFacet": "1.0.0", @@ -2249,16 +2362,13 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", "CBridgeFacet": "1.0.0", - "CBridgeFacetPacked": "1.0.3", - "RelayerCelerIM": "2.0.0", - "CelerIMFacetImmutable": "2.0.0", - "StargateFacetV2": "1.0.1", - "ReceiverStargateV2": "1.0.0" + "CBridgeFacetPacked": "1.0.3" } } }, - "xlayer": { + "beratest": { "production": { "LiFiDiamond": { "DiamondCutFacet": "1.0.0", @@ -2279,10 +2389,8 @@ "FeeCollector": "1.0.0", "Receiver": "2.0.2", "LiFuelFeeCollector": "1.0.1", - "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", - "CBridgeFacet": "1.0.0", - "CBridgeFacetPacked": "1.0.3" + "Permit2Proxy": "1.0.0" }, "LiFiDiamondImmutable": { "DiamondCutFacet": "1.0.0", @@ -2303,14 +2411,12 @@ "FeeCollector": "1.0.0", "Receiver": "2.0.2", "LiFuelFeeCollector": "1.0.1", - "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", - "CBridgeFacet": "1.0.0", - "CBridgeFacetPacked": "1.0.3" + "Permit2Proxy": "1.0.0" } } }, - "beratest": { + "rootstocks": { "production": { "LiFiDiamond": { "DiamondCutFacet": "1.0.0", @@ -2331,7 +2437,10 @@ "FeeCollector": "1.0.0", "Receiver": "2.0.2", "LiFuelFeeCollector": "1.0.1", - "LiFiDEXAggregator": "1.0.0" + "TokenWrapper": "1.0.0", + "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", + "SymbiosisFacet": "1.0.0" }, "LiFiDiamondImmutable": { "DiamondCutFacet": "1.0.0", @@ -2352,7 +2461,10 @@ "FeeCollector": "1.0.0", "Receiver": "2.0.2", "LiFuelFeeCollector": "1.0.1", - "LiFiDEXAggregator": "1.0.0" + "TokenWrapper": "1.0.0", + "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", + "SymbiosisFacet": "1.0.0" } } } From d95e58fb6b9ad5099d01e85baa21a0b96e8ff5e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Mon, 14 Oct 2024 06:55:19 +0700 Subject: [PATCH 47/69] deploy script fixed --- script/deploy/facets/DeployPermit2Proxy.s.sol | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/script/deploy/facets/DeployPermit2Proxy.s.sol b/script/deploy/facets/DeployPermit2Proxy.s.sol index 72183428a..d1553cd3e 100644 --- a/script/deploy/facets/DeployPermit2Proxy.s.sol +++ b/script/deploy/facets/DeployPermit2Proxy.s.sol @@ -20,6 +20,7 @@ contract DeployScript is DeployScriptBase { } function getConstructorArgs() internal override returns (bytes memory) { + // get the address of the LiFiDiamond for the given network string memory deployments = string.concat( root, "/deployments/", @@ -32,20 +33,30 @@ contract DeployScript is DeployScriptBase { address diamond = deploymentsJSON.readAddress(".LiFiDiamond"); - // get path of permit2 config file + // get the Permit2 contract address for the given network string memory permit2ProxyConfig = string.concat( root, "/config/permit2Proxy.json" ); - // read file into json variable string memory permit2ProxyConfigJSON = vm.readFile(permit2ProxyConfig); - // extract Permit2 contract address for the given network address permit2Address = permit2ProxyConfigJSON.readAddress( string.concat(".", network) ); - return abi.encode(diamond, permit2Address); + // get the multisig SAFE address for the given network + string memory networksConfig = string.concat( + root, + "/config/networks.json" + ); + + string memory networksConfigJSON = vm.readFile(networksConfig); + + address safeAddress = networksConfigJSON.readAddress( + string.concat(".", network, ".safeAddress") + ); + + return abi.encode(diamond, permit2Address, safeAddress); } } From 959e1c91cd7e1367a1b5be18658e4817256ff3eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Mon, 14 Oct 2024 06:59:42 +0700 Subject: [PATCH 48/69] deployed to PROD (arb, opt, pol) --- deployments/_deployments_log_file.json | 40 ++++++++++++++++++++++++++ deployments/arbitrum.diamond.json | 3 +- deployments/arbitrum.json | 3 +- deployments/optimism.diamond.json | 3 +- deployments/optimism.json | 3 +- deployments/polygon.diamond.json | 3 +- deployments/polygon.json | 5 ++-- 7 files changed, 53 insertions(+), 7 deletions(-) diff --git a/deployments/_deployments_log_file.json b/deployments/_deployments_log_file.json index 7060470e8..064497cf5 100644 --- a/deployments/_deployments_log_file.json +++ b/deployments/_deployments_log_file.json @@ -23411,6 +23411,46 @@ "VERIFIED": "true" } ] + }, + "production": { + "1.0.0": [ + { + "ADDRESS": "0x5c6e208468213999CB0Be4bAB6d1492c3139A4EE", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-10-14 06:57:18", + "CONSTRUCTOR_ARGS": "0x0000000000000000000000001231deb6f5749ef6ce6943a275a1d3e7486f4eae000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba30000000000000000000000009e606d0d2bba344b911e2f4eab95d9235a83fe15", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "polygon": { + "production": { + "1.0.0": [ + { + "ADDRESS": "0x5c6e208468213999CB0Be4bAB6d1492c3139A4EE", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-10-14 06:55:51", + "CONSTRUCTOR_ARGS": "0x0000000000000000000000001231deb6f5749ef6ce6943a275a1d3e7486f4eae000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba30000000000000000000000008bcc385948c73736423d38cc567cfede0f1826a3", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "optimism": { + "production": { + "1.0.0": [ + { + "ADDRESS": "0x5c6e208468213999CB0Be4bAB6d1492c3139A4EE", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-10-14 06:58:45", + "CONSTRUCTOR_ARGS": "0x0000000000000000000000001231deb6f5749ef6ce6943a275a1d3e7486f4eae000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba3000000000000000000000000a8892ea3fddef2aa8afb1e3643a3284f978a5114", + "SALT": "", + "VERIFIED": "true" + } + ] } } } diff --git a/deployments/arbitrum.diamond.json b/deployments/arbitrum.diamond.json index eba3af0fc..d785cce15 100644 --- a/deployments/arbitrum.diamond.json +++ b/deployments/arbitrum.diamond.json @@ -137,7 +137,8 @@ "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d", "GasRebateDistributor": "", "LiFiDEXAggregator": "0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc", - "ReceiverStargateV2": "0x1493e7B8d4DfADe0a178dAD9335470337A3a219A" + "ReceiverStargateV2": "0x1493e7B8d4DfADe0a178dAD9335470337A3a219A", + "Permit2Proxy": "0x5c6e208468213999CB0Be4bAB6d1492c3139A4EE" } } } diff --git a/deployments/arbitrum.json b/deployments/arbitrum.json index 1efb31630..c91b4cb3f 100644 --- a/deployments/arbitrum.json +++ b/deployments/arbitrum.json @@ -47,5 +47,6 @@ "GenericSwapFacetV3": "0x31a9b1835864706Af10103b31Ea2b79bdb995F5F", "StargateFacetV2": "0x6e378C84e657C57b2a8d183CFf30ee5CC8989b61", "ReceiverStargateV2": "0x1493e7B8d4DfADe0a178dAD9335470337A3a219A", - "LiFiDEXAggregator": "0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc" + "LiFiDEXAggregator": "0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc", + "Permit2Proxy": "0x5c6e208468213999CB0Be4bAB6d1492c3139A4EE" } \ No newline at end of file diff --git a/deployments/optimism.diamond.json b/deployments/optimism.diamond.json index 9b30dc1ff..2c4486718 100644 --- a/deployments/optimism.diamond.json +++ b/deployments/optimism.diamond.json @@ -145,7 +145,8 @@ "ReceiverStargateV2": "0x1493e7B8d4DfADe0a178dAD9335470337A3a219A", "RelayerCelerIM": "0x6a8b11bF29C0546991DEcD6E0Db8cC7Fda22bA97", "ServiceFeeCollector": "0x894b3e1e30Be0727eb138d2cceb0A99d2Fc4C55D", - "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d" + "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d", + "Permit2Proxy": "0x5c6e208468213999CB0Be4bAB6d1492c3139A4EE" } } } diff --git a/deployments/optimism.json b/deployments/optimism.json index 7c1178f29..c48877672 100644 --- a/deployments/optimism.json +++ b/deployments/optimism.json @@ -46,5 +46,6 @@ "GenericSwapFacetV3": "0x31a9b1835864706Af10103b31Ea2b79bdb995F5F", "StargateFacetV2": "0x6e378C84e657C57b2a8d183CFf30ee5CC8989b61", "ReceiverStargateV2": "0x1493e7B8d4DfADe0a178dAD9335470337A3a219A", - "LiFiDEXAggregator": "0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc" + "LiFiDEXAggregator": "0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc", + "Permit2Proxy": "0x5c6e208468213999CB0Be4bAB6d1492c3139A4EE" } \ No newline at end of file diff --git a/deployments/polygon.diamond.json b/deployments/polygon.diamond.json index 819e958e0..725c54b6a 100644 --- a/deployments/polygon.diamond.json +++ b/deployments/polygon.diamond.json @@ -153,7 +153,8 @@ "ReceiverStargateV2": "0x1493e7B8d4DfADe0a178dAD9335470337A3a219A", "RelayerCelerIM": "0x6a8b11bF29C0546991DEcD6E0Db8cC7Fda22bA97", "ServiceFeeCollector": "0x894b3e1e30Be0727eb138d2cceb0A99d2Fc4C55D", - "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d" + "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d", + "Permit2Proxy": "0x5c6e208468213999CB0Be4bAB6d1492c3139A4EE" } } } diff --git a/deployments/polygon.json b/deployments/polygon.json index 3ccfa25ca..d763406a2 100644 --- a/deployments/polygon.json +++ b/deployments/polygon.json @@ -50,5 +50,6 @@ "GenericSwapFacetV3": "0x31a9b1835864706Af10103b31Ea2b79bdb995F5F", "StargateFacetV2": "0x6e378C84e657C57b2a8d183CFf30ee5CC8989b61", "ReceiverStargateV2": "0x1493e7B8d4DfADe0a178dAD9335470337A3a219A", - "LiFiDEXAggregator": "0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc" -} + "LiFiDEXAggregator": "0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc", + "Permit2Proxy": "0x5c6e208468213999CB0Be4bAB6d1492c3139A4EE" +} \ No newline at end of file From 0565895a4403877aa798d2cd7f066f9d36aec74b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Mon, 14 Oct 2024 07:06:44 +0700 Subject: [PATCH 49/69] fix test --- test/solidity/Periphery/Permit2Proxy.t.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/solidity/Periphery/Permit2Proxy.t.sol b/test/solidity/Periphery/Permit2Proxy.t.sol index bc6e55251..f10c2f0e3 100644 --- a/test/solidity/Periphery/Permit2Proxy.t.sol +++ b/test/solidity/Periphery/Permit2Proxy.t.sol @@ -216,7 +216,7 @@ contract Permit2ProxyTest is TestBase { ); // expect call to revert since signature is invalid - vm.expectRevert("EIP2612: invalid signature"); + vm.expectRevert("ECRecover: invalid signature 'v' value"); // call Permit2Proxy with signature permit2Proxy.callDiamondWithEIP2612Signature( From 6ab55d42168e4d58e2b1ffd24d60d7434a7a9ca6 Mon Sep 17 00:00:00 2001 From: Ed Zynda Date: Tue, 15 Oct 2024 16:29:32 +0300 Subject: [PATCH 50/69] Update Witness Type Hash to match convention --- src/Periphery/Permit2Proxy.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Periphery/Permit2Proxy.sol b/src/Periphery/Permit2Proxy.sol index 8adc21613..c3699ea89 100644 --- a/src/Periphery/Permit2Proxy.sol +++ b/src/Periphery/Permit2Proxy.sol @@ -22,7 +22,7 @@ contract Permit2Proxy is WithdrawablePeriphery { "LiFiCall witness)LiFiCall(address tokenReceiver,address diamondAddress,bytes32 diamondCalldataHash)TokenPermissions(address token,uint256 amount)"; bytes32 public constant WITNESS_TYPEHASH = keccak256( - "LiFiCall(address tokenReceiver,address diamondAddress,bytes32 diamondCalldataHash)" + "LiFiCall(address diamondAddress,bytes32 diamondCalldataHash)" ); bytes32 public immutable PERMIT_WITH_WITNESS_TYPEHASH; From 2d8537e3deb863ddc4885faf071d51d09918ec6f Mon Sep 17 00:00:00 2001 From: Ed Zynda Date: Wed, 16 Oct 2024 10:25:32 +0300 Subject: [PATCH 51/69] Deploy update to staging --- deployments/_deployments_log_file.json | 20 ++++++++++++++++---- deployments/arbitrum.diamond.staging.json | 3 ++- deployments/arbitrum.staging.json | 4 ++-- deployments/polygon.diamond.staging.json | 3 ++- deployments/polygon.staging.json | 3 ++- 5 files changed, 24 insertions(+), 9 deletions(-) diff --git a/deployments/_deployments_log_file.json b/deployments/_deployments_log_file.json index 064497cf5..6cc44f686 100644 --- a/deployments/_deployments_log_file.json +++ b/deployments/_deployments_log_file.json @@ -23403,11 +23403,11 @@ "staging": { "1.0.0": [ { - "ADDRESS": "0xA3C7a31a2A97b847D967e0B755921D084C46a742", + "ADDRESS": "0x67fD38A575702a476422b4578B480c5d797314Fa", "OPTIMIZER_RUNS": "1000000", - "TIMESTAMP": "2024-08-30 14:01:34", - "CONSTRUCTOR_ARGS": "0x000000000000000000000000d3b2b0ac0afdd0d166a495f5e9fca4ecc715a782000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba3", - "SALT": "09072024", + "TIMESTAMP": "2024-10-16 10:24:44", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000d3b2b0ac0afdd0d166a495f5e9fca4ecc715a782000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba30000000000000000000000009e606d0d2bba344b911e2f4eab95d9235a83fe15", + "SALT": "", "VERIFIED": "true" } ] @@ -23437,6 +23437,18 @@ "VERIFIED": "true" } ] + }, + "staging": { + "1.0.0": [ + { + "ADDRESS": "0x67fD38A575702a476422b4578B480c5d797314Fa", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-10-16 10:11:05", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000d3b2b0ac0afdd0d166a495f5e9fca4ecc715a782000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba30000000000000000000000008bcc385948c73736423d38cc567cfede0f1826a3", + "SALT": "", + "VERIFIED": "true" + } + ] } }, "optimism": { diff --git a/deployments/arbitrum.diamond.staging.json b/deployments/arbitrum.diamond.staging.json index 000ab479d..3db0ddb5b 100644 --- a/deployments/arbitrum.diamond.staging.json +++ b/deployments/arbitrum.diamond.staging.json @@ -153,7 +153,8 @@ "ReceiverStargateV2": "", "RelayerCelerIM": "0xa1Ed8783AC96385482092b82eb952153998e9b70", "ServiceFeeCollector": "", - "TokenWrapper": "0xF63b27AE2Dc887b88f82E2Cc597d07fBB2E78E70" + "TokenWrapper": "0xF63b27AE2Dc887b88f82E2Cc597d07fBB2E78E70", + "Permit2Proxy": "" } } } \ No newline at end of file diff --git a/deployments/arbitrum.staging.json b/deployments/arbitrum.staging.json index 3ed21d7b4..f98eb61d2 100644 --- a/deployments/arbitrum.staging.json +++ b/deployments/arbitrum.staging.json @@ -46,5 +46,5 @@ "LiFuelFeeCollector": "0x94EA56D8049e93E0308B9c7d1418Baf6A7C68280", "TokenWrapper": "0xF63b27AE2Dc887b88f82E2Cc597d07fBB2E78E70", "EmergencyPauseFacet": "0x17Bb203F42d8e404ac7E8dB6ff972B7E8473850b", - "Permit2Proxy": "0xA3C7a31a2A97b847D967e0B755921D084C46a742" -} + "Permit2Proxy": "0x67fD38A575702a476422b4578B480c5d797314Fa" +} \ No newline at end of file diff --git a/deployments/polygon.diamond.staging.json b/deployments/polygon.diamond.staging.json index 1325faed2..490ee5729 100644 --- a/deployments/polygon.diamond.staging.json +++ b/deployments/polygon.diamond.staging.json @@ -133,7 +133,8 @@ "ReceiverStargateV2": "", "RelayerCelerIM": "0xa1Ed8783AC96385482092b82eb952153998e9b70", "ServiceFeeCollector": "", - "TokenWrapper": "0xF63b27AE2Dc887b88f82E2Cc597d07fBB2E78E70" + "TokenWrapper": "0xF63b27AE2Dc887b88f82E2Cc597d07fBB2E78E70", + "Permit2Proxy": "" } } } \ No newline at end of file diff --git a/deployments/polygon.staging.json b/deployments/polygon.staging.json index a948eb0bf..50c042331 100644 --- a/deployments/polygon.staging.json +++ b/deployments/polygon.staging.json @@ -45,5 +45,6 @@ "CalldataVerificationFacet": "0x90B5b319cA20D9E466cB5b843952363C34d1b54E", "CelerCircleBridgeFacet": "0x371E073f6A09DCBEE1D2Ac56E940F878a0Ba9DAE", "HopFacetOptimized": "0xf82135385765f1324257ffF74489F16382EBBb8A", - "SymbiosisFacet": "0x21571D628B0bCBeb954D5933A604eCac35bAF2c7" + "SymbiosisFacet": "0x21571D628B0bCBeb954D5933A604eCac35bAF2c7", + "Permit2Proxy": "0x67fD38A575702a476422b4578B480c5d797314Fa" } \ No newline at end of file From 7c2ab14b60cba7221732bbf0a184d326a9d1902b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Fri, 18 Oct 2024 10:21:41 +0700 Subject: [PATCH 52/69] update audit report (incl fixed typehash issue) --- audit/reports/2024.10.03_Permit2Proxy.pdf | Bin 67118 -> 68563 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/audit/reports/2024.10.03_Permit2Proxy.pdf b/audit/reports/2024.10.03_Permit2Proxy.pdf index 0ba9ef6d1f77cd15e0684958e586f344fe134bdf..94e17b345a4959ac311bbfd8583069f3f8f0789f 100644 GIT binary patch delta 27557 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

u*lg~haW5u7GEzUkWGH%Xx0 z)Q%fh2Nu2`CsA;!a=1hD8Dqg0VZE;11!Ill8j3zF2zGf6<}9Yn&o#PVZb$)0V(yXj zb zm4{IrrRIc)0f#Q?Uj#BBytywB@XwBzW=S$UV9}Yi)h6G}Xh>k~$hAHzK(GB)$!(>U zH$ZP~rRk_1czoW??(Me!V!i&SNWUBG*JH$zV5Vo-u9%K9e2Jx(Sw)*sJ*5T1YY?}(r$$9(J2 z1xooJkwlJWo>1PB{vDl=*je5eW?)9-H_98yJ3{?q%*>O2w?X|bt|bJo>rm&Vs{h^i zH?qBb;FQzj)KO=)FNEl=_l2%!zuh0Q;@@5MMABQ$JLTIC91?Bbv^@<8Y;HK5W_i8q z-!i|4zDEPQ`T2i%yqCzGogcawiFq%9bv!(gKsO`82Z@00?wRj|%u@L`aAl1qnx+OF z_Wx-f@h;sA4k)L>>u)-3mjZ8&ZU2C3!%NI-6}8$ZMjkf5e|mP*bW(Z7i+cxjR;*=9 z&>1+LR#o$cPwOr_5(zcg%~DifT%eY6uY`u%;05Rzd%P~uMv2LmJRkSuw*zN&Mw4l; zQtJr>zWK}TN*JxRfLO==o~Imsyl>Wf_Iu}MjTcEB21%gG7JrDYE;w&>y01YPHcP|DPdo1)KDDfWqZ|~rSusI4>&6HPs5zw8 z)-c1t=s!hooR_If>v44iWlkB_kY8}VdNubITq^ZHJK{ZGr~_d;KsX2Es|hXP-c1Os zPkzV_(OhK*OEE!U?*iV8pGMDyrvmD4Pc|YVc{>gw1y)?Sb*{z}=S14z+~$@@4)WH{ z=w3RWOnlFXe>K-I≈DH&-Q0P9RX$r&yc_ zvscG^KOe(X8`Lsm0lsnCX+d~VYfd}A0gfWr0tHSQhwjXwxXi`8nsd1BGq?arnq0@K z7c~>mMBRitfl#H}FH1%uqYFaPc@{@a!EOwU7pcl>;nM1HQng)JK##FF?h&C0Fj*04 zy2Buj^ch`-=VNcaV|t`=Xd){S886HCfeo6ZO)FPqn%Le1T6t6czysr(9P6L+n!GxQ z_d#y)7^zr_?qjfVHN&U}e93$+Ba50oyU90W6ZbFYMb$hT8q3yLe@dc6p!wDpMVzvg zy7@Zwou(@USGhKhnrV!f*30kME6VFcmTn!xEr;|vA*bfIPYeOGCs*95Notp4waBB(1ck{?-$?bT}X;w!6P(x#t=K-f0kcOI$;7t*TA|QqOxm|bv=4zcE zG}6zzgi2;JlXva4zW%4UFB;|Trn=tZ$M=$dV&KiC>bG>Y$%FBocc_sbos|C$U%_nY znjfGk05H2DGOLEIuO&GLm>ro_hx{`;IVbslCe@vs+{rok`2Nx2)sb1{zF0Z^v%vpj zNs@mS(w zu_sy7aagv*9=Sg9Da1x7HM3npUZqr(lCCA0RYD+9wfd}2PqG-f+>hD%0T~v9o`TuW zz?2ec4)ddV7cZq3K9)9D*jxxXZ>-WR{@1OCU;eInw{Pz=e`DOTYMyKcq;ff2hehC&)M*$H@rc8^;pQwFdNkgSw;Uwex_h!Xe@@u#U1F?3WyiIT!zPfzCp_NVOQUg4{33GMP_sQxyp|BQLpg1|l zSW_SMz3kCZ4Uk?U(6V04QnQy11qdun7o%y+MmzSMR6V2}b?s1n9)x5Lbd^z`+%#XF zB=U+iW7HdC@R94z!UsMVAs)W692#RoV2Vm)Qp)-x=>KY`^fEFQxOVF{XldR@pMFu} zlv<3#`Ul?l;jU&(PAD&Bo~pY@jmsNR=Gj;x!BmSo(H4CWU?vr-`;#WC9EilVvVBr% zX^P>o`a)&bOQ&u8(SABAj??U7rRWxK&O^@~pz~#k@Bkt=fgyXnK+L;uKCBkQzkL$} zy^D>yell%lq45qqo9|v|?B==^U13~bCV0ooZ5^?SJZd=bG4r^xWV=ER1+PtOSJfGW zt1!vhBUIp}MEw4y)PC7u0NP&3+w$@9UulJVX+3}8bPo45tQxZPm{?J@TYq%+@za}4 zuQ{kThV%%S0mv_`@JTyP?A}tu-x<)GY9(HLOLYI~qepHa*G4Y84P@wKP`4ORj!rGj zK3CQd=bI{#B$B}=W>TeC(?^+ZftzNGlP|@cwXilK#al6fobQkGg6@a;2K+Ns=HJV6 zNA~jvBtBoYQy^P@v2b&DwKR47&*(p&H@LZ*y1IM2T3R9V@N#o-A=A)EsYoOL7ylOz AjQ{`u delta 26257 zcmYJZb8u(R(>5G?W81s2Z9CaG8{4)&v3+8DW82Qg8*Ge?ZTs2p?|!S^s`;a9>YSdL ztFP|r>FN12sG1lkG^+37k_;@290*ju7lxM+IC<*P(!c=`ObkdK7FJ-}m(z^Ns z(L_5%vGoAYp7e4>aZ@#SKY|&(1S%VWg;#58Fng&D)RH= z!tLh?bEgb$+!~{N)lFk9JQfQxzGqZ(C!$A_)06h_Embz!yY41jsQH4{;6xXRM6mePCZ zV3*Svi3_@7I{}zn;7h)fzJZN_Psop0z~eZS)<99-Us~B?}K&k=XiAPZP94ShZqxjWi_*L`mWzIDSNsR zUmx2PZ~6~BgN=#wU7LBKKR+!dsb;;d;1082=Hh2-$#z^v?xS^CS-XC9y;8P z2V{SWMxw-T-yDHLJmC+nJ?EMWxY*n?m9Q5?1DXO(Ur~)sG=I^m&h+RJ8@a>F1kY4S zpe-xokfB1~^Gb7nDR$z4f?}fKVIE0T!_-?3FUW;5RfB_~QO)(*04bh>mTDl{M!LrT}=1@GxUnn&ryX$}w$k6JP$A;6!W)uAev2yMEt|mqi z92?2Jj7YYiw$amC(b3W0?-mgJ$aW?K(wWll#$Y9d!XvY+TZIxOx6;Zl6OOqx#Dd*A zAgmuwGK@K_Eg~O#h^A8a_HPnW9Ge~a(>fKje+J_8G0Kb26k#X8gD(^pLbqQ z9@zq38pVwg64iK7lPQAL>xz-A%xO^_`n1qP`5yU^G-J1eNagWxX<_{Z_a*l49Ab`V zPFN=e8mht!ws=gva;u2E`;JI3xL2N(JBz-U2t_UvyRQUoKj#qGo8U_GXH&nFj5Zcy z;_ZNV-V$^j@?bamf2_scW)Yo>QI>DKN?HGUtxCszwIKNV^DgbEwq?Z%hwIwF@>BI7 zX!juvd6VBPPdLQ+$H=7$Jk)ikDTGl;knSKN{j_pe1AD;OC%$Z{jKH<2{Y(5TcIm89 z`#+v<(>Y?uv2^N%Axgd&>k#iVdzN=wiYkUXN;5mv!wKI8Is%BYZhDd^qavx?sq z9KCrn?e1PhS!AV(^k6IMeU!b2={eK_YsHhx08qA+)L z)InB&w08Uv?o@|ZV-9k|-IgQ^lgR_2;a=bxZA$8$6S!%xUJXD7(j;Z7O}=~4%RLL_ zYkI^reI53)y2SJBI230ll8Kr7Ro36dKA-#TZ|LAUJ0`aB{9OKCJacn=dA=wG%{?X@ zcSrzwLB8hZrPZ9Z6O+&k>$Y$++b>ajm$v2(inY3Y`$f_>|`yp@+r)$hl`y3t9<= zJTD#ybbNtvD!0eX-wyv>XI8JrSHCD5e)>)FLaCODEcF+MIrR{ZG&im*?CbAK*5N#} zUHMHz0vrIAO8VPjKPi>(gyg;?=qlYs6sp#9mvT<+TLCB9EY20D@wCEb1UiKni5Q~` zr^1mK&R~H71C2HVTvd?=?Ait8iQC>WJ1$aL@CF*^MPwjV#I4gAYjpy-E4q6|AU$VK zuh01l!&VVQCEd}s`&f^KaxMh?E(CnpD+8tG!}n!ZyZ6nsUul1!hm(c6LE0$c^9nVj z6tB|MUyL*5ixPIE$GqpYY#ZBtt}AV)%fxD~JC?tiGOC;?)h-T`Tso5S_7YLCYN&CjrhY~B+)ET9 zJWskz$*n7Ay~=zwkAUR9Je;G0{Yx60QccLVRJo7CW0gH_h0(|#0*vEc;j=jWwV$!m z3&W&6+EqKZc0G9*XQg0naTA{a22vxapOj4$hG z=lLy!9z7ttVRZ8dR;|Ot5y8Bb!NKHD@Hwvlj{n7YR?h!oJR~>Q|HbtwZ5@X-UUdJN zTIT7VU&2#2w90-q^FT|qJ69Hz0AkajNGh&Q8U3@gv#Lq`r(ZbESjTXr(hQ79K>CiE z^fVV6;WX-u1{=SRN7%+?R#syg&Ojbo9PZ=s*?~K??_Rhq_V+l-@J!Q(;oC#RAeINl zIewX2b&k*XgG+Zpkpd8^IbkkCnJ}4680ij7x02@Ra}dm2r);Ie`ES%v!TSwq^g>e7 z^Iz^~525mK_jx95kB5{~yA_HQ62JDA+`lZLq%C~CZ<=#2ad7tc)n6HL)-0IFB zzNtU?jS|xdBu~F>Ep?}%A137o)`~g0>7#iDH{;(4InN6!etkU59>#S*TlY1DQnZow z2ktwlW2GIpC`P}fn7DX+5tQJjoWi$X9Dgt}`iU|evE!aYBnU~oNm)C3i)uhR@IwrW z8}F2A3I_?1srt+T)Ldx{EvIMg%T_|?7Yx|PoXyyk2Zm>aF6BFDpYlfC*Ev}B{9Slt zJka&r=s|>B54r(!Lgt?nJz4w1O?H!^Idol9W&3Pj?X0(>pS+eiNcoc2FB|^@9~``@ zX9k>aKTOK~3_Ajq$%)MoWl@azdlmU8*anwd@4a^WKpRd1h&F0+cCpKYP9@9kyp)~% zOYb;b+{e!yz3TNLo0Qb*D}1cxPLdhuLVwu0RTWdvVt!pLaTWPba-E5FcFuv%0gZ|^ z9X_`l?J!fHwW|rZPJ?XPnjCNJxJ}xrqRHla&G1zsOFRpA*h}^D9%F1ZNavDqB)Un~ zLdn5ha;I~3P_BLJc==f^mjx&rt*EBB-h-SnBGD1sVY=rn&H}!ve=j1`+1l-`c34z{$Y{6ax)WBg3G=&c#rw z6mYOcV(x8f)>*b++2Ca_TukKi^_XxbiN2rEc`w-;fiMH8EI;v1x-%<1a(cv-BJcP3 zoBVt@etSHak%4WGum<*Qc)+=8f>*vqLFaRgcSp?GiZcL zAQ+>VqPoqaPdU6mcInQhPx&oxd(seZq@06^u`K-CP@SzS zrUVCMjsfVZPamjBz#?0VorC2j4{r1w37$Y>0rSZz&ka|ji6_Heyt_ccEme{*dJFh| z=rnTeL)4Rp035#@;|l8X?u#SkCH;m3RfHl#Y^gu!SSaYuhLmi392a(u$&MOCD)ui6 zdvChxrVUT;g-(VygvOKn9){JC&+H2Rh)`hu0UN%%I`R-{O1fYq)YV;Sn-qYGF}l=i z-}_*KrJf3pQ&bw)2iGIIvB#m z4g(iSP#c{d*#>hM1pBUzfeIyP%xxKV#NJ1_4t@=5I^)Kl<9S}rm5&(CtJ|T|!KuQl z;|L!SY_m0!ipiLuAJ|nkl&UapE)>yA!fL^3WYplw*k9-B$0?EIKb)?JX8jU(!`S!c zPu{l@x5OO4#+z6++iY$>{p*_vXcuNBuX^0BMG_R_Oi}7g^d~i8v4qC593|-2nDjn7 zlxm`t9!P0*Qi>4C57w~}Dr=i3qE}}r`9TZ=_s7Mfi0w{YiJ~T2ZKmQEm@cdt9E@T= zadEmBJ4z?vFwxrJd#{8^GEC0=LAdqX;i6+#6Q02?I?uWofy}u_cytJ5(2M6{GK#7e zbKK>ug(0cA-4abPCHKU+Ka4wUOj?U68Cy6cZk3@ZAFf+wIb!pe&qQx~Z?Mvf1s$3zdeEE!K6W^VVm5H7!I zRr;{FaS2o_wIA_R6@LYffYjyyyYiVVGHM@%F>b2LR`eCl<*cdn)B~&_D=TDlOGev8 z_x`>zu-8kOOuzyZ zhxdL`*!N{$FQ2|xmrIdK{)4XQzuqa>e6Z4KBPw{zQ3v(1C!{iKwmxW(?6e|}U1`UC zt|dFhje$k<9hdu3aDv~YG3*WM)U1rTzv5MmcMUQx%ibmj%Ggi)Jf=!6L11vTxc17v z6ny@D&X2C{5!YER0fjyJ@V&CT860H|%A63B%CGHrWwWn}A}d7ra28(51ycIS{5A?{ zotMl9jmU$yIO=)V;Tb&sxufx}v&SjNV*cuE<-a$Drkm(SQl(Xxs_mE-7?bMcOdz>! z_p1*KVPtRat2i169#$bFzK5j&@z_gGvqB}&3&1*s7xwyU0K)H|N^2^;yVcc}_)}s2 zF2sHxV7na_*ceu9o0q8khd0;qk;=0zfAz5Fs)=HHmAy2lWySS}CUK z>}*3Q)+8CTqJhdqYA)2Rc-y`PuY)?1H6?^>#hbh&3M;>wjT9&t*REZ0B zG26C1hw?4vgnw&ciBLA)&uY}bnto@vT|bkhp65n~G7z$l)XVx_=IH)57#}~ZjbEZ* zZwzgpjKO1Vu+^qDO3qwv#WK5g(NEq&o{e2@4Ry^#db*2SN5-Z;-T`@ZsdSlD^u=a@ zTv45+dDfyRn(Y2e8L|uYUn0J+;nm$xJ*J&Q*KVToCGhy171yN;BB`w-3R6sX`d~~md{d+@o^c^1JNsy#GXiwEaQ4h+S z;Qdk1(Dhfao4sEBG26Q;UFrkS7=jT0K{-ViCWZ_xlv=Tf@U@(?;;~kIAedi5`JW% zW9~j}dS}}cQ696}oWm5t37V~`zn7wQg`5wOG+V{aJJgljcGGzmJY;Vu0oi4>ESFZ> zy{(z+opZL6FjX52e0#41C7G|(&E9ypaTwGcO$2YT2-jdH7V6}wo1MnbXoHLm&FyT~L3}zxqNwG&D?u#A z#dm9~ZU~0$id`{|b>e7eQ3{>k1}ax;+|4eYMvoWG>nbf<{I#nephoJ_mBlHE(uA;% zTWD|LnFmk{c4^ott2AIMadf2%se)#Q;4VBimaA6n?J@?NHTF?3e&WiCGc}~CuVC^- zjMnEJ_=FroC5WRAgVb7ETQV2~*W%Swo{TW0hQla>+)hOT@K~{qg5lOl%YF%*SZg0j zac%G05AKSYxlQnz*>yb6x~o037tc0zrFe|fl>^uh`s2oly zQi(7i!yIf)=JAIU3R(hcl$>gDx4V|AK{K;W+lwLx!&0*CQ}Au@(}g;R)Rw+wS3yx? zp`)$&G!aDMc-kn8Kh~!7T3f#~G02vCFbVwV)kL~L+4^AkzQwMNYnIV%^KRVRe4R>o zg@oj#uDMq93YrP7Zy#Htj|%d|NTM78R6{V3nZK2gn(`C);l#UB&4EoJxPWbg_bt;6 z)W6HM)uca$<=AuPV1V^Ltcl5E2447wQTQ6OHnfsEh$uHorDHP>jUh3|B!MyJVj zxm5o)qc8YEQcrjv{^>w{EP3A!c_-qday0JnFuwfF$Nl-Wh;cb!T(mCGCth0B?&C(| zHc-L+M#%1BOEjJ3*+Mz)UPcH#gxQf&Xob2mx^r{~Ml0LU?{AwQ|_uJ6?i5VJhzkHZCC25ydQpi`aW~qq)J9sU<)l&bxbZK4~qbv7m@)$Ow0uPwK zfuWeQGg!Hx_)#KHC7j{hZST#*g3%jMl@vSKNzlar;hRM>-C(iO2u#;rCw14>vN~>i z6)f_%RmZ*ej$ey`#hv@Z`)NCbNo%x^XP7maOGs)O^O3iHB?6-AeW}7Bx)O)2%P_^T&w)lXF251dekGe z5{+SLwDq~)=Hi{$CelSgqt4jqOwswZ0ERaB)R58L`{E`|!9r*mP}8VNc@%>IS??k) zEIVL%gNcwEx;#Xg_{I(`DI!h;|5yU`0SEd^hs6gHgRcv+%wQh1Du+^b55z!fiZ>Ws z4t>4|DT5rp%^=(jVdo39d38rmwsf3)XNtie{K_R|5D%GQiK_xXY7HTKkU}?OBQKsU zt?PeFJ|jZ;bhQG1W#J+SQ!4^1?(1}g-ylGI6Q!rzqD%WPL1W|ot(re`;E9B9TnU=J z*~#y(b)=#eKIEL@f^PG~&Jp$JTr=5ASWwH?%>=c%gajJbPE8iAHp@Q+nlYsuI#W~ z`~o~kCVbIpz1Gb#m{1y0q@bKQK~eRjWzkeyWGaIdTA^io74pa{8}{uq4a|xpDp#r# zv0r%Y$Yj$*pm3{DC>xsBD*TU*hGst%rMGqAD9GS;O~!dvg|AIe=3u#;SD8JXZx57Z z;7uZm$$ma}hVxw=Pq;!F%T7#wSZZEm!RCF;f42L(-6omf#k8G-+N`C@Usbqy;L>s@+XIdoC*E+eaOF|P!)e0D=SAmAc!Imh89-R zanyB8A|YQ{lkBsUiUThV5JCOVzZ_BQ4QG4TbnaRa?rg1MplSU={5pbh`jY3!FkV05 z8+l)cSmx^k?_X%P>XgW~dxsMt_;Sn$LD8@NdH9d7AptRJ^KLo8#tgR9RiN5Z2fFpo_R~2+ zwFNysGoQn8HFoQ)L6`}rW@V?s2dZ7E)zIJ$i`L;r_Z8L~z`A=`zFlYSJIMEg;gD*p z8)8x~$hQGvl4kp?3!;wmnu18Tf>#}Nq#Q!fO3$k41{nMjd+Dxp6bV*a^h$ec=h)&F zB`>o1G~s7;YOc4g0J;kAM$7aq;Tq^8{^4M9IiKRT>0d!w)^GgW6WfR?A7bAKiPTES zOPp}uQ97A_q^V)IwKUl&SV8Bs8En72sS&17S2g~KHSkr_fW(1h$#PK(ILrF=DtOiu zI%>ELRQ~{to8dPOlvbK%AlS1jO*yBWQtHjcl2zsX2IK)g?cR59lciV{&Y# zk#%9^>aGJu=xB^sJ+-_DUp~PYCCT%FL|Y8tnD`qq3j&k8nZ1RpB`GUA3-kXnm_PrO z34dYvFV*7aGr|8xKJ=%Ff$qxcARvWC;*(%fBz7$@wlf_|Dbwrh?b#|QOSDo!XP~2b z5MkT7x|;N;aWR)3gKCsVJV;jylc26PQemcI05WNUrr;ezoF#|8Q2WJbsgai7Z16x=eQn$j~W?6 z3M0p2r5jjI25siUQtvZYTF4^afj1gs!=kc8<*KG{^1z@oKzqXy{yhYf3vR~&gF@9W z40mK?wv+Maw#d9BOdb11+D;hI|{TCq!4})-!VVBev6d=e?jgpyx z>SPB-eX zIM!JjkuygjFNW@le}kqDf(S%plMK-H@kd1^q7HZ!rpdemLpjOG0o9*nnt^ga*^OsJ zK8=&%!={3}Hw>@a;hR}Xt9);7U&)4eI3TSL7OKi<%AP4>@;{<7vop`ZE9R2vm6jHh z`~(e(s}$Fx&s0H=gRB#NH{qT5sf_yVXx}g)Us)@^g@+v2%w4j_ZcwM2vGMj9Tsl@5 zWvweUw@JHver?|@_BXjp_Rn6X&qa3FxLK-hq|~Ph82x!SkNx6o+rB1bt2)yA{WgIc z>2#K@{B~>7FQ@E6rSVJY=mwZBMxR8cMXFkJ_gjNpVfC?Y7AWgId)Zf}=er!Vl4xO$ zk6lm;4P#QDPTseL!k_NZKbd8#p_;rLZ4-?!*;BuczS6I->eBaSoQRm zQdld)?<+1E6@*TIKBpi#lIL1u9<&A6xQ(f(c-^`mGA7l0Mm?YtRInN1tUq#X4bQuuC>!RB z{+(q0u;uR=BwC@)ujFugu`<~5!*d{SMS2~Al@2-2l4v!fd^b&jXq{xMEx zveMCudt(5RD7*+wlPt&f1r#wDspa_?*7X6)EUBhgmtR#sZVf(1KGNs?XMBzb*d{gR zj}P^V?G-8*mX?PWrI`Yf5-o9-ue{VFP3s;0?1K`_(xRWQc$t>Ji2Hc-H^e`S^`nPt zWEA-`vbCHrCGha&O&$Do5tBj=8SHdx#hwUv)3gSqDiVw<)^s+b8pR5wyR{ymFX|3t z%4xYo{`s5vLzm5mS|9#rnqDo8gx9+f>+9--e>Rikb0Ma<@3jB)riIYPubUuB5>bk!1kv*}2}8ZblnUD6as5O&hA~?-K5u zI38F~>w+^Af}b}g{x{6;%fC2+hViCi7kre{=L@ca_*9AYon*qf)^QQ#lPXbHS=b*h zzCx3Ohb$QVp0~;j(QXn%lE@*9id|liD2>^3_58{|ddZwH-x+aVI+-@-RYO$+Ir8We zAs_%39i%#c%`|E6c+URl3_5W9*$TfN)Fnm%^5cP^YX1ToyQ6qqrtD(F&`9g3aCm}F z05VGdgT|w95i;kl=BP3y{kLE;ZkPiO*7r(tON8U(t^y5)hCX>DNEU3$IPtg?|Ne`- z*>5^`A74awxG18j)@TZG5U!g zK!kqYa{Z#5sZ0%)H0CgPOi}g~vu-mY$9U?EckEF<$?izPiWimu6Ws=ZkRZ_HH%J{zHX;<{q?xwU%OwUY@<2Br>%&fRQ zJXSv5`MBs{K28W(tT^9K6u0S3Ut+(1fSBI4uX`O%B_ncL5jf@qbV4-&UIht>Bk?G9 zTHmi#)Z#;lr{Yl%Z4??sYW zz5!XNJrg4;<5&EJ$5cnkWC{*u@Ce<>dXE(+Zhx{WT5F^HLN{`UU!>p-Y`obFKpm-H zv;x-A3WRd5t|@&wP0+Ywq>7e3;A*~*o|kpZk3z1dMeDb_7M-c9nX7+SWk}2?ZnQef zC*PBg2;s(!)RT-V6Ntknz4pmu$Z?_aJ`B?h zA(Y9NeN)M_dFe2vz-~FN`)oy%Yp>^^5KBtmCXES4g}d)o=EUVe(rTzCK`|?;!?^6_uDNQ>gIG7QX#t;_i{t(5OjFN#S9otE46PH|X9g&Ipu2uAVOlaAsYj zlDx+7?qNI&ZHkqE&zvDIiR(kLZ9A@c+Ox4UD2s{scx$8hJ9FP4g~9Ys(9_?^YZgi> zXPiYzXc+-N&oJDm2r3B)>2{_!z0$DpTyzy&>Bjn>NZ;yuMF!~3SJW$lACf;$N!X-@pb-_{!`|`p9?T^U;(rO6TrVmC;&mQ7=nZIf4@Ch(*u2?aKNLW z0|W=x|F^~o1jqjxga9AmYhZ^H3y^WA1FA&kfqgD)AVu^D*yqLq=r!m7M-F&kL@XQF z;K2es#LIzMUMwJ0nH8{*_z3`fSb(m_|9$gIR2;~XvnMFS|Cet5bzVj0$h~l0Aq2?#vk9mfdixxQot|ep8&EX7I1FJ4z#Jn0q|0ojoPZP z-~f&^Cg7%44{*s~0etG~fS~MuNBIqi%V7a%ntOnf{6BoOJ^?ueEP(jO5}>0f3~*=< z0{Ti=z!#lyK;k+b7HEdwh z;2EG&$NsmMegIno3(!`j2QH0PfmaQz#!cfd;J}?GHUMt=2*CZoYW!k``ETL}Hb59j z2N;_#0abcffTG14P@<0s=vzJkV+L4&jnxpaY={YnS?2?_Mwq~(buln*j0x1*+yiGO zSpQa}1NLlZfM-+e#xXlAaDdz#3)qUG14vzv09uDG2#J@|xe^lCXoC0S_ zV#jJ6a3Tf=cr37hd*>!V))EWoaVY~dtp3@@wH08p#snnYE`VL@|48uxWZC?O!U0fh ziv>IX`spp6Hxb} z2WtHmfxpg}jT!zV;6S+x7GS7A2Rwm(1Lm$+fPp0)02eq3u)6(66FLAhXcS;~#|Ey0 z-T)~N%tr7K7;r$;6AK^=T?9nDFdJdQc)`m|Wmj05~N5llnfHDq%2`IK$}ekpR)p4|MwsA*{_hc$jXb#HD0QCIXT6OOl%#RF(~GGZj9|PBZbaUa zlBFODVL?H`fQ&;SyZ}mEYrA8xd@sGWz;n_#kePQ%0yu~-AjcpY<{hij+K3jq!Tq^X z4=g7oI+{X=uCgeU<_XIrP`i#hKu_z~05QcGOh)+Z6Fs2tje||b#-i@oMm4wUlZQ(O zHlj0l3Dl3hh@9W61JTagV9le&*B-P_`RQ;)7jok;iGBw0*Vx2j*Te)iAP3A4#f5IM zSH;?!Hwg4e9SG_#MJ6R-l8T_{8(2U_c&Kjy>lOUom0xOa7!ch^w>*0KN%e7v3=Ibr zP&K6oW^OpKWb@SeLWk7yG^#(gFubw~nLjf!FaiTr_kRDnG=6n2DuN2jHvqlBRSP`!a^Q>*r7vmGPY=fx&#A0E4@BoKYO*0 zgj}RMS6ALQgf@imVY}Rsp}#?VoNP=lJogSLoL8Uof2Kix;*~*ugwK2m@j5e>x-T9W zBZE@iKk)$$wbfNfB$Z}&me!YWmXAOio`4Vk-M$LcvU`I|`T)FN&~GS;^W&@Qi{MD1 zJsp=02xnj~@yRO42dD|n!BoP7`+q>C)A=B9(Ck6Y^mOg+^J|d)%!iAE(YrP&?io0z z)DP`|bVR6?1!%YRKM`o{Y?lg-cTP4NZy+bCUGd0<;0>d|Q}vNn2CALO?gNF%WHq8@ z^qRSF&7klvkyJ@hJ5H_p7?-9ZBO~x5VZfKK{6^(CY6nuU?_u^Q+12sPjOaoxQ^nB;lZD;`bCU|NfSNj*MvH@4cxW--Zy_dJUiHRWm*J{qvM~5C-a` zT(K1lr!E@z;8NI{A3rVN%vs-2b2rYKWV(wka(SK#{gvOg@Xkpw$FRSBiGOJhsyk6b zJr6oJqJ2ve^Kl&G4SQ+enPd(R3;E=TGyU=fk_5yWAUCUTIQI5MdDT}RbjZHJjDJbq zm>xV<|D&02IPG4k7w`m)L4wc`AxxfD5 zv?6LM1|;U5EaA*t?qu$2Bp&>$SlfU8@;=z{uskUXrBLg4y<*y{l*U1L!yrghEVR(= z$vK{zEkgyW+@dmPMJtu=N_h993P|J3zo2fz8Qzx6OCN8p4LMjU4|K`M|G4V*mO=dz zk!b9n?fD+thI*Wb++yf9$`IStH-| zvbgHzIivLDBJat6c&#jFrI{0vNW9K8l#`kSim2yX?V_WbCppyQ>M!2gqRW<~Pb(p_ zrtFyh>UH}2F35m{sA7D&K|F#%vhUnrnJ?g&cxfb>3I;k zj5pNV1pJ3yFW*S%U38}_WU#A^axDIcBTZ~k+E*cKf8H8h6|`om7A1a7x>%GL{(ziH zbFBQqI8O1Me7ptaRo-@EuSdfYaRNjBX;{@}mc$=7ZW>coci%xNLSk47I}jb>PTV{&s8>60kfI5jVJaB(v;Y+l33h$i5!&4ffAn0&2p z9dB}eDFeSRmL;PME@Pv=$aG1ah_V`K^kIa&Zs1q4JV2~9aPHbRM$3GYXuaEJ1a`+WRqj5E_2~(ezsHT^yaUSLW!T8oO< z%Cv>#DiWa9GD)JOgSnKhKl{TruQXUF)=L8X632{dpt%Q6S~AMkyC=L1v`J!tN_xP9c&e-*4< zQS-hroc+EnDIym#os$;smesRD(YZ()V|BRL4lh=bQ6@94f62urKkB6IpB?CEiE^Fz z%WLDAy^}~?PSyQq!NYx7t(W~K5)56zqd05_(wXZa(13T@q|x%H5j|q*qv7?AD945# zM&3Dy;uKNl=vMP2ggiOdSJfp)MvWYSK^H2}`TeQEx0!zW@J0e+`8yVzlb-$`Tja&= z*wp8e65F|?eH}%D^SQ&{l4?cxF?RPOa`Zi8HY0c~3%@VT7#uxggUD#8u_eL;FEg(7 z4{CYAd|(!vW~x0@WPg$54oWB(@;o~5HGU5=b6}@V7acnT zx)q2*?o!Ziv{`JE^MPHYvJ;dkKBaI*UnMgo+I?2*S#+8QIO*tkWtbDg&$rKQ4f$lM zk$;c|>9o-sFF(SkUwA;zJe&*Y!+hxZN5f)!I6}s|T4=7$MB?NzWKg5>(q+IOipi`` zTl|Dzmvk+x6q>{&YDAF#gDYr7=>$yyvN=I@ik0?1>pu|_D}1pRV0|z?%Xi3ZZk8S} zyJ1Hu@@}j9qd8buZrw$K9ZVgGY{Sd@1wQ{bTkC7H`ScEX4!i8k4vV;xu zxzrOB)mbn;-%<2W+l*Q8!-t!800P`aL5s1QK-65JwP9~pL}uNot^O=CbZ^oQP+15_o`KyU`MzlZVkt}YIQ&u>On7Zh28Q2kZ;wMd z0P9c53V&c)cWIwE=>*Ch$|RPn0zCW3}ZdV2?1kq?ymX}kK) z%|O@O?RAHtt4r6hXxB>EOL~{66D&bg;DFXg;?WJEIXkGjF~Y2ZaXk18;N_s9% z(KF#Ez0y09N_oWPSzVwyd|o}#Rl)1oPPJe>UwNW0SuwHA75R5vF+2(uKBa7}g`$)} z=E<*FjM|^6kCpwbw5k)Nph}od9oRVF&k}D<&#kC_+&x7kBZDaZSyTAr?YAAQZrJ#> z(F+n!U8X6x6)^DQSE4-(N&3wz@&+sVrG~gX9P6IJSW!WahVcF=vgcb>NUS|%2^JQ? zw->oPsj*>8xA|RL_A$Fi{PZxbqt?qH&WpvSI8HR+b-~dXoGnitl)~8coMS_TZnJ&T z={G$j3B_tN;G+_&D?To70X5QCoug{`r#S2Sy4Cl!m$*hpxulA|MH^WKpH|7xADD)sYe7+vC^=K)v6_eDfYm&D1`537yNv0^QEe}(*5T*`XY@- z$D_s{ic%&xYs~^4A(BHUM^Mw#XBDkCeGuu&H$iCPPR7}`6nUYo0T}W($u1-U%J}m` zoQb-qd`8QApw89FcJ)NzBHS>GdwWwp?R`Hps6SsCgz!^67ahHA4q{+8+({ z3?mzF5wE_4`q5UTyUFxnf4?4=LQxnrNyuQ4xu4=< zg^Sn=(mpJ=AmeFa5cE686TQc4bfHm01ayE)yYU)_5B;At0SdfGdpy`8=-s*-Co zqUeIUc@-4bhqAU(+4z1W(9orFPV#gFIs9e%vUtq-iMl_-=k-WNW+SnMyXMmI2|>TY z5x-Gq1*A(|i<(uGx_;A+o}`Lg=~cr1eD!C?Vmj=fq6QWV zl)1;5llW9kj+~)gW1`b^zqvrd_uSB=o(uz`rM-7VK^?8({NM8~Tt2qka&J9Nv0WrN zb4l?h_#&)~!Dny3dK_p>;=|K4Euh+2Rns$#H$e=co~Q6*b0~js2vO_WN2fe4EbUOh z{536;h|iD>?z2xUA^G0~JpVo~1i9(L!yIWe%ekqq5yK3fNo*94UE9Lkiu-cOAuBpl z3#C=;gZ(RdRwTDU_sBP*$4Y9~K*g<+SN3rGFMYbh+JY0SaMRprBF{t*a9WmBD z>H^iax3IIwo=z?svW#I=Q^-g+jjMe6kGGIhen0kZ4BNcxqhIM<$ihpkjb!wpwwGN;GUkOw5wzN4EgdV z>a|{PkHBp{;o}HzuR}LOM?5rASL=A?f;ABbWd1%r&cHHX^!w{c>nXmm84?`2$VPBO z%!FrC8}oOHTT)y5cpH9gDijjxYG}B#_#)V7%~X+Ig=#XfA*BwD!6!D%V3anck^|7- zsLv!?Q$1|I%3p_B-<4fsPjmE%i&?M6&KVLUgEo;&oo3&mwI})!B$D09qqq&N`y_w7 zA!b{D-;5=-fuB20tVFjx4r@_w3>$sfv=mow9xoPTF z+Ak)OdfY#q$?()aGxOK+2z5>Si@lYC0Fw&95FxBqv8D-ZR zoebb8bYT*uS$NP0$>w;a7Z_G+?3ysHn*ZX;VsNcdA_aXfiR>i#(C%ox;l2w}F+|c2 zhu7nKnkSxo%YT6#09#3@kI{IX=bbmYG+-#pQHn(K?#hl6r{KvmG5wYns1i1f88M_a ze!ZJ5xSxKoJ6W7PK${WG4)d!ToYV9w>tne}K%;J4?`k-UjS(?~N<@h!|F8hN@3;o3 z)g9@CEd1>D_ihMZA(efxEZe^cBq8Uw%*O69q2Wk~Z;v_o4rotgDQ#SSZ@a>d)1+{|JP~OsTpEzR zwYy@2>CwSXw0OQ(Sg6S4P?tvkUTbH-_~Jw%oPo6^8ih3P2(Si76o|NZ?&YKt(jR{) z%tmccFUS(PrX}R+?kDAfinLen#de9Oe|k}p36Mu4IBe;~73?vdhY|TTXnq#!r2RtX zBlmof3JSPBJl^WR#MyeBn5^26&-Zm8NFQV3pl9CGI@iiB2g};l@(p9MJEtMJZ{9(o z)>R#;R3sxkq`l;9cRCit2OAtijzTF2*jTqP(!PeQNOhI$p%jJy2_X?tZXybUE@IOL zK(LV&n)nj$K?&RTqlAmY`hr9cD}4^c4`JCxgTg++N}$l{TH#sDdu=$UlEgx#RnheJ zEDD#$pXA`_a+Y#~lW7Nq7IVF=EwBZskz> zzEM_+B5VSyO~*e#nuJ`F62G>r8J4raqSey=Um;%^6o(UhNkVX!;O=gVyDV-2f&_Q} zu{Z?TB}i}AlCm2C-=ddl z>f)5?!_A&|Lxk>eoe-cHviT+n0qbw5-7v$G4{IQ?p-7_^#Z#i&U# zf8F>PRY``ZXo;QDBNrD!nR1bE=lfp2tuz5S-)3TaW$%?1ChUsBvoJ*JsxD^WGyLB! z@Kz%?q}-Hh>ZuG<_$Q*pq7fmJ4o&Il;oU%$&6wa+D(k^~KD`E}zMu->UJ2T%NKQPP zeR(B`R?t$kdEa8NtmE2Tn=SEGUZ%CBdpKP{U<&+lI(JC8AMiXRFIaneHj?czDp)>& zsxx#rE;Kxh>?Ya+N3xJdr$l~k6@ZIZ?3)UQ@fm{H6i6yTCnE8*!~*Fx=U(h{o}a=d z(AT(`4Rs)N`6X);!N#6-03wMb5-4{Fw<{b6Y&j6ug2cY7Vzb`NdebTH{qdniZ;5Nw zvz*s`S+KOG^6~xZ$K&^}XQK31W#m3hq(Y8%to>ub31(qGKnCteFOSwqcOE~`Lc@5B zww~C|UUo!3!(;P5&Cd`;jS80SscO@CL@D_hX{{B~MlGJIVaW0 zm>>~2m?^)_9sZc^AA#&zL#C0`T9UdB+leJmuksJs|9EU?bV(;HlAbv>j4b1q5l_=Vi*Q=aVeCymK<5)NOmxHhxwiQEy@<-W1#O`dKDBCp$sjIQY` znoO>W`^e<7U|_)vF#70`N&t8MMWcr(d>Wnir)19OPxp(iZ&Sf?uoDx$lJG`#Wbhsq z@?)V0Q0(cIW&BNB2&Be}1SzI;mzG8;`6ZbTr+&9uG6llc`CT&oW~f}twvhzfz4gof z&!|ZD=nZm3Az5@@_wafS@nhs@oLjsUC3qqsTqjX7b}X)H0qWUA@T%&%L-#1;9{cR0VMm>3!V ztID+3C+FV7J&b;Rksk&_u`1)+x&|Ft+lQmY=WTb&wY4buf$XB%LOPwq)u#ZLC4A6t z0-!^~`OgPWky~?q=RoQ;9K6jrQ4}pvyeo1^*e?)4dN3gdYVD?T_(3oqaIY5S+hDI= zF4rhUnL4slr0A+Op1fhh9A&1TjE+qaTTiSh5(oC30Cmq?N$DOpb81HX5$vTH*Qj## zOD<<^vAc}`_HDjkcc%6`9p)#qX{#>uk$}4hqOMCb|D07fIWv{oYkab1C{;W8n~f0r zB|aqS4O*ky&on*TWYjOTlY1|QZ%i-~>%1a2V>u#gBM^h~PkXa!zbYsF6uq8gtV{aE?ABvsc4;aNqg8-(9tHfiqY8hvUIzOuivGTX)!0FcBG~~N z6h?auftd++#|N=SCUpDKmHgd!Iq}~8QvzARJPg^!Dj$P%8ThB-0B#1uP11zlgi`E! zSZ%oG>+2p#ovBu6U>Dh$mV*@?qF60Wio|gxh8e5ln3_zJi$V=NdQ8Y8er$dph4C17 zQ(fl!ihi>p9R*;b{90GGqpN!#n*T*pD!_@r!w#jEeY74r2heJ9BCE1Ip~mq)rGfYl zm4^8bcv}Sszz=(TTV9wXIs2=bFf^+8#rvTgi<#Fk zw|dF5n6y!C+FaRV^FTN7QStYS<>+K$6j6AcIsfI zx^LNaq(cVd78~VW-MK{mDdGamUqJ%%*-SUMv^<3LFB!Fj2?r=g%D_@g+n|Bww>$r& zZ|6W6RXhAR;G^)rcb1cTDUwP{cI=m}#%(=u%X3qr_S7gNrCD0{9pqa-{XMG8d6Dn% zc#9**Lc44aD6CbU-f%%3`H&O21sd`I&Ayku4UmHn)H3nhmPt2ZI0{FvX*wQ15{g@S3h1tz?~- zVLWH6=Gr>NAFRw{B(75*cU(qPpMj()F?oQaPavUPxtGjibR#}UEcFCLa30Ue#`1I1 z=2xKA`)(d@7zt%C+!`_A3`9%8KTv$+zt%3#W9i+g${v`K5$<0){dnUe$EyQVrAcaM zHKlpE?V{N9k6U1j%9bm85iU>Iq*^MU5^n4|J6jd21DC-gCE(rWZ>k58Z&dnp&DP9XCdSHH_R>K9HflK8NqX0ap0|v?#i=)t{H3^UxB3@VzV0wV zy&!q^h!E$l>%hY;j>4&ZRJ9!s6EApAfhqqQ>!#(JfTadSDBYl1n{VF@AB9#VIsd66 z-=sgh0{Co|9Wo(F!_;Fy^t^!siEVeV)o)r980u8VW?Z~lBV-P{<2|94N2M+$p@8+B_#86k3XdV{@f1n&k^?5*yRa93se3lLB+ z-mUVbR1^j{w<7eY@$IY`_3Tk2s71EoR<0h@*yis-2!X*r(Mqkol$lfA(KFZoPrpYp z_eaIH6;e$O5-*v>hR+5;8e_>CvBN9DwG|jI)LZ z4Bo>UL#QX`oT-5B6?-SNfL~5djVQrql7+6R4>eEM43j5M+3Cdz<92?Du<1o07U*c6 z9vrjTQcp6-?S*HyOw!y&k|6uaH^Y5m!}66d)lEa+)BwD{9?8_Ul^zI-r~^&*1}F1! zrm9-ad)h7p-Ati4-Fv|%jpzNuC3nyFVrwjgKnshSnhC36praXRBd)A|TIX>IVsX|(2V3q^?$}|obNS`Xt zdh23>?Vpr`-S0Lw$Ahl^b*Wi427=7Y`f5QE%@Xx&)|5{;doEi8u~QktZ`0h37rNKQ zLvjm3kL5lXznsx3R0Tl0XLl#@mnRh z&qVR4=ShrQ>v1ll`-vZ}RI3E?*QhUqL6b`3p#6lHpnSX3~+XHJKPdOaI%)r$8(lDWs8r{P$efDJ{8Iq8p5t6Uw&?1XFA4^3DD2t}>%W}Dn zJxm+QJx$avdb1FXI&Tl`I&q7RpH6Ws${DL*+r^t)BhJ-vqV)=1rGD@VlH17QhZaW} zA%lY4%MSN%COsw&R!|5s$y=!rQ7i!@D=ke{`!tfZsmiHx?>9w6N3``^uvi7BAZ0w0 zT#kz*p{Y*>3xyTXjMx_<%V;m{k-)L+)AO8IwsLryP>UfzMLG;YOc9L&q7HV(_p_ zgn3xBL3|RoM6VN?e=Klx&O`H+O!4cNtE$<_#2#dUL$p7LWD;J!jLHPm3svaF$K;&C zc1Gz&6tcPz?9W-$d8@6_0?VR2g{HhXN|R?nRziK%G0*f`CS*O3eTB}8xk*#y*29hd zE01i7@wBFCaMA$~9P{zLZL@F>WYN}8B zwskOnqCBe;;6hrOjt?RS7a1M2m|Pt&IuUnQ%6i=+aw+}1kBi=QQfglDls}J->CO3) z8M zpW_A=nd9>i*P(~QExX#{RD_dC-af+anmH{Kk=N)?-6ndBp%N%mp&SoKOT(!sAf?K6 z-~(QN#tUTa!lF6ILJ`a5CIjzHjxqRk;LUaZ@O5jU&`Yum{qZ?MMCQ4BD8?xfpGo$d zN0B$aC0dkLgz+J_(x8#CVJ+whgXK?U5#7N*9UenxGh1Je>#C2*KPX-hEhgiP+DhZg^Gj* z9(lRzDKh>e<{#1PIh3ZU%aK0>9Ie6Rh~HM#&-AWGCrGj%J=w}IB#=wz_uyVbm8Pz*$!?Pk6km2(h^4gDn;*@%$@#772fn-((%L_FaeJS!_;SspPs_sl?b{!ZMY8?PWK9-Rvz?&SLFRW2<}S ziq2*fT5$ej1NW(BYcd5%|AEV6Q;k}S0Um@*MGy=kSmRtyl53`2t`YnMmZWe z&Cs6Q-9(?k->K^0S;N;bVJ6e1m6~!*Rl!!qz?ul!d%<8FDhC5ja!e^VBo-9C88rUS zFFE^>?zT^8Bi{y}tNH$vizxlM{h1UAX?iRFszP9A_TehPAvW(u<@Eh=R(Atsdcn}h zD=BNNGAdtr8k*jz!_WiBd0wOeaBFB9E&DL1KQu0L(Le5>Lm3L_adJ)?#9*Ob{Vzyl z_{4c+qmB;kukNxOV=!37RQ(po^J7bjRD2+PEt&kp-)i&Z-@g_k_nZo+U^kLMNCkjF zsGA07cU0qL!5d5aS>ME!dV_*_8CBj7gjV>|svD1>Yo4+4A#URY9YfAsy4-mi;tHd; zI6_(Dc>iklXw32r-~C%&yx=Dg0cQh;+0ohjJWy(vHN&Eva4EHkqUJqQnp#xMSyx~k z`1&e-_)eay{`iMMH>UDyW3Ugz(&Uh%$2IeqO#YxMNMwaA`MQ_==Ef1XaVbvx+=D(K9|U!d!H%k613hJ2ibm;owy^X$^1%R4s3SZLedV zZZP^vWY}R36{Cs^26-1g7eL_$+Ous_au*Mb^O8o#Nu&aS4qo-M3bVL-E20=fQd+W- z;0+>Wk*KCd3H)$AfOjqns|~FzE(xvO3+1U>VymF?~>C39)n zJfH<`dVDSX*2vtmh|)=A^nls2St&N3!O0r2d!o4=ksV}Ph6!QD9b_3wFABxV=&Y5v zYu}uZO75+?;V(kHkbge8}uF zHqI`t6x{s0|5x${0Pt}AkL2$^mz8>~w{oLjDd4K*sZTnsm?xfG)B{f2|D1%iw%R>! zetF2s+pBmCld@MmA3w9m`L*IRhNT~JoBg6mTv)a8kU05l^fk=YJx23<|u+eT^e{S zy(sFvRIp_F#?=%|oQ+@lya)ZX@=(|n=#?Dz0<(Mn`Z5^?rn=G;ykJZZf3Hja_P9K6x@%V)<7zVt6mutzaP%x?8*h7Jqw$PCZl>Zk(p^#^S?O^{I zm6^2{8aGu|+RN40xw`4}MVeaFHSZBS7q zT4bso7_E%pk92r-Z`#aC2J#YHt%_G7VGk_G#0`Z03zk28eQ(;$!g3zVAtiJ`RUYns zQ9;GXGHGO^C$p#$ltxtSW=zt5lH_dUc2@!Wrbk)6+VGADFW6lf8S^2~ zXpm7W?iHlv$&8uJ-E0)u*%L^Uiu3rVdDEl)0MkdK?szZnA)5|a6OFmEWAibW`1a)y z*(?yJFcU{`i=2MYvd&=QNV(ROe5xev=r5R!jcO%n%xebx)1WNqmj22uK@jHh-g3_rZHxCJ^l#P@JAy|<_?S6v=SG=tj;pyYKB$Y7vma@@){$+&dmrp)=IZlZ6EKe+J= zD(l9wy_MG~xed=4)uf1fm$Fq?+KLB`-pvu>b$tGe zrdu_L2dnx?x%FoyESz%_+{qW$Kk%P}h!tK;?ERGNO(h58vEvuAivw}XaE-W)LfC(^ zC$cYyF=i3gF(nhQ|6(64P5A);+~Mn{nbblou~z#o`>umhcmR3BGu#Blq0*S3ZEK)8 zLkL&BKQ)yiHJ`4pXSh!O90HZS9O)X((QMaV@2+%ML!78vrH??IVC((2VjSk0_U-IK zrO%!@|)3uZAfkiJ^?ZZ1V!7Pv>Ldf#TQWZIew1;mE@x zTc@69651v%7;H@XXI&57`(&>8(q|Gsv7DiG^bE;flED#hLJXu_c$pEDW?k;uAJE(G z1F{WC_UGn^n6t>S37WGATS)aox?OlacJil_;lW=!xZqN+IN)Nd1>o)`WYvusIWjoS zzR1~b6uR04Tf^sGVgv2uVn{TeaEu|p@wQv`G{l$?O zZEpz0k?ebKKK<)$VJJN+wp0bbXMwiyk#dpMYWHsUi-_Lcl*DR=+85#~Xp14v4pRIK z`@9p=E73}FkCZ9VOux+~nNWuUzfT9iWx7rW+-A6{02V52iJZ}S%Zp(}1XKKO(_AGx zWdoNmibGvF^XWuk#)=`KP_VNtl>#EBZ*Cw=H~346Ln<9>xQreI7+PsF8iu0p^1oOk!VWBU_e^Y}+2wPX04oXB$W z7J7l0;u**nv>a@@X_m!2x-Sr7cNV(Ol#+1^*KNCL1h$cSbQI&uZ8BGcjJB$9q@T?8 z6}vN>BKD+jmJc#tgfp#dd_Gutk7DWfhzF{*E{GVuchGUT7eJ4%;na3jGuvR%>+a}P zd*A5&P}v2r>M&lidpxsW(nms#H#aA<;8f)`{QOI!2d=%_R(A;-?s(Bsi(RjtoX5iL z*=<$d;NC!M@LVN^Q)Q>5UYEP3W_Kn$MU9rn|RpvuKvFmYlULC6;5LYv2S{ET(kdQv2YdAat4-wl$vZ!s1oxggT zIdyA`k9=u(kzm`2)E`dJXSMsu{PCjIq>hm9>N@QFd1TOAG^{w*trx4<(R!nSB$DqI z7G_`=^1`T;3%4`#pRL@#dp*8BcSL18L(1Ei)^_z#RN>0)qWcxBWX9)O{`cbX;OA?M z0S&F}E_axpx5unoVst=7%N;iIlAxhGK@G6rkCSOX%&+!V8iu+Zk@Z z2A$WeEsbNslNj_Sy7EdJ$yUgE=j|Hk8ca5lwq0OMp*E5eaScl8#h2+wdbN(hb)+-HO855e zWEwM8Zu_{yaRX=m1=@RyIggU_B(xA=S~7Q<_JG^*a|k)}YWQ4a6&^;HEk;#)S@{v# zKfgJ=!I#h%J;1=pX;cKzgN3fS$1$e`ISYE!?5#a_26wdlUrXoc$GB>xv{3<3R zJ}W0LYkQQdpBr85D(L4@L-#pZlWTw*QF`tyh6>YK`EeFdkn<$^fir&gFgyLo;BqJf z6XelLAFAfl!Lz)}ouWay>(kQ#6o3q-+yz)sX`ag}tA7kC#tC#Cp125=ix!9&EY7>n z&)N{jZNKpjtcq|zPf`I*`+AcxA;UE`7;d3W9t!4jCHKs^| z!s#_%hTdEa-W&h>cl&bVTqn94*zB;RT8JXd!mAo|7Nyr4_hcrrTrbyJn8tc_`!u8R zW|8kbzp^4pfWhX~U2RaYw_M~mv|6#MiZ_OFo7-eqwx@KWt@qg6WV0j^v7LkeT)$!( znPvS!RzQ7CV<28Y4vGV3U}Z!j0oKaQ1Nl9b$*Mh=KO^U8fjeoeRH1WKv_Jouq36@| zn7x}`4au_2;_MVH#2jgtEmGbx>2q9|fkLfKQm+YCgiyZT@nz zu4)>A-jV#W4c;eL*k!!oM0kU6;MS%me? zYWR%o$$OvlsDtGwF|4J13igoUjw1wFuymxow9-S*Aj1_dRZgtB;K;T%P+IldlX6rr zB*0b`%+XOivmxhWpj;&`;!U@158@@qB^%!jXK$Z8-1LVCKi$KP9$Vl<>NK0_aNC6~yWF&d``8fD^_yssbDgOT@s2>OY zkB18%Ag;=e2|_!&r~y?Y8Mt%-S0_$Nst?EVNT-pXq97+2C1Mv;r*T^-^RVJ}hn~Rq ze4Q~ejzGaL*LU1crXkjvNhtv0?93d}P5HV0LoLfqN@YUdWB~*i z)1A;Z#9QM5C19Iqfm4{Ukvr-krY0XuP(!jcdanddlQhtUGLx>x1I1=M;7ck?KWlah z;+E}eZW=+ zmJkw+v=|L5Ay?HedcSTk68z#|-sQD`dR&^~Ipi@EB+TGP#qf)30VXZ`bG;<(cL^T* z0L&AA)_=e^BUiK&KfwjG+ey^Af4!sEuXh~s;Iiv|V=*iY64?o-8xn+Zrwx*U(Z}r> zQ|0O8bH1TeF_4=(AS!g!JZqa(?IB>*xh%S^GA2rf1+BvziE9 z{cZT9l-dkUJzzMBq$P@vmr;qE9g8)^^c`Z@ zffXN~C#N6JgP?NM)g|F{@SES~JBfTC#@2zXlf8azf9Up5ZluKWgSoe`4;JakNT`-m zwQ@QUW}$a)ii4B;ch`3OeH}e~#Zxe=7dX_AEu~#f;OY$$l3AR*(Ps~bz zpi@c~-)LYtM^XvamTs8iS5q2>@+6ZGVjEL#-Ia89hpN(L;WihUl{Ut+y6|V!mv%=H zem1y)f(-_z0pE?S(w`TL9w~NXX<+g`JpfqY{Eg&7`DjHgBudg1H#ZNHRCBLLMwUm= l2rOX-h7#DpQeI;?H-9*fm!NziMd9V-5 Date: Mon, 21 Oct 2024 09:24:16 +0300 Subject: [PATCH 53/69] Fix stype string as well --- src/Periphery/Permit2Proxy.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Periphery/Permit2Proxy.sol b/src/Periphery/Permit2Proxy.sol index c3699ea89..ca82ae5bc 100644 --- a/src/Periphery/Permit2Proxy.sol +++ b/src/Periphery/Permit2Proxy.sol @@ -19,7 +19,7 @@ contract Permit2Proxy is WithdrawablePeriphery { ISignatureTransfer public immutable PERMIT2; string public constant WITNESS_TYPE_STRING = - "LiFiCall witness)LiFiCall(address tokenReceiver,address diamondAddress,bytes32 diamondCalldataHash)TokenPermissions(address token,uint256 amount)"; + "LiFiCall witness)LiFiCall(address diamondAddress,bytes32 diamondCalldataHash)TokenPermissions(address token,uint256 amount)"; bytes32 public constant WITNESS_TYPEHASH = keccak256( "LiFiCall(address diamondAddress,bytes32 diamondCalldataHash)" From 2da847b1a38fd30ce92574b6a26cfc14377a5013 Mon Sep 17 00:00:00 2001 From: Ed Zynda Date: Mon, 21 Oct 2024 09:39:20 +0300 Subject: [PATCH 54/69] Redeploy to staging --- deployments/_deployments_log_file.json | 8 ++++---- deployments/arbitrum.staging.json | 2 +- deployments/polygon.staging.json | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/deployments/_deployments_log_file.json b/deployments/_deployments_log_file.json index 6cc44f686..5d688a233 100644 --- a/deployments/_deployments_log_file.json +++ b/deployments/_deployments_log_file.json @@ -23403,9 +23403,9 @@ "staging": { "1.0.0": [ { - "ADDRESS": "0x67fD38A575702a476422b4578B480c5d797314Fa", + "ADDRESS": "0x6FC01BC9Ff6Cdab694Ec8Ca41B21a2F04C8c37E5", "OPTIMIZER_RUNS": "1000000", - "TIMESTAMP": "2024-10-16 10:24:44", + "TIMESTAMP": "2024-10-21 09:31:13", "CONSTRUCTOR_ARGS": "0x000000000000000000000000d3b2b0ac0afdd0d166a495f5e9fca4ecc715a782000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba30000000000000000000000009e606d0d2bba344b911e2f4eab95d9235a83fe15", "SALT": "", "VERIFIED": "true" @@ -23441,9 +23441,9 @@ "staging": { "1.0.0": [ { - "ADDRESS": "0x67fD38A575702a476422b4578B480c5d797314Fa", + "ADDRESS": "0x6FC01BC9Ff6Cdab694Ec8Ca41B21a2F04C8c37E5", "OPTIMIZER_RUNS": "1000000", - "TIMESTAMP": "2024-10-16 10:11:05", + "TIMESTAMP": "2024-10-21 09:32:45", "CONSTRUCTOR_ARGS": "0x000000000000000000000000d3b2b0ac0afdd0d166a495f5e9fca4ecc715a782000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba30000000000000000000000008bcc385948c73736423d38cc567cfede0f1826a3", "SALT": "", "VERIFIED": "true" diff --git a/deployments/arbitrum.staging.json b/deployments/arbitrum.staging.json index f98eb61d2..be78eaf5f 100644 --- a/deployments/arbitrum.staging.json +++ b/deployments/arbitrum.staging.json @@ -46,5 +46,5 @@ "LiFuelFeeCollector": "0x94EA56D8049e93E0308B9c7d1418Baf6A7C68280", "TokenWrapper": "0xF63b27AE2Dc887b88f82E2Cc597d07fBB2E78E70", "EmergencyPauseFacet": "0x17Bb203F42d8e404ac7E8dB6ff972B7E8473850b", - "Permit2Proxy": "0x67fD38A575702a476422b4578B480c5d797314Fa" + "Permit2Proxy": "0x6FC01BC9Ff6Cdab694Ec8Ca41B21a2F04C8c37E5" } \ No newline at end of file diff --git a/deployments/polygon.staging.json b/deployments/polygon.staging.json index 50c042331..a2138f135 100644 --- a/deployments/polygon.staging.json +++ b/deployments/polygon.staging.json @@ -46,5 +46,5 @@ "CelerCircleBridgeFacet": "0x371E073f6A09DCBEE1D2Ac56E940F878a0Ba9DAE", "HopFacetOptimized": "0xf82135385765f1324257ffF74489F16382EBBb8A", "SymbiosisFacet": "0x21571D628B0bCBeb954D5933A604eCac35bAF2c7", - "Permit2Proxy": "0x67fD38A575702a476422b4578B480c5d797314Fa" + "Permit2Proxy": "0x6FC01BC9Ff6Cdab694Ec8Ca41B21a2F04C8c37E5" } \ No newline at end of file From 426807eaeb3bd73dd6ec35289cad4b73d77cc803 Mon Sep 17 00:00:00 2001 From: Ed Zynda Date: Fri, 1 Nov 2024 12:37:09 +0300 Subject: [PATCH 55/69] Update Permit2Proxy.t.sol --- test/solidity/Periphery/Permit2Proxy.t.sol | 1 - 1 file changed, 1 deletion(-) diff --git a/test/solidity/Periphery/Permit2Proxy.t.sol b/test/solidity/Periphery/Permit2Proxy.t.sol index f10c2f0e3..d0a5c433f 100644 --- a/test/solidity/Periphery/Permit2Proxy.t.sol +++ b/test/solidity/Periphery/Permit2Proxy.t.sol @@ -5,7 +5,6 @@ import { TestBase, ILiFi, console, ERC20 } from "../utils/TestBase.sol"; import { Permit2Proxy } from "lifi/Periphery/Permit2Proxy.sol"; import { ISignatureTransfer } from "permit2/interfaces/ISignatureTransfer.sol"; import { PermitHash } from "permit2/libraries/PermitHash.sol"; -import { ERC20 } from "../utils/TestBase.sol"; import { PolygonBridgeFacet } from "lifi/Facets/PolygonBridgeFacet.sol"; import { ERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol"; From 6b9c6429bae1b9a33ef94c5bf9d34f84940e6ddb Mon Sep 17 00:00:00 2001 From: Max Klenk Date: Mon, 4 Nov 2024 11:49:16 +0100 Subject: [PATCH 56/69] add demo for permit2 flow without witness --- package.json | 11 ++- script/demoScripts/demoPermit2.ts | 113 ++++++++++++++++++++++++++++++ yarn.lock | 8 +++ 3 files changed, 130 insertions(+), 2 deletions(-) create mode 100644 script/demoScripts/demoPermit2.ts diff --git a/package.json b/package.json index b6a965aae..973cd1a6e 100644 --- a/package.json +++ b/package.json @@ -95,6 +95,7 @@ "@safe-global/safe-apps-sdk": "^9.0.0", "@safe-global/safe-core-sdk-types": "^4.1.0", "@uma/sdk": "^0.22.1", + "@uniswap/permit2-sdk": "^1.3.0", "@uniswap/sdk": "^3.0.3", "chalk": "4.1.2", "citty": "^0.1.6", @@ -111,7 +112,13 @@ "zx": "^8.0.2" }, "lint-staged": { - "*.{ts,js}": ["prettier --write", "eslint --fix"], - "*.sol": ["prettier --write", "solhint --fix"] + "*.{ts,js}": [ + "prettier --write", + "eslint --fix" + ], + "*.sol": [ + "prettier --write", + "solhint --fix" + ] } } diff --git a/script/demoScripts/demoPermit2.ts b/script/demoScripts/demoPermit2.ts new file mode 100644 index 000000000..ed493a718 --- /dev/null +++ b/script/demoScripts/demoPermit2.ts @@ -0,0 +1,113 @@ +import { + http, + createPublicClient, + parseAbi, + Hex, + parseUnits, + serializeSignature, + createWalletClient, +} from 'viem' +import { privateKeyToAccount, sign } from 'viem/accounts' +import { arbitrum } from 'viem/chains' +import { defineCommand, runMain } from 'citty' +import { PermitTransferFrom, SignatureTransfer } from '@uniswap/Permit2-sdk' + +const USDT_ADDRESS = '0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9' +const PERMIT2_PROXY_ADDRESS = '0xA3C7a31a2A97b847D967e0B755921D084C46a742' +const PERMIT2_ADDRESS = '0x000000000022D473030F116dDEE9F6B43aC78BA3' + +const main = defineCommand({ + meta: { + name: 'demo-permit2', + description: 'Demonstrate a Permit2 tx', + }, + args: { + signerKey: { + type: 'string', + description: 'Private key of signer', + required: true, + }, + }, + async run({ args }) { + const SIGNER_PRIVATE_KEY = `0x${args.signerKey}` as Hex + + // Setup the required ABI + const permit2ProxyAbi = parseAbi([ + 'function callDiamondWithPermit2(bytes,((address,uint256),uint256,uint256),bytes) external', + 'function nextNonce(address owner) external view returns (uint256)', + ]) + + // Setup a READ-ONLY client + const client = createPublicClient({ + chain: arbitrum, + transport: http(), + }) + + // Setup a signer account + const account = privateKeyToAccount(SIGNER_PRIVATE_KEY) + + // Get calldata to bridge USDT from LIFI API + const url = + 'https://li.quest/v1/quote?fromChain=ARB&toChain=POL&fromToken=0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9&toToken=0xc2132D05D31c914a87C6611C10748AEb04B58e8F&fromAddress=0xb9c0dE368BECE5e76B52545a8E377a4C118f597B&toAddress=0xb9c0dE368BECE5e76B52545a8E377a4C118f597B&fromAmount=5000000' + const options = { method: 'GET', headers: { accept: 'application/json' } } + const lifiResp = await fetch(url, options) + const calldata = (await lifiResp.json()).transactionRequest.data + + // Get the nonce from the PERMIT2 contract + const nonce = await client.readContract({ + address: PERMIT2_PROXY_ADDRESS, + abi: permit2ProxyAbi, + functionName: 'nextNonce', + args: [account.address], + }) + + // Get latest block + const block = await client.getBlock() + const deadline = block.timestamp + 1200n // 20 min deadline, + + // build hash + const permitTransferFrom: PermitTransferFrom = { + permitted: { + token: USDT_ADDRESS, + amount: parseUnits('5', 6), + }, + nonce, + spender: PERMIT2_PROXY_ADDRESS, + deadline, + } + const msgHash = SignatureTransfer.hash( + permitTransferFrom, + PERMIT2_ADDRESS, + arbitrum.id + ) as `0x${string}` + console.log(msgHash) + + // Sign the message hash + const rsvSig = await sign({ hash: msgHash, privateKey: SIGNER_PRIVATE_KEY }) + const signature = serializeSignature(rsvSig) + console.log('signature', signature) + + // Instantiate the executor account and a WRITE enabled client + const executorAccount = privateKeyToAccount(SIGNER_PRIVATE_KEY) + const walletClient = createWalletClient({ + account: executorAccount, + chain: arbitrum, + transport: http(), + }) + + // Execute using the Permit2 Proxy + const tx = await walletClient.writeContract({ + address: PERMIT2_PROXY_ADDRESS, + abi: permit2ProxyAbi, + functionName: 'callDiamondWithPermit2', + args: [ + calldata, + [[USDT_ADDRESS as `0x${string}`, parseUnits('5', 6)], nonce, deadline], + signature, + ], + }) + console.log(`Transfers submitted in tx: ${tx}`) + }, +}) + +runMain(main) diff --git a/yarn.lock b/yarn.lock index 1fcd97361..6e6fad1a5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2082,6 +2082,14 @@ immer "^9.0.7" lodash-es "^4.17.21" +"@uniswap/permit2-sdk@^1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@uniswap/permit2-sdk/-/permit2-sdk-1.3.0.tgz#b54124e570f0adbaca9d39b2de3054fd7d3798a1" + integrity sha512-LstYQWP47dwpQrgqBJ+ysFstne9LgI5FGiKHc2ewjj91MTY8Mq1reocu6U/VDncdR5ef30TUOcZ7gPExRY8r6Q== + dependencies: + ethers "^5.7.0" + tiny-invariant "^1.1.0" + "@uniswap/sdk@^3.0.3": version "3.0.3" resolved "https://registry.yarnpkg.com/@uniswap/sdk/-/sdk-3.0.3.tgz#8201c7c72215d0030cb99acc7e661eff895c18a9" From b0b4bda03de9c8d9e8fb5fa802b6e12c6131ce77 Mon Sep 17 00:00:00 2001 From: Max Klenk Date: Mon, 4 Nov 2024 23:07:22 +0100 Subject: [PATCH 57/69] clean permit2 generation --- script/demoScripts/demoPermit2.ts | 128 ++++++++++++++++++++---------- 1 file changed, 87 insertions(+), 41 deletions(-) diff --git a/script/demoScripts/demoPermit2.ts b/script/demoScripts/demoPermit2.ts index ed493a718..92e554f7f 100644 --- a/script/demoScripts/demoPermit2.ts +++ b/script/demoScripts/demoPermit2.ts @@ -6,16 +6,91 @@ import { parseUnits, serializeSignature, createWalletClient, + PublicClient, } from 'viem' import { privateKeyToAccount, sign } from 'viem/accounts' import { arbitrum } from 'viem/chains' import { defineCommand, runMain } from 'citty' import { PermitTransferFrom, SignatureTransfer } from '@uniswap/Permit2-sdk' +// Sample Transfer: +// sent: +// https://arbiscan.io/tx/0x8d0caf2b4fda6688b5ab36254632f6b6412cf25f0f262689ed41a2a21fe489f1 +// status: +// https://scan.li.fi/tx/0x8d0caf2b4fda6688b5ab36254632f6b6412cf25f0f262689ed41a2a21fe489f1 +// received: +// https://polygonscan.com/tx/0x5dda19403942912027ef5e941e844de3a2a65b0c5a4173e3d388a71a54e5e635 + const USDT_ADDRESS = '0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9' const PERMIT2_PROXY_ADDRESS = '0xA3C7a31a2A97b847D967e0B755921D084C46a742' const PERMIT2_ADDRESS = '0x000000000022D473030F116dDEE9F6B43aC78BA3' +/** + * Get the nonce from the PERMIT2 contract + */ +const getNonceForAddress = async ( + client: PublicClient, + address: `0x${string}` +): Promise => { + const permit2ProxyAbi = parseAbi([ + 'function nextNonce(address owner) external view returns (uint256)', + ]) + + return client.readContract({ + address: PERMIT2_PROXY_ADDRESS, + abi: permit2ProxyAbi, + functionName: 'nextNonce', + args: [address], + }) +} + +/** + * Generate permit data + */ +const getPermitData = async (params: { + userAddress: `0x${string}` + tokenAddress: `0x${string}` + amount: bigint + deadline: bigint +}) => { + const client = createPublicClient({ + chain: arbitrum, + transport: http(), + }) + + // Get latest block + const block = await client.getBlock() + const _deadline = block.timestamp + params.deadline + const nonce = await getNonceForAddress(client, params.userAddress) + + // build hash + const permitTransferFrom: PermitTransferFrom = { + permitted: { + token: params.tokenAddress, + amount: params.amount, + }, + nonce, + spender: PERMIT2_PROXY_ADDRESS, + deadline: _deadline, + } + const msgHash: `0x${string}` = SignatureTransfer.hash( + permitTransferFrom, + PERMIT2_ADDRESS, + arbitrum.id + ) as `0x${string}` + + const transferFromData: [[`0x${string}`, bigint], bigint, bigint] = [ + [params.tokenAddress, params.amount], + nonce, + _deadline, + ] + + return { + msgHash, + transferFromData, + } +} + const main = defineCommand({ meta: { name: 'demo-permit2', @@ -37,12 +112,6 @@ const main = defineCommand({ 'function nextNonce(address owner) external view returns (uint256)', ]) - // Setup a READ-ONLY client - const client = createPublicClient({ - chain: arbitrum, - transport: http(), - }) - // Setup a signer account const account = privateKeyToAccount(SIGNER_PRIVATE_KEY) @@ -53,37 +122,20 @@ const main = defineCommand({ const lifiResp = await fetch(url, options) const calldata = (await lifiResp.json()).transactionRequest.data - // Get the nonce from the PERMIT2 contract - const nonce = await client.readContract({ - address: PERMIT2_PROXY_ADDRESS, - abi: permit2ProxyAbi, - functionName: 'nextNonce', - args: [account.address], + // Get Hash + const permitData = await getPermitData({ + userAddress: account.address, + tokenAddress: USDT_ADDRESS, + amount: parseUnits('5', 6), + deadline: 1200n, // 20min }) - - // Get latest block - const block = await client.getBlock() - const deadline = block.timestamp + 1200n // 20 min deadline, - - // build hash - const permitTransferFrom: PermitTransferFrom = { - permitted: { - token: USDT_ADDRESS, - amount: parseUnits('5', 6), - }, - nonce, - spender: PERMIT2_PROXY_ADDRESS, - deadline, - } - const msgHash = SignatureTransfer.hash( - permitTransferFrom, - PERMIT2_ADDRESS, - arbitrum.id - ) as `0x${string}` - console.log(msgHash) + console.log(permitData) // Sign the message hash - const rsvSig = await sign({ hash: msgHash, privateKey: SIGNER_PRIVATE_KEY }) + const rsvSig = await sign({ + hash: permitData.msgHash, + privateKey: SIGNER_PRIVATE_KEY, + }) const signature = serializeSignature(rsvSig) console.log('signature', signature) @@ -94,17 +146,11 @@ const main = defineCommand({ chain: arbitrum, transport: http(), }) - - // Execute using the Permit2 Proxy const tx = await walletClient.writeContract({ address: PERMIT2_PROXY_ADDRESS, abi: permit2ProxyAbi, functionName: 'callDiamondWithPermit2', - args: [ - calldata, - [[USDT_ADDRESS as `0x${string}`, parseUnits('5', 6)], nonce, deadline], - signature, - ], + args: [calldata, permitData.transferFromData, signature], }) console.log(`Transfers submitted in tx: ${tx}`) }, From 181566e2a494073c5ab30b30b60f8b69d437bdc4 Mon Sep 17 00:00:00 2001 From: Max Klenk Date: Sat, 9 Nov 2024 13:07:26 +0100 Subject: [PATCH 58/69] simplify signature generation --- script/demoScripts/demoPermit2.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/script/demoScripts/demoPermit2.ts b/script/demoScripts/demoPermit2.ts index 92e554f7f..e12c71d58 100644 --- a/script/demoScripts/demoPermit2.ts +++ b/script/demoScripts/demoPermit2.ts @@ -4,7 +4,6 @@ import { parseAbi, Hex, parseUnits, - serializeSignature, createWalletClient, PublicClient, } from 'viem' @@ -132,11 +131,11 @@ const main = defineCommand({ console.log(permitData) // Sign the message hash - const rsvSig = await sign({ + const signature = await sign({ hash: permitData.msgHash, privateKey: SIGNER_PRIVATE_KEY, + to: 'hex', }) - const signature = serializeSignature(rsvSig) console.log('signature', signature) // Instantiate the executor account and a WRITE enabled client From 6113466e8fc1b8193683d65d66336a2d21bc430c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Mon, 25 Nov 2024 10:54:00 +0700 Subject: [PATCH 59/69] remove old openzeppelin lib --- lib/openzeppelin-contracts | 1 - 1 file changed, 1 deletion(-) delete mode 160000 lib/openzeppelin-contracts diff --git a/lib/openzeppelin-contracts b/lib/openzeppelin-contracts deleted file mode 160000 index e50c24f58..000000000 --- a/lib/openzeppelin-contracts +++ /dev/null @@ -1 +0,0 @@ -Subproject commit e50c24f5839db17f46991478384bfda14acfb830 From f07b9a1c6170abd5c88cb3508843bc8393412400 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Mon, 25 Nov 2024 10:54:30 +0700 Subject: [PATCH 60/69] forge install: openzeppelin-contracts v4.9.2 --- lib/openzeppelin-contracts | 1 + 1 file changed, 1 insertion(+) create mode 160000 lib/openzeppelin-contracts diff --git a/lib/openzeppelin-contracts b/lib/openzeppelin-contracts new file mode 160000 index 000000000..e50c24f58 --- /dev/null +++ b/lib/openzeppelin-contracts @@ -0,0 +1 @@ +Subproject commit e50c24f5839db17f46991478384bfda14acfb830 From 39aee6b2568014e478b817538433801e100bd1d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Mon, 25 Nov 2024 10:54:45 +0700 Subject: [PATCH 61/69] update audit log and report --- audit/auditLog.json | 17 ++++++++++------- ...2Proxy.pdf => 2024.11.22_Permit2Proxy.pdf} | Bin 68563 -> 71570 bytes 2 files changed, 10 insertions(+), 7 deletions(-) rename audit/reports/{2024.10.03_Permit2Proxy.pdf => 2024.11.22_Permit2Proxy.pdf} (60%) diff --git a/audit/auditLog.json b/audit/auditLog.json index 959398508..7e73b0108 100644 --- a/audit/auditLog.json +++ b/audit/auditLog.json @@ -35,25 +35,28 @@ "auditReportPath": "./audit/reports/2024.11.05_EmergencyPauseFacet_ReAudit.pdf", "auditCommitHash": "da61880ba3847c07c35b64a78b957ff845ec18ac" }, - "audit20241003": { - "auditCompletedOn": "10.03.2024", + "audit20241122": { + "auditCompletedOn": "22.11.2024", "auditedBy": "Sujith Somraaj (individual security researcher)", "auditorGitHandle": "sujithsomraaj", - "auditReportPath": "./audit/reports/2024.10.03_Permit2Proxy.pdf", + "auditReportPath": "./audit/reports/2024.11.22_Permit2Proxy.pdf", "auditCommitHash": "0e3debb78abcdf9a9f934115338b611e16b039a0" } }, "auditedContracts": { - "EmergencyPauseFacet": { - "1.0.0": ["audit20240913"], - "1.0.1": ["audit20241105"] - }, "AcrossFacetV3": { "1.0.0": ["audit20241007"] }, "AcrossFacetPackedV3": { "1.0.0": ["audit20241007"] }, + "EmergencyPauseFacet": { + "1.0.0": ["audit20240913"], + "1.0.1": ["audit20241105"] + }, + "Permit2Proxy": { + "1.0.0": ["audit20241122"] + }, "ReceiverAcrossV3": { "1.0.0": ["audit20241007"] }, diff --git a/audit/reports/2024.10.03_Permit2Proxy.pdf b/audit/reports/2024.11.22_Permit2Proxy.pdf similarity index 60% rename from audit/reports/2024.10.03_Permit2Proxy.pdf rename to audit/reports/2024.11.22_Permit2Proxy.pdf index 94e17b345a4959ac311bbfd8583069f3f8f0789f..f23a830c89abe6bfdd2267c09279a91097fc98ca 100644 GIT binary patch delta 29253 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 Date: Mon, 25 Nov 2024 11:04:40 +0700 Subject: [PATCH 62/69] target state updated --- script/deploy/_targetState.json | 314 +++++++++++++++----------------- 1 file changed, 144 insertions(+), 170 deletions(-) diff --git a/script/deploy/_targetState.json b/script/deploy/_targetState.json index a9a0494b5..3a8341d36 100644 --- a/script/deploy/_targetState.json +++ b/script/deploy/_targetState.json @@ -12,9 +12,9 @@ "GenericSwapFacet": "1.0.0", "GenericSwapFacetV3": "1.0.1", "LIFuelFacet": "1.0.1", - "CalldataVerificationFacet": "1.1.1", + "CalldataVerificationFacet": "1.1.2", "StandardizedCallFacet": "1.1.0", - "EmergencyPauseFacet": "1.0.0", + "EmergencyPauseFacet": "1.0.1", "LiFiDiamond": "1.0.0", "ERC20Proxy": "1.0.0", "Executor": "2.0.0", @@ -42,11 +42,11 @@ "HopFacet": "2.0.0", "HopFacetPacked": "1.0.6", "HopFacetOptimized": "2.0.0", - "HyphenFacet": "1.0.0", "MayanFacet": "1.0.0", "OmniBridgeFacet": "1.0.0", "OptimismBridgeFacet": "1.0.0", "PolygonBridgeFacet": "1.0.0", + "RelayFacet": "\u001b[31m[error] the following filepath is invalid: \u001b[31m[error] could not find src FILE path for contract RelayFacet\u001b[0m\u001b[0m", "SquidFacet": "1.0.0", "StargateFacet": "2.2.0", "StargateFacetV2": "1.0.1", @@ -69,9 +69,9 @@ "GenericSwapFacet": "1.0.0", "GenericSwapFacetV3": "1.0.1", "LIFuelFacet": "1.0.1", - "CalldataVerificationFacet": "1.1.1", + "CalldataVerificationFacet": "1.1.2", "StandardizedCallFacet": "1.1.0", - "EmergencyPauseFacet": "1.0.0", + "EmergencyPauseFacet": "1.0.1", "LiFiDiamond": "1.0.0", "ERC20Proxy": "1.0.0", "Executor": "2.0.0", @@ -97,8 +97,8 @@ "HopFacet": "2.0.0", "HopFacetPacked": "1.0.6", "HopFacetOptimized": "2.0.0", - "HyphenFacet": "1.0.0", "MayanFacet": "1.0.0", + "RelayFacet": "\u001b[31m[error] the following filepath is invalid: \u001b[31m[error] could not find src FILE path for contract RelayFacet\u001b[0m\u001b[0m", "SquidFacet": "1.0.0", "StargateFacet": "2.2.0", "StargateFacetV2": "1.0.1", @@ -120,9 +120,9 @@ "GenericSwapFacet": "1.0.0", "GenericSwapFacetV3": "1.0.1", "LIFuelFacet": "1.0.1", - "CalldataVerificationFacet": "1.1.1", + "CalldataVerificationFacet": "1.1.2", "StandardizedCallFacet": "1.1.0", - "EmergencyPauseFacet": "1.0.0", + "EmergencyPauseFacet": "1.0.1", "LiFiDiamond": "1.0.0", "ERC20Proxy": "1.0.0", "Executor": "2.0.0", @@ -131,7 +131,6 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", - "Permit2Proxy": "1.0.0", "AllBridgeFacet": "2.0.0", "AmarokFacet": "3.0.0", "AmarokFacetPacked": "1.0.0", @@ -139,9 +138,9 @@ "CBridgeFacetPacked": "1.0.3", "RelayerCelerIM": "2.0.0", "CelerIMFacetMutable": "2.0.0", - "HyphenFacet": "1.0.0", "MayanFacet": "1.0.0", "OpBNBBridgeFacet": "\u001b[31m[error] the following filepath is invalid: \u001b[31m[error] could not find src FILE path for contract OpBNBBridgeFacet\u001b[0m\u001b[0m", + "RelayFacet": "\u001b[31m[error] the following filepath is invalid: \u001b[31m[error] could not find src FILE path for contract RelayFacet\u001b[0m\u001b[0m", "SquidFacet": "1.0.0", "StargateFacet": "2.2.0", "StargateFacetV2": "1.0.1", @@ -164,9 +163,9 @@ "GenericSwapFacet": "1.0.0", "GenericSwapFacetV3": "1.0.1", "LIFuelFacet": "1.0.1", - "CalldataVerificationFacet": "1.1.1", + "CalldataVerificationFacet": "1.1.2", "StandardizedCallFacet": "1.1.0", - "EmergencyPauseFacet": "1.0.0", + "EmergencyPauseFacet": "1.0.1", "LiFiDiamond": "1.0.0", "ERC20Proxy": "1.0.0", "Executor": "2.0.0", @@ -175,7 +174,6 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", - "Permit2Proxy": "1.0.0", "AmarokFacet": "3.0.0", "AmarokFacetPacked": "1.0.0", "CBridgeFacet": "1.0.0", @@ -199,9 +197,9 @@ "GenericSwapFacet": "1.0.0", "GenericSwapFacetV3": "1.0.1", "LIFuelFacet": "1.0.1", - "CalldataVerificationFacet": "1.1.1", + "CalldataVerificationFacet": "1.1.2", "StandardizedCallFacet": "1.1.0", - "EmergencyPauseFacet": "1.0.0", + "EmergencyPauseFacet": "1.0.1", "LiFiDiamond": "1.0.0", "ERC20Proxy": "1.0.0", "Executor": "2.0.0", @@ -210,11 +208,9 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", - "Permit2Proxy": "1.0.0", "CBridgeFacet": "1.0.0", "RelayerCelerIM": "2.0.0", "CelerIMFacetMutable": "2.0.0", - "HyphenFacet": "1.0.0", "SquidFacet": "1.0.0", "StargateFacet": "2.2.0" } @@ -234,9 +230,9 @@ "GenericSwapFacet": "1.0.0", "GenericSwapFacetV3": "1.0.1", "LIFuelFacet": "1.0.1", - "CalldataVerificationFacet": "1.1.1", + "CalldataVerificationFacet": "1.1.2", "StandardizedCallFacet": "1.1.0", - "EmergencyPauseFacet": "1.0.0", + "EmergencyPauseFacet": "1.0.1", "LiFiDiamond": "1.0.0", "ERC20Proxy": "1.0.0", "Executor": "2.0.0", @@ -245,15 +241,14 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", - "Permit2Proxy": "1.0.0", "AllBridgeFacet": "2.0.0", "CBridgeFacet": "1.0.0", "CBridgeFacetPacked": "1.0.3", "CelerCircleBridgeFacet": "1.0.1", "RelayerCelerIM": "2.0.0", "CelerIMFacetMutable": "2.0.0", - "HyphenFacet": "1.0.0", "MayanFacet": "1.0.0", + "RelayFacet": "\u001b[31m[error] the following filepath is invalid: \u001b[31m[error] could not find src FILE path for contract RelayFacet\u001b[0m\u001b[0m", "SquidFacet": "1.0.0", "StargateFacet": "2.2.0", "StargateFacetV2": "1.0.1", @@ -276,9 +271,9 @@ "GenericSwapFacet": "1.0.0", "GenericSwapFacetV3": "1.0.1", "LIFuelFacet": "1.0.1", - "CalldataVerificationFacet": "1.1.1", + "CalldataVerificationFacet": "1.1.2", "StandardizedCallFacet": "1.1.0", - "EmergencyPauseFacet": "1.0.0", + "EmergencyPauseFacet": "1.0.1", "LiFiDiamond": "1.0.0", "ERC20Proxy": "1.0.0", "Executor": "2.0.0", @@ -304,8 +299,8 @@ "HopFacet": "2.0.0", "HopFacetPacked": "1.0.6", "HopFacetOptimized": "2.0.0", - "HyphenFacet": "1.0.0", "MayanFacet": "1.0.0", + "RelayFacet": "\u001b[31m[error] the following filepath is invalid: \u001b[31m[error] could not find src FILE path for contract RelayFacet\u001b[0m\u001b[0m", "SquidFacet": "1.0.0", "StargateFacet": "2.2.0", "StargateFacetV2": "1.0.1", @@ -327,9 +322,9 @@ "GenericSwapFacet": "1.0.0", "GenericSwapFacetV3": "1.0.1", "LIFuelFacet": "1.0.1", - "CalldataVerificationFacet": "1.1.1", + "CalldataVerificationFacet": "1.1.2", "StandardizedCallFacet": "1.1.0", - "EmergencyPauseFacet": "1.0.0", + "EmergencyPauseFacet": "1.0.1", "LiFiDiamond": "1.0.0", "ERC20Proxy": "1.0.0", "Executor": "2.0.0", @@ -355,7 +350,7 @@ "HopFacet": "2.0.0", "HopFacetPacked": "1.0.6", "HopFacetOptimized": "2.0.0", - "HyphenFacet": "1.0.0", + "RelayFacet": "\u001b[31m[error] the following filepath is invalid: \u001b[31m[error] could not find src FILE path for contract RelayFacet\u001b[0m\u001b[0m", "StargateFacet": "2.2.0", "StargateFacetV2": "1.0.1", "ReceiverStargateV2": "1.0.0", @@ -376,9 +371,9 @@ "GenericSwapFacet": "1.0.0", "GenericSwapFacetV3": "1.0.1", "LIFuelFacet": "1.0.1", - "CalldataVerificationFacet": "1.1.1", + "CalldataVerificationFacet": "1.1.2", "StandardizedCallFacet": "1.1.0", - "EmergencyPauseFacet": "1.0.0", + "EmergencyPauseFacet": "1.0.1", "LiFiDiamond": "1.0.0", "ERC20Proxy": "1.0.0", "Executor": "2.0.0", @@ -387,7 +382,6 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", - "Permit2Proxy": "1.0.0", "CBridgeFacet": "1.0.0", "RelayerCelerIM": "2.0.0", "CelerIMFacetMutable": "2.0.0" @@ -407,9 +401,9 @@ "GenericSwapFacet": "1.0.0", "GenericSwapFacetV3": "1.0.1", "LIFuelFacet": "1.0.1", - "CalldataVerificationFacet": "1.1.1", + "CalldataVerificationFacet": "1.1.2", "StandardizedCallFacet": "1.1.0", - "EmergencyPauseFacet": "1.0.0", + "EmergencyPauseFacet": "1.0.1", "LiFiDiamond": "1.0.0", "ERC20Proxy": "1.0.0", "Executor": "2.0.0", @@ -418,7 +412,6 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", - "Permit2Proxy": "1.0.0", "CBridgeFacet": "1.0.0", "SquidFacet": "1.0.0" } @@ -437,9 +430,9 @@ "GenericSwapFacet": "1.0.0", "GenericSwapFacetV3": "1.0.1", "LIFuelFacet": "1.0.1", - "CalldataVerificationFacet": "1.1.1", + "CalldataVerificationFacet": "1.1.2", "StandardizedCallFacet": "1.1.0", - "EmergencyPauseFacet": "1.0.0", + "EmergencyPauseFacet": "1.0.1", "LiFiDiamond": "1.0.0", "ERC20Proxy": "1.0.0", "Executor": "2.0.0", @@ -447,8 +440,7 @@ "Receiver": "2.0.2", "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", - "LiFiDEXAggregator": "1.0.0", - "Permit2Proxy": "1.0.0" + "LiFiDEXAggregator": "1.0.0" } } }, @@ -468,9 +460,9 @@ "GenericSwapFacet": "1.0.0", "GenericSwapFacetV3": "1.0.1", "LIFuelFacet": "1.0.1", - "CalldataVerificationFacet": "1.1.1", + "CalldataVerificationFacet": "1.1.2", "StandardizedCallFacet": "1.1.0", - "EmergencyPauseFacet": "1.0.0", + "EmergencyPauseFacet": "1.0.1", "LiFiDiamond": "1.0.0", "ERC20Proxy": "1.0.0", "Executor": "2.0.0", @@ -479,7 +471,6 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", - "Permit2Proxy": "1.0.0", "CBridgeFacet": "1.0.0", "RelayerCelerIM": "2.0.0", "CelerIMFacetMutable": "2.0.0", @@ -501,9 +492,9 @@ "GenericSwapFacet": "1.0.0", "GenericSwapFacetV3": "1.0.1", "LIFuelFacet": "1.0.1", - "CalldataVerificationFacet": "1.1.1", + "CalldataVerificationFacet": "1.1.2", "StandardizedCallFacet": "1.1.0", - "EmergencyPauseFacet": "1.0.0", + "EmergencyPauseFacet": "1.0.1", "LiFiDiamond": "1.0.0", "ERC20Proxy": "1.0.0", "Executor": "2.0.0", @@ -512,7 +503,6 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", - "Permit2Proxy": "1.0.0", "CBridgeFacet": "1.0.0", "SymbiosisFacet": "1.0.0" } @@ -531,9 +521,9 @@ "GenericSwapFacet": "1.0.0", "GenericSwapFacetV3": "1.0.1", "LIFuelFacet": "1.0.1", - "CalldataVerificationFacet": "1.1.1", + "CalldataVerificationFacet": "1.1.2", "StandardizedCallFacet": "1.1.0", - "EmergencyPauseFacet": "1.0.0", + "EmergencyPauseFacet": "1.0.1", "LiFiDiamond": "1.0.0", "ERC20Proxy": "1.0.0", "Executor": "2.0.0", @@ -542,7 +532,6 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", - "Permit2Proxy": "1.0.0", "RelayerCelerIM": "2.0.0", "CelerIMFacetMutable": "2.0.0", "SymbiosisFacet": "1.0.0" @@ -562,9 +551,9 @@ "GenericSwapFacet": "1.0.0", "GenericSwapFacetV3": "1.0.1", "LIFuelFacet": "1.0.1", - "CalldataVerificationFacet": "1.1.1", + "CalldataVerificationFacet": "1.1.2", "StandardizedCallFacet": "1.1.0", - "EmergencyPauseFacet": "1.0.0", + "EmergencyPauseFacet": "1.0.1", "LiFiDiamond": "1.0.0", "ERC20Proxy": "1.0.0", "Executor": "2.0.0", @@ -573,7 +562,6 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", - "Permit2Proxy": "1.0.0", "AcrossFacet": "2.0.0", "AcrossFacetPacked": "1.0.0", "AcrossFacetV3": "1.0.0", @@ -582,6 +570,7 @@ "CBridgeFacet": "1.0.0", "CBridgeFacetPacked": "1.0.3", "CelerIMFacetMutable": "2.0.0", + "RelayFacet": "\u001b[31m[error] the following filepath is invalid: \u001b[31m[error] could not find src FILE path for contract RelayFacet\u001b[0m\u001b[0m", "SymbiosisFacet": "1.0.0" } } @@ -599,9 +588,9 @@ "GenericSwapFacet": "1.0.0", "GenericSwapFacetV3": "1.0.1", "LIFuelFacet": "1.0.1", - "CalldataVerificationFacet": "1.1.1", + "CalldataVerificationFacet": "1.1.2", "StandardizedCallFacet": "1.1.0", - "EmergencyPauseFacet": "1.0.0", + "EmergencyPauseFacet": "1.0.1", "LiFiDiamond": "1.0.0", "ERC20Proxy": "1.0.0", "Executor": "2.0.0", @@ -610,12 +599,12 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", - "Permit2Proxy": "1.0.0", "CBridgeFacet": "1.0.0", "RelayerCelerIM": "2.0.0", "CelerIMFacetMutable": "2.0.0", "HopFacet": "2.0.0", "HopFacetPacked": "1.0.6", + "RelayFacet": "\u001b[31m[error] the following filepath is invalid: \u001b[31m[error] could not find src FILE path for contract RelayFacet\u001b[0m\u001b[0m", "SymbiosisFacet": "1.0.0" } } @@ -633,9 +622,9 @@ "GenericSwapFacet": "1.0.0", "GenericSwapFacetV3": "1.0.1", "LIFuelFacet": "1.0.1", - "CalldataVerificationFacet": "1.1.1", + "CalldataVerificationFacet": "1.1.2", "StandardizedCallFacet": "1.1.0", - "EmergencyPauseFacet": "1.0.0", + "EmergencyPauseFacet": "1.0.1", "LiFiDiamond": "1.0.0", "ERC20Proxy": "1.0.0", "Executor": "2.0.0", @@ -644,13 +633,13 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", - "Permit2Proxy": "1.0.0", "AmarokFacet": "3.0.0", "CBridgeFacet": "1.0.0", "CBridgeFacetPacked": "1.0.3", "HopFacet": "2.0.0", "HopFacetPacked": "1.0.6", "HopFacetOptimized": "2.0.0", + "RelayFacet": "\u001b[31m[error] the following filepath is invalid: \u001b[31m[error] could not find src FILE path for contract RelayFacet\u001b[0m\u001b[0m", "StargateFacet": "2.2.0", "StargateFacetV2": "1.0.1", "ReceiverStargateV2": "1.0.0", @@ -671,9 +660,9 @@ "GenericSwapFacet": "1.0.0", "GenericSwapFacetV3": "1.0.1", "LIFuelFacet": "1.0.1", - "CalldataVerificationFacet": "1.1.1", + "CalldataVerificationFacet": "1.1.2", "StandardizedCallFacet": "1.1.0", - "EmergencyPauseFacet": "1.0.0", + "EmergencyPauseFacet": "1.0.1", "LiFiDiamond": "1.0.0", "ERC20Proxy": "1.0.0", "Executor": "2.0.0", @@ -682,7 +671,6 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", - "Permit2Proxy": "1.0.0", "AcrossFacet": "2.0.0", "AcrossFacetPacked": "1.0.0", "AcrossFacetV3": "1.0.0", @@ -696,6 +684,7 @@ "HopFacet": "2.0.0", "HopFacetPacked": "1.0.6", "HopFacetOptimized": "2.0.0", + "RelayFacet": "\u001b[31m[error] the following filepath is invalid: \u001b[31m[error] could not find src FILE path for contract RelayFacet\u001b[0m\u001b[0m", "StargateFacet": "2.2.0", "StargateFacetV2": "1.0.1", "ReceiverStargateV2": "1.0.0", @@ -716,9 +705,9 @@ "GenericSwapFacet": "1.0.0", "GenericSwapFacetV3": "1.0.1", "LIFuelFacet": "1.0.1", - "CalldataVerificationFacet": "1.1.1", + "CalldataVerificationFacet": "1.1.2", "StandardizedCallFacet": "1.1.0", - "EmergencyPauseFacet": "1.0.0", + "EmergencyPauseFacet": "1.0.1", "LiFiDiamond": "1.0.0", "ERC20Proxy": "1.0.0", "Executor": "2.0.0", @@ -726,8 +715,7 @@ "Receiver": "2.0.2", "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", - "LiFiDEXAggregator": "1.0.0", - "Permit2Proxy": "1.0.0" + "LiFiDEXAggregator": "1.0.0" } } }, @@ -744,9 +732,9 @@ "GenericSwapFacet": "1.0.0", "GenericSwapFacetV3": "1.0.1", "LIFuelFacet": "1.0.1", - "CalldataVerificationFacet": "1.1.1", + "CalldataVerificationFacet": "1.1.2", "StandardizedCallFacet": "1.1.0", - "EmergencyPauseFacet": "1.0.0", + "EmergencyPauseFacet": "1.0.1", "LiFiDiamond": "1.0.0", "ERC20Proxy": "1.0.0", "Executor": "2.0.0", @@ -755,7 +743,6 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", - "Permit2Proxy": "1.0.0", "AmarokFacet": "3.0.0", "StargateFacet": "2.2.0", "StargateFacetV2": "1.0.1", @@ -777,9 +764,9 @@ "GenericSwapFacet": "1.0.0", "GenericSwapFacetV3": "1.0.1", "LIFuelFacet": "1.0.1", - "CalldataVerificationFacet": "1.1.1", + "CalldataVerificationFacet": "1.1.2", "StandardizedCallFacet": "1.1.0", - "EmergencyPauseFacet": "1.0.0", + "EmergencyPauseFacet": "1.0.1", "LiFiDiamond": "1.0.0", "ERC20Proxy": "1.0.0", "Executor": "2.0.0", @@ -788,7 +775,6 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", - "Permit2Proxy": "1.0.0", "AcrossFacet": "2.0.0", "AcrossFacetPacked": "1.0.0", "AcrossFacetV3": "1.0.0", @@ -796,6 +782,7 @@ "ReceiverAcrossV3": "1.0.0", "CBridgeFacet": "1.0.0", "CBridgeFacetPacked": "1.0.3", + "RelayFacet": "\u001b[31m[error] the following filepath is invalid: \u001b[31m[error] could not find src FILE path for contract RelayFacet\u001b[0m\u001b[0m", "StargateFacet": "2.2.0", "StargateFacetV2": "1.0.1", "ReceiverStargateV2": "1.0.0", @@ -816,9 +803,9 @@ "GenericSwapFacet": "1.0.0", "GenericSwapFacetV3": "1.0.1", "LIFuelFacet": "1.0.1", - "CalldataVerificationFacet": "1.1.1", + "CalldataVerificationFacet": "1.1.2", "StandardizedCallFacet": "1.1.0", - "EmergencyPauseFacet": "1.0.0", + "EmergencyPauseFacet": "1.0.1", "LiFiDiamond": "1.0.0", "ERC20Proxy": "1.0.0", "Executor": "2.0.0", @@ -827,13 +814,13 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", - "Permit2Proxy": "1.0.0", "AcrossFacet": "2.0.0", "AcrossFacetPacked": "1.0.0", "AcrossFacetV3": "1.0.0", "AcrossFacetPackedV3": "1.0.0", "ReceiverAcrossV3": "1.0.0", "AmarokFacet": "3.0.0", + "RelayFacet": "\u001b[31m[error] the following filepath is invalid: \u001b[31m[error] could not find src FILE path for contract RelayFacet\u001b[0m\u001b[0m", "SymbiosisFacet": "1.0.0" } } @@ -851,9 +838,9 @@ "GenericSwapFacet": "1.0.0", "GenericSwapFacetV3": "1.0.1", "LIFuelFacet": "1.0.1", - "CalldataVerificationFacet": "1.1.1", + "CalldataVerificationFacet": "1.1.2", "StandardizedCallFacet": "1.1.0", - "EmergencyPauseFacet": "1.0.0", + "EmergencyPauseFacet": "1.0.1", "LiFiDiamond": "1.0.0", "ERC20Proxy": "1.0.0", "Executor": "2.0.0", @@ -862,7 +849,6 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", - "Permit2Proxy": "1.0.0", "SquidFacet": "1.0.0", "StargateFacet": "2.2.0", "StargateFacetV2": "1.0.1", @@ -884,9 +870,9 @@ "GenericSwapFacet": "1.0.0", "GenericSwapFacetV3": "1.0.1", "LIFuelFacet": "1.0.1", - "CalldataVerificationFacet": "1.1.1", + "CalldataVerificationFacet": "1.1.2", "StandardizedCallFacet": "1.1.0", - "EmergencyPauseFacet": "1.0.0", + "EmergencyPauseFacet": "1.0.1", "LiFiDiamond": "1.0.0", "ERC20Proxy": "1.0.0", "Executor": "2.0.0", @@ -914,9 +900,9 @@ "GenericSwapFacet": "1.0.0", "GenericSwapFacetV3": "1.0.1", "LIFuelFacet": "1.0.1", - "CalldataVerificationFacet": "1.1.1", + "CalldataVerificationFacet": "1.1.2", "StandardizedCallFacet": "1.1.0", - "EmergencyPauseFacet": "1.0.0", + "EmergencyPauseFacet": "1.0.1", "LiFiDiamond": "1.0.0", "ERC20Proxy": "1.0.0", "Executor": "2.0.0", @@ -925,7 +911,6 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", - "Permit2Proxy": "1.0.0", "AcrossFacet": "2.0.0", "AcrossFacetPacked": "1.0.0", "AcrossFacetV3": "1.0.0", @@ -933,6 +918,7 @@ "ReceiverAcrossV3": "1.0.0", "CBridgeFacet": "1.0.0", "CBridgeFacetPacked": "1.0.3", + "RelayFacet": "\u001b[31m[error] the following filepath is invalid: \u001b[31m[error] could not find src FILE path for contract RelayFacet\u001b[0m\u001b[0m", "SquidFacet": "1.0.0", "SymbiosisFacet": "1.0.0" } @@ -952,9 +938,9 @@ "GenericSwapFacet": "1.0.0", "GenericSwapFacetV3": "1.0.1", "LIFuelFacet": "1.0.1", - "CalldataVerificationFacet": "1.1.1", + "CalldataVerificationFacet": "1.1.2", "StandardizedCallFacet": "1.1.0", - "EmergencyPauseFacet": "1.0.0", + "EmergencyPauseFacet": "1.0.1", "LiFiDiamond": "1.0.0", "ERC20Proxy": "1.0.0", "Executor": "2.0.0", @@ -963,8 +949,9 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", - "Permit2Proxy": "1.0.0", "SquidFacet": "1.0.0", + "StargateFacetV2": "1.0.1", + "ReceiverStargateV2": "1.0.0", "SymbiosisFacet": "1.0.0" } } @@ -982,39 +969,9 @@ "GenericSwapFacet": "1.0.0", "GenericSwapFacetV3": "1.0.1", "LIFuelFacet": "1.0.1", - "CalldataVerificationFacet": "1.1.1", - "StandardizedCallFacet": "1.1.0", - "EmergencyPauseFacet": "1.0.0", - "LiFiDiamond": "1.0.0", - "ERC20Proxy": "1.0.0", - "Executor": "2.0.0", - "FeeCollector": "1.0.0", - "Receiver": "2.0.2", - "LiFuelFeeCollector": "1.0.1", - "TokenWrapper": "1.0.0", - "LiFiDEXAggregator": "1.0.0", - "Permit2Proxy": "1.0.0", - "SquidFacet": "1.0.0", - "SymbiosisFacet": "1.0.0" - } - } - }, - "gravity": { - "production": { - "LiFiDiamond": { - "DiamondCutFacet": "1.0.0", - "DiamondLoupeFacet": "1.0.0", - "OwnershipFacet": "1.0.0", - "DexManagerFacet": "1.0.1", - "AccessManagerFacet": "1.0.0", - "WithdrawFacet": "1.0.0", - "PeripheryRegistryFacet": "1.0.0", - "GenericSwapFacet": "1.0.0", - "GenericSwapFacetV3": "1.0.1", - "LIFuelFacet": "1.0.1", - "CalldataVerificationFacet": "1.1.1", + "CalldataVerificationFacet": "1.1.2", "StandardizedCallFacet": "1.1.0", - "EmergencyPauseFacet": "1.0.0", + "EmergencyPauseFacet": "1.0.1", "LiFiDiamond": "1.0.0", "ERC20Proxy": "1.0.0", "Executor": "2.0.0", @@ -1023,14 +980,13 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", - "Permit2Proxy": "1.0.0", "StargateFacetV2": "1.0.1", "ReceiverStargateV2": "1.0.0", "SymbiosisFacet": "1.0.0" } } }, - "immutablezkevm": { + "gravity": { "production": { "LiFiDiamond": { "DiamondCutFacet": "1.0.0", @@ -1043,9 +999,9 @@ "GenericSwapFacet": "1.0.0", "GenericSwapFacetV3": "1.0.1", "LIFuelFacet": "1.0.1", - "CalldataVerificationFacet": "1.1.1", + "CalldataVerificationFacet": "1.1.2", "StandardizedCallFacet": "1.1.0", - "EmergencyPauseFacet": "1.0.0", + "EmergencyPauseFacet": "1.0.1", "LiFiDiamond": "1.0.0", "ERC20Proxy": "1.0.0", "Executor": "2.0.0", @@ -1054,7 +1010,6 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", - "Permit2Proxy": "1.0.0", "CBridgeFacet": "1.0.0", "CBridgeFacetPacked": "1.0.3", "RelayerCelerIM": "2.0.0", @@ -1064,7 +1019,7 @@ } } }, - "bsc-testnet": { + "immutablezkevm": { "production": { "LiFiDiamond": { "DiamondCutFacet": "1.0.0", @@ -1077,9 +1032,9 @@ "GenericSwapFacet": "1.0.0", "GenericSwapFacetV3": "1.0.1", "LIFuelFacet": "1.0.1", - "CalldataVerificationFacet": "1.1.1", + "CalldataVerificationFacet": "1.1.2", "StandardizedCallFacet": "1.1.0", - "EmergencyPauseFacet": "1.0.0", + "EmergencyPauseFacet": "1.0.1", "LiFiDiamond": "1.0.0", "ERC20Proxy": "1.0.0", "Executor": "2.0.0", @@ -1088,12 +1043,11 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", - "Permit2Proxy": "1.0.0", "SquidFacet": "1.0.0" } } }, - "sepolia": { + "bsc-testnet": { "production": { "LiFiDiamond": { "DiamondCutFacet": "1.0.0", @@ -1106,9 +1060,9 @@ "GenericSwapFacet": "1.0.0", "GenericSwapFacetV3": "1.0.1", "LIFuelFacet": "1.0.1", - "CalldataVerificationFacet": "1.1.1", + "CalldataVerificationFacet": "1.1.2", "StandardizedCallFacet": "1.1.0", - "EmergencyPauseFacet": "1.0.0", + "EmergencyPauseFacet": "1.0.1", "LiFiDiamond": "1.0.0", "ERC20Proxy": "1.0.0", "Executor": "2.0.0", @@ -1117,13 +1071,12 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", - "Permit2Proxy": "1.0.0", "RelayerCelerIM": "2.0.0", "CelerIMFacetMutable": "2.0.0" } } }, - "mumbai": { + "sepolia": { "production": { "LiFiDiamond": { "DiamondCutFacet": "1.0.0", @@ -1136,9 +1089,9 @@ "GenericSwapFacet": "1.0.0", "GenericSwapFacetV3": "1.0.1", "LIFuelFacet": "1.0.1", - "CalldataVerificationFacet": "1.1.1", + "CalldataVerificationFacet": "1.1.2", "StandardizedCallFacet": "1.1.0", - "EmergencyPauseFacet": "1.0.0", + "EmergencyPauseFacet": "1.0.1", "LiFiDiamond": "1.0.0", "ERC20Proxy": "1.0.0", "Executor": "2.0.0", @@ -1146,12 +1099,11 @@ "Receiver": "2.0.2", "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", - "LiFiDEXAggregator": "1.0.0", - "Permit2Proxy": "1.0.0" + "LiFiDEXAggregator": "1.0.0" } } }, - "lineatest": { + "mumbai": { "production": { "LiFiDiamond": { "DiamondCutFacet": "1.0.0", @@ -1164,9 +1116,9 @@ "GenericSwapFacet": "1.0.0", "GenericSwapFacetV3": "1.0.1", "LIFuelFacet": "1.0.1", - "CalldataVerificationFacet": "1.1.1", + "CalldataVerificationFacet": "1.1.2", "StandardizedCallFacet": "1.1.0", - "EmergencyPauseFacet": "1.0.0", + "EmergencyPauseFacet": "1.0.1", "LiFiDiamond": "1.0.0", "ERC20Proxy": "1.0.0", "Executor": "2.0.0", @@ -1175,7 +1127,6 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", - "Permit2Proxy": "1.0.0", "RelayerCelerIM": "2.0.0", "CelerIMFacetMutable": "2.0.0" } @@ -1194,9 +1145,9 @@ "GenericSwapFacet": "1.0.0", "GenericSwapFacetV3": "1.0.1", "LIFuelFacet": "1.0.1", - "CalldataVerificationFacet": "1.1.1", + "CalldataVerificationFacet": "1.1.2", "StandardizedCallFacet": "1.1.0", - "EmergencyPauseFacet": "1.0.0", + "EmergencyPauseFacet": "1.0.1", "LiFiDiamond": "1.0.0", "ERC20Proxy": "1.0.0", "Executor": "2.0.0", @@ -1205,7 +1156,6 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", - "Permit2Proxy": "1.0.0", "RelayerCelerIM": "2.0.0", "CelerIMFacetMutable": "2.0.0" } @@ -1224,9 +1174,9 @@ "GenericSwapFacet": "1.0.0", "GenericSwapFacetV3": "1.0.1", "LIFuelFacet": "1.0.1", - "CalldataVerificationFacet": "1.1.1", + "CalldataVerificationFacet": "1.1.2", "StandardizedCallFacet": "1.1.0", - "EmergencyPauseFacet": "1.0.0", + "EmergencyPauseFacet": "1.0.1", "LiFiDiamond": "1.0.0", "ERC20Proxy": "1.0.0", "Executor": "2.0.0", @@ -1234,8 +1184,7 @@ "Receiver": "2.0.2", "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", - "LiFiDEXAggregator": "1.0.0", - "Permit2Proxy": "1.0.0" + "LiFiDEXAggregator": "1.0.0" } } }, @@ -1252,9 +1201,9 @@ "GenericSwapFacet": "1.0.0", "GenericSwapFacetV3": "1.0.1", "LIFuelFacet": "1.0.1", - "CalldataVerificationFacet": "1.1.1", + "CalldataVerificationFacet": "1.1.2", "StandardizedCallFacet": "1.1.0", - "EmergencyPauseFacet": "1.0.0", + "EmergencyPauseFacet": "1.0.1", "LiFiDiamond": "1.0.0", "ERC20Proxy": "1.0.0", "Executor": "2.0.0", @@ -1263,7 +1212,6 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", - "Permit2Proxy": "1.0.0", "CBridgeFacet": "1.0.0", "CBridgeFacetPacked": "1.0.3", "RelayerCelerIM": "2.0.0", @@ -1286,9 +1234,9 @@ "GenericSwapFacet": "1.0.0", "GenericSwapFacetV3": "1.0.1", "LIFuelFacet": "1.0.1", - "CalldataVerificationFacet": "1.1.1", + "CalldataVerificationFacet": "1.1.2", "StandardizedCallFacet": "1.1.0", - "EmergencyPauseFacet": "1.0.0", + "EmergencyPauseFacet": "1.0.1", "LiFiDiamond": "1.0.0", "ERC20Proxy": "1.0.0", "Executor": "2.0.0", @@ -1297,7 +1245,6 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", - "Permit2Proxy": "1.0.0", "CBridgeFacet": "1.0.0", "CBridgeFacetPacked": "1.0.3" } @@ -1316,17 +1263,16 @@ "GenericSwapFacet": "1.0.0", "GenericSwapFacetV3": "1.0.1", "LIFuelFacet": "1.0.1", - "CalldataVerificationFacet": "1.1.1", + "CalldataVerificationFacet": "1.1.2", "StandardizedCallFacet": "1.1.0", - "EmergencyPauseFacet": "1.0.0", + "EmergencyPauseFacet": "1.0.1", "LiFiDiamond": "1.0.0", "ERC20Proxy": "1.0.0", "Executor": "2.0.0", "FeeCollector": "1.0.0", "Receiver": "2.0.2", "LiFuelFeeCollector": "1.0.1", - "LiFiDEXAggregator": "1.0.0", - "Permit2Proxy": "1.0.0" + "LiFiDEXAggregator": "1.0.0" } } }, @@ -1343,9 +1289,9 @@ "GenericSwapFacet": "1.0.0", "GenericSwapFacetV3": "1.0.1", "LIFuelFacet": "1.0.1", - "CalldataVerificationFacet": "1.1.1", + "CalldataVerificationFacet": "1.1.2", "StandardizedCallFacet": "1.1.0", - "EmergencyPauseFacet": "1.0.0", + "EmergencyPauseFacet": "1.0.1", "LiFiDiamond": "1.0.0", "ERC20Proxy": "1.0.0", "Executor": "2.0.0", @@ -1354,7 +1300,6 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", - "Permit2Proxy": "1.0.0", "SymbiosisFacet": "1.0.0" } } @@ -1372,9 +1317,9 @@ "GenericSwapFacet": "1.0.0", "GenericSwapFacetV3": "1.0.1", "LIFuelFacet": "1.0.1", - "CalldataVerificationFacet": "1.1.1", + "CalldataVerificationFacet": "1.1.2", "StandardizedCallFacet": "1.1.0", - "EmergencyPauseFacet": "1.0.0", + "EmergencyPauseFacet": "1.0.1", "LiFiDiamond": "1.0.0", "ERC20Proxy": "1.0.0", "Executor": "2.0.0", @@ -1382,8 +1327,7 @@ "Receiver": "2.0.2", "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", - "LiFiDEXAggregator": "1.0.0", - "Permit2Proxy": "1.0.0" + "LiFiDEXAggregator": "1.0.0" } } }, @@ -1400,20 +1344,21 @@ "GenericSwapFacet": "1.0.0", "GenericSwapFacetV3": "1.0.1", "LIFuelFacet": "1.0.1", - "CalldataVerificationFacet": "1.1.1", + "CalldataVerificationFacet": "1.1.2", "StandardizedCallFacet": "1.1.0", - "EmergencyPauseFacet": "1.0.0", + "EmergencyPauseFacet": "1.0.1", "LiFiDiamond": "1.0.0", "ERC20Proxy": "1.0.0", "Executor": "2.0.0", "FeeCollector": "1.0.0", "Receiver": "2.0.2", "LiFuelFeeCollector": "1.0.1", + "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", - "Permit2Proxy": "1.0.0", "AcrossFacetV3": "1.0.0", "AcrossFacetPackedV3": "1.0.0", - "ReceiverAcrossV3": "1.0.0" + "ReceiverAcrossV3": "1.0.0", + "RelayFacet": "\u001b[31m[error] the following filepath is invalid: \u001b[31m[error] could not find src FILE path for contract RelayFacet\u001b[0m\u001b[0m" } } }, @@ -1430,17 +1375,17 @@ "GenericSwapFacet": "1.0.0", "GenericSwapFacetV3": "1.0.1", "LIFuelFacet": "1.0.1", - "CalldataVerificationFacet": "1.1.1", + "CalldataVerificationFacet": "1.1.2", "StandardizedCallFacet": "1.1.0", - "EmergencyPauseFacet": "1.0.0", + "EmergencyPauseFacet": "1.0.1", "LiFiDiamond": "1.0.0", "ERC20Proxy": "1.0.0", "Executor": "2.0.0", "FeeCollector": "1.0.0", "Receiver": "2.0.2", "LiFuelFeeCollector": "1.0.1", + "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", - "Permit2Proxy": "1.0.0", "AcrossFacetV3": "1.0.0", "AcrossFacetPackedV3": "1.0.0", "ReceiverAcrossV3": "1.0.0" @@ -1460,9 +1405,9 @@ "GenericSwapFacet": "1.0.0", "GenericSwapFacetV3": "1.0.1", "LIFuelFacet": "1.0.1", - "CalldataVerificationFacet": "1.1.1", + "CalldataVerificationFacet": "1.1.2", "StandardizedCallFacet": "1.1.0", - "EmergencyPauseFacet": "1.0.0", + "EmergencyPauseFacet": "1.0.1", "LiFiDiamond": "1.0.0", "ERC20Proxy": "1.0.0", "Executor": "2.0.0", @@ -1471,13 +1416,42 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", - "Permit2Proxy": "1.0.0", "AcrossFacetV3": "1.0.0", "AcrossFacetPackedV3": "1.0.0", "ReceiverAcrossV3": "1.0.0", + "RelayFacet": "\u001b[31m[error] the following filepath is invalid: \u001b[31m[error] could not find src FILE path for contract RelayFacet\u001b[0m\u001b[0m", "StargateFacetV2": "1.0.1", "ReceiverStargateV2": "1.0.1" } } + }, + "fraxtal": { + "production": { + "LiFiDiamond": { + "DiamondCutFacet": "1.0.0", + "DiamondLoupeFacet": "1.0.0", + "OwnershipFacet": "1.0.0", + "DexManagerFacet": "1.0.1", + "AccessManagerFacet": "1.0.0", + "WithdrawFacet": "1.0.0", + "PeripheryRegistryFacet": "1.0.0", + "GenericSwapFacet": "1.0.0", + "GenericSwapFacetV3": "1.0.1", + "LIFuelFacet": "1.0.1", + "CalldataVerificationFacet": "1.1.2", + "StandardizedCallFacet": "1.1.0", + "EmergencyPauseFacet": "1.0.1", + "LiFiDiamond": "1.0.0", + "ERC20Proxy": "1.0.0", + "Executor": "2.0.0", + "FeeCollector": "1.0.0", + "Receiver": "2.0.2", + "LiFuelFeeCollector": "1.0.1", + "TokenWrapper": "1.0.0", + "LiFiDEXAggregator": "1.0.0", + "SquidFacet": "1.0.0", + "SymbiosisFacet": "1.0.0" + } + } } } From 9c6f37baf0478af36cb4682dfeca60bf0110f6a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Mon, 25 Nov 2024 13:04:48 +0700 Subject: [PATCH 63/69] deployed to various PROD networks (excl zksync) --- config/networks.json | 4 +- deployments/_deployments_log_file.json | 98 +++++++++++++++++-- deployments/arbitrum.diamond.json | 5 +- deployments/arbitrum.json | 4 +- deployments/avalanche.diamond.json | 3 +- deployments/avalanche.json | 3 +- deployments/base.diamond.json | 3 +- deployments/base.json | 5 +- deployments/blast.diamond.json | 4 +- deployments/blast.json | 5 +- deployments/bsc.diamond.json | 4 +- deployments/bsc.json | 3 +- deployments/celo.diamond.json | 3 +- deployments/celo.json | 3 +- deployments/mainnet.diamond.json | 3 +- deployments/mainnet.json | 5 +- deployments/optimism.diamond.json | 2 +- deployments/optimism.json | 4 +- deployments/polygon.diamond.json | 5 +- deployments/polygon.json | 5 +- script/deploy/_targetState.json | 94 +++++++----------- script/deploy/deploySingleContract.sh | 6 +- script/deploy/safe/config.ts | 2 +- script/deploy/zksync/DeployPermit2Proxy.s.sol | 62 ++++++++++++ 24 files changed, 237 insertions(+), 98 deletions(-) create mode 100644 script/deploy/zksync/DeployPermit2Proxy.s.sol diff --git a/config/networks.json b/config/networks.json index ee9018e78..cbf583e05 100644 --- a/config/networks.json +++ b/config/networks.json @@ -102,9 +102,9 @@ "explorerUrl": "https://blastscan.io", "explorerApiUrl": "https://api.blastscan.io/api", "multicallAddress": "0xcA11bde05977b3631167028862bE2a173976CA11", - "safeApiUrl": "https://transaction.blast-safe.io/api", + "safeApiUrl": "https://safe-transaction-blast.safe.global/api", "safeAddress": "0xdf61270fDC1A892874Fd3C0143A0A4CBA74F4EF1", - "safeWebUrl": "https://blast-safe.io/transactions/queue?safe=blastmainnet:0xdf61270fDC1A892874Fd3C0143A0A4CBA74F4EF1", + "safeWebUrl": "https://app.safe.global/transactions/queue?safe=blast:0xdf61270fDC1A892874Fd3C0143A0A4CBA74F4EF1", "gasZipChainId": 96 }, "boba": { diff --git a/deployments/_deployments_log_file.json b/deployments/_deployments_log_file.json index 52eb75f9d..11109890d 100644 --- a/deployments/_deployments_log_file.json +++ b/deployments/_deployments_log_file.json @@ -24620,9 +24620,9 @@ "production": { "1.0.0": [ { - "ADDRESS": "0x5c6e208468213999CB0Be4bAB6d1492c3139A4EE", + "ADDRESS": "0x6307119078556Fc8aD77781DFC67df20d75FB4f9", "OPTIMIZER_RUNS": "1000000", - "TIMESTAMP": "2024-10-14 06:57:18", + "TIMESTAMP": "2024-11-25 11:24:27", "CONSTRUCTOR_ARGS": "0x0000000000000000000000001231deb6f5749ef6ce6943a275a1d3e7486f4eae000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba30000000000000000000000009e606d0d2bba344b911e2f4eab95d9235a83fe15", "SALT": "", "VERIFIED": "true" @@ -24634,9 +24634,9 @@ "production": { "1.0.0": [ { - "ADDRESS": "0x5c6e208468213999CB0Be4bAB6d1492c3139A4EE", + "ADDRESS": "0x6307119078556Fc8aD77781DFC67df20d75FB4f9", "OPTIMIZER_RUNS": "1000000", - "TIMESTAMP": "2024-10-14 06:55:51", + "TIMESTAMP": "2024-11-25 11:26:53", "CONSTRUCTOR_ARGS": "0x0000000000000000000000001231deb6f5749ef6ce6943a275a1d3e7486f4eae000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba30000000000000000000000008bcc385948c73736423d38cc567cfede0f1826a3", "SALT": "", "VERIFIED": "true" @@ -24660,15 +24660,99 @@ "production": { "1.0.0": [ { - "ADDRESS": "0x5c6e208468213999CB0Be4bAB6d1492c3139A4EE", + "ADDRESS": "0x6307119078556Fc8aD77781DFC67df20d75FB4f9", "OPTIMIZER_RUNS": "1000000", - "TIMESTAMP": "2024-10-14 06:58:45", + "TIMESTAMP": "2024-11-25 11:25:40", "CONSTRUCTOR_ARGS": "0x0000000000000000000000001231deb6f5749ef6ce6943a275a1d3e7486f4eae000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba3000000000000000000000000a8892ea3fddef2aa8afb1e3643a3284f978a5114", "SALT": "", "VERIFIED": "true" } ] } + }, + "mainnet": { + "production": { + "1.0.0": [ + { + "ADDRESS": "0x6307119078556Fc8aD77781DFC67df20d75FB4f9", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-11-25 11:10:40", + "CONSTRUCTOR_ARGS": "0x0000000000000000000000001231deb6f5749ef6ce6943a275a1d3e7486f4eae000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba300000000000000000000000037347dd595c49212c5fc2d95ea10d1085896f51e", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "avalanche": { + "production": { + "1.0.0": [ + { + "ADDRESS": "0x6307119078556Fc8aD77781DFC67df20d75FB4f9", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-11-25 12:15:16", + "CONSTRUCTOR_ARGS": "0x0000000000000000000000001231deb6f5749ef6ce6943a275a1d3e7486f4eae000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba300000000000000000000000027d4eb2854d93a1a7df8e2aed1a535b080a6f6e4", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "base": { + "production": { + "1.0.0": [ + { + "ADDRESS": "0x6307119078556Fc8aD77781DFC67df20d75FB4f9", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-11-25 12:26:53", + "CONSTRUCTOR_ARGS": "0x0000000000000000000000001231deb6f5749ef6ce6943a275a1d3e7486f4eae000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba30000000000000000000000001f6974c11b833eb52ea07e0b442510165d87d82e", + "SALT": "", + "VERIFIED": "false" + } + ] + } + }, + "blast": { + "production": { + "1.0.0": [ + { + "ADDRESS": "0x6307119078556Fc8aD77781DFC67df20d75FB4f9", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-11-25 12:38:05", + "CONSTRUCTOR_ARGS": "0x0000000000000000000000001231deb6f5749ef6ce6943a275a1d3e7486f4eae000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba3000000000000000000000000df61270fdc1a892874fd3c0143a0a4cba74f4ef1", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "bsc": { + "production": { + "1.0.0": [ + { + "ADDRESS": "0x6307119078556Fc8aD77781DFC67df20d75FB4f9", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-11-25 12:39:31", + "CONSTRUCTOR_ARGS": "0x0000000000000000000000001231deb6f5749ef6ce6943a275a1d3e7486f4eae000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba300000000000000000000000020b6b31d76e054c3e4de6154feca385ca58c7c15", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "celo": { + "production": { + "1.0.0": [ + { + "ADDRESS": "0x6307119078556Fc8aD77781DFC67df20d75FB4f9", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-11-25 13:03:21", + "CONSTRUCTOR_ARGS": "0x0000000000000000000000001231deb6f5749ef6ce6943a275a1d3e7486f4eae000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba3000000000000000000000000a89a87986e8ee1ac8fdacc5ac91627010ec9f772", + "SALT": "", + "VERIFIED": "true" + } + ] + } } } -} \ No newline at end of file +} diff --git a/deployments/arbitrum.diamond.json b/deployments/arbitrum.diamond.json index e83671922..df5253c01 100644 --- a/deployments/arbitrum.diamond.json +++ b/deployments/arbitrum.diamond.json @@ -156,7 +156,8 @@ "ReceiverAcrossV3": "0xB9CEc304899037E661F49DdFa7f64943b5920072", "ReceiverStargateV2": "0x1493e7B8d4DfADe0a178dAD9335470337A3a219A", "RelayerCelerIM": "0x6a8b11bF29C0546991DEcD6E0Db8cC7Fda22bA97", - "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d" + "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d", + "Permit2Proxy": "0x5c6e208468213999CB0Be4bAB6d1492c3139A4EE" } } -} +} \ No newline at end of file diff --git a/deployments/arbitrum.json b/deployments/arbitrum.json index 0cf7025c6..be4f46d1d 100644 --- a/deployments/arbitrum.json +++ b/deployments/arbitrum.json @@ -48,9 +48,9 @@ "StargateFacetV2": "0x6e378C84e657C57b2a8d183CFf30ee5CC8989b61", "ReceiverStargateV2": "0x1493e7B8d4DfADe0a178dAD9335470337A3a219A", "LiFiDEXAggregator": "0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc", - "Permit2Proxy": "0x5c6e208468213999CB0Be4bAB6d1492c3139A4EE", + "Permit2Proxy": "0x6307119078556Fc8aD77781DFC67df20d75FB4f9", "EmergencyPauseFacet": "0x6F2baA7cd5F156CA1B132F7FF11E0fa2aD775F61", "AcrossFacetV3": "0x6e00e0a7685Ca22c288d56D9E7924746B5043Ee7", "ReceiverAcrossV3": "0xB9CEc304899037E661F49DdFa7f64943b5920072", "AcrossFacetPackedV3": "0x20F3FFf5A89e988c4109A6e496a839480B1B558f" -} +} \ No newline at end of file diff --git a/deployments/avalanche.diamond.json b/deployments/avalanche.diamond.json index 2a38d0dbe..2472ab479 100644 --- a/deployments/avalanche.diamond.json +++ b/deployments/avalanche.diamond.json @@ -128,7 +128,8 @@ "ReceiverAcrossV3": "", "ReceiverStargateV2": "0x1493e7B8d4DfADe0a178dAD9335470337A3a219A", "RelayerCelerIM": "0x6a8b11bF29C0546991DEcD6E0Db8cC7Fda22bA97", - "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d" + "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d", + "Permit2Proxy": "" } } } \ No newline at end of file diff --git a/deployments/avalanche.json b/deployments/avalanche.json index c6378272c..07d97a104 100644 --- a/deployments/avalanche.json +++ b/deployments/avalanche.json @@ -45,5 +45,6 @@ "StargateFacetV2": "0x6e378C84e657C57b2a8d183CFf30ee5CC8989b61", "ReceiverStargateV2": "0x1493e7B8d4DfADe0a178dAD9335470337A3a219A", "LiFiDEXAggregator": "0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc", - "EmergencyPauseFacet": "0x6F2baA7cd5F156CA1B132F7FF11E0fa2aD775F61" + "EmergencyPauseFacet": "0x6F2baA7cd5F156CA1B132F7FF11E0fa2aD775F61", + "Permit2Proxy": "0x6307119078556Fc8aD77781DFC67df20d75FB4f9" } \ No newline at end of file diff --git a/deployments/base.diamond.json b/deployments/base.diamond.json index 994fa4e55..72cdf5e49 100644 --- a/deployments/base.diamond.json +++ b/deployments/base.diamond.json @@ -144,7 +144,8 @@ "ReceiverAcrossV3": "0xB9CEc304899037E661F49DdFa7f64943b5920072", "ReceiverStargateV2": "0x1493e7B8d4DfADe0a178dAD9335470337A3a219A", "RelayerCelerIM": "", - "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d" + "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d", + "Permit2Proxy": "" } } } \ No newline at end of file diff --git a/deployments/base.json b/deployments/base.json index 8291f7601..e239176b5 100644 --- a/deployments/base.json +++ b/deployments/base.json @@ -41,5 +41,6 @@ "EmergencyPauseFacet": "0x6F2baA7cd5F156CA1B132F7FF11E0fa2aD775F61", "AcrossFacetV3": "0x6e00e0a7685Ca22c288d56D9E7924746B5043Ee7", "ReceiverAcrossV3": "0xB9CEc304899037E661F49DdFa7f64943b5920072", - "AcrossFacetPackedV3": "0x20F3FFf5A89e988c4109A6e496a839480B1B558f" -} + "AcrossFacetPackedV3": "0x20F3FFf5A89e988c4109A6e496a839480B1B558f", + "Permit2Proxy": "0x6307119078556Fc8aD77781DFC67df20d75FB4f9" +} \ No newline at end of file diff --git a/deployments/blast.diamond.json b/deployments/blast.diamond.json index 24daaddb6..7b65cd88f 100644 --- a/deployments/blast.diamond.json +++ b/deployments/blast.diamond.json @@ -92,7 +92,9 @@ "ReceiverAcrossV3": "0xB9CEc304899037E661F49DdFa7f64943b5920072", "ReceiverStargateV2": "", "RelayerCelerIM": "", - "TokenWrapper": "0xF2ee649caB7a0edEdED7a27821B0aCDF77778aeD" + "TokenWrapper": "0xF2ee649caB7a0edEdED7a27821B0aCDF77778aeD", + "Permit2Proxy.flattened": "", + "Permit2Proxy": "" } } } \ No newline at end of file diff --git a/deployments/blast.json b/deployments/blast.json index 519ddd42b..7ef518cf1 100644 --- a/deployments/blast.json +++ b/deployments/blast.json @@ -28,5 +28,6 @@ "EmergencyPauseFacet": "0x6F2baA7cd5F156CA1B132F7FF11E0fa2aD775F61", "AcrossFacetV3": "0x6e00e0a7685Ca22c288d56D9E7924746B5043Ee7", "ReceiverAcrossV3": "0xB9CEc304899037E661F49DdFa7f64943b5920072", - "AcrossFacetPackedV3": "0x20F3FFf5A89e988c4109A6e496a839480B1B558f" -} + "AcrossFacetPackedV3": "0x20F3FFf5A89e988c4109A6e496a839480B1B558f", + "Permit2Proxy": "0x6307119078556Fc8aD77781DFC67df20d75FB4f9" +} \ No newline at end of file diff --git a/deployments/bsc.diamond.json b/deployments/bsc.diamond.json index ebbff036e..78490b364 100644 --- a/deployments/bsc.diamond.json +++ b/deployments/bsc.diamond.json @@ -132,7 +132,9 @@ "ReceiverAcrossV3": "", "ReceiverStargateV2": "0x1493e7B8d4DfADe0a178dAD9335470337A3a219A", "RelayerCelerIM": "0x6a8b11bF29C0546991DEcD6E0Db8cC7Fda22bA97", - "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d" + "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d", + "Permit2Proxy.flattened": "", + "Permit2Proxy": "" } } } \ No newline at end of file diff --git a/deployments/bsc.json b/deployments/bsc.json index b875005f3..87d20f70a 100644 --- a/deployments/bsc.json +++ b/deployments/bsc.json @@ -46,5 +46,6 @@ "ReceiverStargateV2": "0x1493e7B8d4DfADe0a178dAD9335470337A3a219A", "StargateFacetV2": "0x6e378C84e657C57b2a8d183CFf30ee5CC8989b61", "LiFiDEXAggregator": "0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc", - "EmergencyPauseFacet": "0x6F2baA7cd5F156CA1B132F7FF11E0fa2aD775F61" + "EmergencyPauseFacet": "0x6F2baA7cd5F156CA1B132F7FF11E0fa2aD775F61", + "Permit2Proxy": "0x6307119078556Fc8aD77781DFC67df20d75FB4f9" } \ No newline at end of file diff --git a/deployments/celo.diamond.json b/deployments/celo.diamond.json index 1a3abf9e4..aba8c1a2e 100644 --- a/deployments/celo.diamond.json +++ b/deployments/celo.diamond.json @@ -76,7 +76,8 @@ "ReceiverAcrossV3": "", "ReceiverStargateV2": "", "RelayerCelerIM": "", - "TokenWrapper": "0xF2ee649caB7a0edEdED7a27821B0aCDF77778aeD" + "TokenWrapper": "0xF2ee649caB7a0edEdED7a27821B0aCDF77778aeD", + "Permit2Proxy": "" } } } \ No newline at end of file diff --git a/deployments/celo.json b/deployments/celo.json index d0980da36..baa221a3c 100644 --- a/deployments/celo.json +++ b/deployments/celo.json @@ -24,5 +24,6 @@ "AllBridgeFacet": "0x222b97E533220A47ad8700C396537D58e001808C", "GenericSwapFacetV3": "0xD8066332e52A210f7E6BD8F8D4F2be4197BE290D", "LiFiDEXAggregator": "0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc", - "EmergencyPauseFacet": "0x6F2baA7cd5F156CA1B132F7FF11E0fa2aD775F61" + "EmergencyPauseFacet": "0x6F2baA7cd5F156CA1B132F7FF11E0fa2aD775F61", + "Permit2Proxy": "0x6307119078556Fc8aD77781DFC67df20d75FB4f9" } \ No newline at end of file diff --git a/deployments/mainnet.diamond.json b/deployments/mainnet.diamond.json index 8428ec9f0..49b94fa36 100644 --- a/deployments/mainnet.diamond.json +++ b/deployments/mainnet.diamond.json @@ -180,7 +180,8 @@ "ReceiverAcrossV3": "0xB9CEc304899037E661F49DdFa7f64943b5920072", "ReceiverStargateV2": "0x1493e7B8d4DfADe0a178dAD9335470337A3a219A", "RelayerCelerIM": "0x6a8b11bF29C0546991DEcD6E0Db8cC7Fda22bA97", - "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d" + "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d", + "Permit2Proxy": "" } } } \ No newline at end of file diff --git a/deployments/mainnet.json b/deployments/mainnet.json index d28177b4c..9e2949597 100644 --- a/deployments/mainnet.json +++ b/deployments/mainnet.json @@ -59,5 +59,6 @@ "EmergencyPauseFacet": "0x6F2baA7cd5F156CA1B132F7FF11E0fa2aD775F61", "AcrossFacetV3": "0x6e00e0a7685Ca22c288d56D9E7924746B5043Ee7", "ReceiverAcrossV3": "0xB9CEc304899037E661F49DdFa7f64943b5920072", - "AcrossFacetPackedV3": "0x20F3FFf5A89e988c4109A6e496a839480B1B558f" -} + "AcrossFacetPackedV3": "0x20F3FFf5A89e988c4109A6e496a839480B1B558f", + "Permit2Proxy": "0x6307119078556Fc8aD77781DFC67df20d75FB4f9" +} \ No newline at end of file diff --git a/deployments/optimism.diamond.json b/deployments/optimism.diamond.json index 8077a5a94..baa417e23 100644 --- a/deployments/optimism.diamond.json +++ b/deployments/optimism.diamond.json @@ -160,4 +160,4 @@ "Permit2Proxy": "0x5c6e208468213999CB0Be4bAB6d1492c3139A4EE" } } -} +} \ No newline at end of file diff --git a/deployments/optimism.json b/deployments/optimism.json index 87ac853a4..1d43fc913 100644 --- a/deployments/optimism.json +++ b/deployments/optimism.json @@ -47,9 +47,9 @@ "StargateFacetV2": "0x6e378C84e657C57b2a8d183CFf30ee5CC8989b61", "ReceiverStargateV2": "0x1493e7B8d4DfADe0a178dAD9335470337A3a219A", "LiFiDEXAggregator": "0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc", - "Permit2Proxy": "0x5c6e208468213999CB0Be4bAB6d1492c3139A4EE", + "Permit2Proxy": "0x6307119078556Fc8aD77781DFC67df20d75FB4f9", "EmergencyPauseFacet": "0x6F2baA7cd5F156CA1B132F7FF11E0fa2aD775F61", "AcrossFacetV3": "0x6e00e0a7685Ca22c288d56D9E7924746B5043Ee7", "ReceiverAcrossV3": "0xB9CEc304899037E661F49DdFa7f64943b5920072", "AcrossFacetPackedV3": "0x20F3FFf5A89e988c4109A6e496a839480B1B558f" -} +} \ No newline at end of file diff --git a/deployments/polygon.diamond.json b/deployments/polygon.diamond.json index 5d7a15b37..d40b744a7 100644 --- a/deployments/polygon.diamond.json +++ b/deployments/polygon.diamond.json @@ -156,7 +156,8 @@ "ReceiverAcrossV3": "0xB9CEc304899037E661F49DdFa7f64943b5920072", "ReceiverStargateV2": "0x1493e7B8d4DfADe0a178dAD9335470337A3a219A", "RelayerCelerIM": "0x6a8b11bF29C0546991DEcD6E0Db8cC7Fda22bA97", - "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d" + "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d", + "Permit2Proxy": "0x5c6e208468213999CB0Be4bAB6d1492c3139A4EE" } } -} +} \ No newline at end of file diff --git a/deployments/polygon.json b/deployments/polygon.json index 0a8e98c7f..63741bcb8 100644 --- a/deployments/polygon.json +++ b/deployments/polygon.json @@ -54,5 +54,6 @@ "EmergencyPauseFacet": "0x6F2baA7cd5F156CA1B132F7FF11E0fa2aD775F61", "AcrossFacetV3": "0x6e00e0a7685Ca22c288d56D9E7924746B5043Ee7", "ReceiverAcrossV3": "0xB9CEc304899037E661F49DdFa7f64943b5920072", - "AcrossFacetPackedV3": "0x20F3FFf5A89e988c4109A6e496a839480B1B558f" -} + "AcrossFacetPackedV3": "0x20F3FFf5A89e988c4109A6e496a839480B1B558f", + "Permit2Proxy": "0x6307119078556Fc8aD77781DFC67df20d75FB4f9" +} \ No newline at end of file diff --git a/script/deploy/_targetState.json b/script/deploy/_targetState.json index 3a8341d36..189be8d69 100644 --- a/script/deploy/_targetState.json +++ b/script/deploy/_targetState.json @@ -131,6 +131,7 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", "AllBridgeFacet": "2.0.0", "AmarokFacet": "3.0.0", "AmarokFacetPacked": "1.0.0", @@ -241,6 +242,7 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", "AllBridgeFacet": "2.0.0", "CBridgeFacet": "1.0.0", "CBridgeFacetPacked": "1.0.3", @@ -508,36 +510,7 @@ } } }, - "arbitrumnova": { - "production": { - "LiFiDiamond": { - "DiamondCutFacet": "1.0.0", - "DiamondLoupeFacet": "1.0.0", - "OwnershipFacet": "1.0.0", - "DexManagerFacet": "1.0.1", - "AccessManagerFacet": "1.0.0", - "WithdrawFacet": "1.0.0", - "PeripheryRegistryFacet": "1.0.0", - "GenericSwapFacet": "1.0.0", - "GenericSwapFacetV3": "1.0.1", - "LIFuelFacet": "1.0.1", - "CalldataVerificationFacet": "1.1.2", - "StandardizedCallFacet": "1.1.0", - "EmergencyPauseFacet": "1.0.1", - "LiFiDiamond": "1.0.0", - "ERC20Proxy": "1.0.0", - "Executor": "2.0.0", - "FeeCollector": "1.0.0", - "Receiver": "2.0.2", - "LiFuelFeeCollector": "1.0.1", - "TokenWrapper": "1.0.0", - "LiFiDEXAggregator": "1.0.0", - "RelayerCelerIM": "2.0.0", - "CelerIMFacetMutable": "2.0.0", - "SymbiosisFacet": "1.0.0" - } - } - }, + "arbitrumnova": {}, "zksync": { "production": { "LiFiDiamond": { @@ -562,6 +535,7 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", "AcrossFacet": "2.0.0", "AcrossFacetPacked": "1.0.0", "AcrossFacetV3": "1.0.0", @@ -671,6 +645,7 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", "AcrossFacet": "2.0.0", "AcrossFacetPacked": "1.0.0", "AcrossFacetV3": "1.0.0", @@ -911,6 +886,7 @@ "LiFuelFeeCollector": "1.0.1", "TokenWrapper": "1.0.0", "LiFiDEXAggregator": "1.0.0", + "Permit2Proxy": "1.0.0", "AcrossFacet": "2.0.0", "AcrossFacetPacked": "1.0.0", "AcrossFacetV3": "1.0.0", @@ -924,7 +900,34 @@ } } }, - "rootstock": {}, + "rootstock": { + "production": { + "LiFiDiamond": { + "DiamondCutFacet": "1.0.0", + "DiamondLoupeFacet": "1.0.0", + "OwnershipFacet": "1.0.0", + "DexManagerFacet": "1.0.1", + "AccessManagerFacet": "1.0.0", + "WithdrawFacet": "1.0.0", + "PeripheryRegistryFacet": "1.0.0", + "GenericSwapFacet": "1.0.0", + "GenericSwapFacetV3": "1.0.1", + "LIFuelFacet": "1.0.1", + "CalldataVerificationFacet": "1.1.2", + "StandardizedCallFacet": "1.1.0", + "EmergencyPauseFacet": "1.0.1", + "LiFiDiamond": "1.0.0", + "ERC20Proxy": "1.0.0", + "Executor": "2.0.0", + "FeeCollector": "1.0.0", + "Receiver": "2.0.2", + "LiFuelFeeCollector": "1.0.1", + "TokenWrapper": "1.0.0", + "LiFiDEXAggregator": "1.0.0", + "SymbiosisFacet": "1.0.0" + } + } + }, "sei": { "production": { "LiFiDiamond": { @@ -1276,34 +1279,7 @@ } } }, - "rootstocks": { - "production": { - "LiFiDiamond": { - "DiamondCutFacet": "1.0.0", - "DiamondLoupeFacet": "1.0.0", - "OwnershipFacet": "1.0.0", - "DexManagerFacet": "1.0.1", - "AccessManagerFacet": "1.0.0", - "WithdrawFacet": "1.0.0", - "PeripheryRegistryFacet": "1.0.0", - "GenericSwapFacet": "1.0.0", - "GenericSwapFacetV3": "1.0.1", - "LIFuelFacet": "1.0.1", - "CalldataVerificationFacet": "1.1.2", - "StandardizedCallFacet": "1.1.0", - "EmergencyPauseFacet": "1.0.1", - "LiFiDiamond": "1.0.0", - "ERC20Proxy": "1.0.0", - "Executor": "2.0.0", - "FeeCollector": "1.0.0", - "Receiver": "2.0.2", - "LiFuelFeeCollector": "1.0.1", - "TokenWrapper": "1.0.0", - "LiFiDEXAggregator": "1.0.0", - "SymbiosisFacet": "1.0.0" - } - } - }, + "rootstocks": {}, "cronos": { "production": { "LiFiDiamond": { diff --git a/script/deploy/deploySingleContract.sh b/script/deploy/deploySingleContract.sh index 3209b0a49..15449095d 100755 --- a/script/deploy/deploySingleContract.sh +++ b/script/deploy/deploySingleContract.sh @@ -96,7 +96,7 @@ deploySingleContract() { if [[ $NETWORK == "zksync" ]]; then DEPLOY_SCRIPT_DIRECTORY="script/deploy/zksync/" fi - + if [[ -z "$CONTRACT" ]]; then # get user-selected deploy script and contract from list SCRIPT=$(ls -1 "$DEPLOY_SCRIPT_DIRECTORY" | sed -e 's/\.s.sol$//' | grep 'Deploy' | gum filter --placeholder "Deploy Script") @@ -225,10 +225,10 @@ deploySingleContract() { if [[ $NETWORK == "zksync" ]]; then # Deploy zksync scripts using the zksync specific fork of forge from Docker - RAW_RETURN_DATA=$(docker run --rm -it --volume .:/foundry -u $(id -u):$(id -g) -e FOUNDRY_PROFILE=zksync -e DEPLOYSALT=$DEPLOYSALT -e NETWORK=$NETWORK -e FILE_SUFFIX=$FILE_SUFFIX -e PRIVATE_KEY=$(getPrivateKey "$NETWORK" "$ENVIRONMENT") foundry-zksync forge script "$FULL_SCRIPT_PATH" -f $NETWORK --json --silent --broadcast --skip-simulation --slow --zksync) + RAW_RETURN_DATA=$(docker run --rm -it --volume .:/foundry -u $(id -u):$(id -g) -e FOUNDRY_PROFILE=zksync -e DEPLOYSALT=$DEPLOYSALT -e NETWORK=$NETWORK -e FILE_SUFFIX=$FILE_SUFFIX -e PRIVATE_KEY=$(getPrivateKey "$NETWORK" "$ENVIRONMENT") foundry-zksync forge script "$FULL_SCRIPT_PATH" -f $NETWORK --json --silent --broadcast --skip-simulation --slow --zksync) else # try to execute call - RAW_RETURN_DATA=$(DEPLOYSALT=$DEPLOYSALT CREATE3_FACTORY_ADDRESS=$CREATE3_FACTORY_ADDRESS NETWORK=$NETWORK FILE_SUFFIX=$FILE_SUFFIX DEFAULT_DIAMOND_ADDRESS_DEPLOYSALT=$DEFAULT_DIAMOND_ADDRESS_DEPLOYSALT DEPLOY_TO_DEFAULT_DIAMOND_ADDRESS=$DEPLOY_TO_DEFAULT_DIAMOND_ADDRESS PRIVATE_KEY=$(getPrivateKey "$NETWORK" "$ENVIRONMENT") DIAMOND_TYPE=$DIAMOND_TYPE forge script "$FULL_SCRIPT_PATH" -f $NETWORK -vvvvvv --json --silent --broadcast --skip-simulation --legacy) + RAW_RETURN_DATA=$(DEPLOYSALT=$DEPLOYSALT CREATE3_FACTORY_ADDRESS=$CREATE3_FACTORY_ADDRESS NETWORK=$NETWORK FILE_SUFFIX=$FILE_SUFFIX DEFAULT_DIAMOND_ADDRESS_DEPLOYSALT=$DEFAULT_DIAMOND_ADDRESS_DEPLOYSALT DEPLOY_TO_DEFAULT_DIAMOND_ADDRESS=$DEPLOY_TO_DEFAULT_DIAMOND_ADDRESS PRIVATE_KEY=$(getPrivateKey "$NETWORK" "$ENVIRONMENT") DIAMOND_TYPE=$DIAMOND_TYPE forge script "$FULL_SCRIPT_PATH" -f $NETWORK --json --broadcast --skip-simulation --legacy) fi RETURN_CODE=$? diff --git a/script/deploy/safe/config.ts b/script/deploy/safe/config.ts index fb126af69..4cd557907 100644 --- a/script/deploy/safe/config.ts +++ b/script/deploy/safe/config.ts @@ -6,7 +6,7 @@ export const safeApiUrls: Record = { aurora: 'https://safe-transaction-aurora.safe.global/api', avalanche: 'https://safe-transaction-avalanche.safe.global/api', base: 'https://safe-transaction-base.safe.global/api', - blast: 'https://transaction.blast-safe.io/api', + blast: 'https://safe-transaction-blast.safe.global/api', boba: 'https://safe-transaction.mainnet.boba.network/api', bsc: 'https://safe-transaction-bsc.safe.global/api', celo: 'https://safe-transaction-celo.safe.global/api', diff --git a/script/deploy/zksync/DeployPermit2Proxy.s.sol b/script/deploy/zksync/DeployPermit2Proxy.s.sol new file mode 100644 index 000000000..d1553cd3e --- /dev/null +++ b/script/deploy/zksync/DeployPermit2Proxy.s.sol @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.17; + +import { DeployScriptBase } from "./utils/DeployScriptBase.sol"; +import { Permit2Proxy } from "lifi/Periphery/Permit2Proxy.sol"; +import { stdJson } from "forge-std/Script.sol"; + +contract DeployScript is DeployScriptBase { + using stdJson for string; + + constructor() DeployScriptBase("Permit2Proxy") {} + + function run() + public + returns (Permit2Proxy deployed, bytes memory constructorArgs) + { + constructorArgs = getConstructorArgs(); + + deployed = Permit2Proxy(deploy(type(Permit2Proxy).creationCode)); + } + + function getConstructorArgs() internal override returns (bytes memory) { + // get the address of the LiFiDiamond for the given network + string memory deployments = string.concat( + root, + "/deployments/", + network, + ".", + fileSuffix, + "json" + ); + string memory deploymentsJSON = vm.readFile(deployments); + + address diamond = deploymentsJSON.readAddress(".LiFiDiamond"); + + // get the Permit2 contract address for the given network + string memory permit2ProxyConfig = string.concat( + root, + "/config/permit2Proxy.json" + ); + + string memory permit2ProxyConfigJSON = vm.readFile(permit2ProxyConfig); + + address permit2Address = permit2ProxyConfigJSON.readAddress( + string.concat(".", network) + ); + + // get the multisig SAFE address for the given network + string memory networksConfig = string.concat( + root, + "/config/networks.json" + ); + + string memory networksConfigJSON = vm.readFile(networksConfig); + + address safeAddress = networksConfigJSON.readAddress( + string.concat(".", network, ".safeAddress") + ); + + return abi.encode(diamond, permit2Address, safeAddress); + } +} From 4e02a155a731f59480bcbf587517b9065fc52985 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Mon, 25 Nov 2024 16:57:01 +0700 Subject: [PATCH 64/69] verified Permit2Proxy on base --- deployments/_deployments_log_file.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployments/_deployments_log_file.json b/deployments/_deployments_log_file.json index 11109890d..a5d539c5d 100644 --- a/deployments/_deployments_log_file.json +++ b/deployments/_deployments_log_file.json @@ -24707,7 +24707,7 @@ "TIMESTAMP": "2024-11-25 12:26:53", "CONSTRUCTOR_ARGS": "0x0000000000000000000000001231deb6f5749ef6ce6943a275a1d3e7486f4eae000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba30000000000000000000000001f6974c11b833eb52ea07e0b442510165d87d82e", "SALT": "", - "VERIFIED": "false" + "VERIFIED": "true" } ] } From 3794dbe02cd499960ed1834a04d62818b21c0b1c Mon Sep 17 00:00:00 2001 From: Ed Zynda Date: Mon, 25 Nov 2024 14:11:39 +0300 Subject: [PATCH 65/69] deploy to zksync --- deployments/_deployments_log_file.json | 14 ++++++++++++++ deployments/zksync.json | 5 +++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/deployments/_deployments_log_file.json b/deployments/_deployments_log_file.json index a5d539c5d..28db164a6 100644 --- a/deployments/_deployments_log_file.json +++ b/deployments/_deployments_log_file.json @@ -24753,6 +24753,20 @@ } ] } + }, + "zksync": { + "production": { + "1.0.0": [ + { + "ADDRESS": "0x6275f6631c955DC5dA9fBe8Dc7f24a3A5919443A", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-11-25 13:42:32", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000341e94069f53234fe6dabef707ad4248305257150000000000000000000000000000000000225e31d15943971f47ad3022f714fa00000000000000000000000002f1272aeacaf7bd8b30278bc2aa381cc623a744", + "SALT": "", + "VERIFIED": "true" + } + ] + } } } } diff --git a/deployments/zksync.json b/deployments/zksync.json index 01317c553..5642cdaba 100644 --- a/deployments/zksync.json +++ b/deployments/zksync.json @@ -22,5 +22,6 @@ "LiFiDEXAggregator": "0x1F683faf1E2a770aa75f7B2e92117A5c11183E9C", "AcrossFacetV3": "0x2e47355B70D6935C6A69d5F67e0aFe437791138e", "ReceiverAcrossV3": "0xFa94c1A99799B3cA89DE6cbB3ccCDEcf1da62aFE", - "AcrossFacetPackedV3": "0x9243578F60a2A3821642481b5851578cE92d9a78" -} + "AcrossFacetPackedV3": "0x9243578F60a2A3821642481b5851578cE92d9a78", + "Permit2Proxy": "0x6275f6631c955DC5dA9fBe8Dc7f24a3A5919443A" +} \ No newline at end of file From 30e9704327b48fb01dddc3ba7965cb5638f48f33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Mon, 25 Nov 2024 21:28:54 +0700 Subject: [PATCH 66/69] diamond logs updated --- deployments/arbitrum.diamond.json | 8 +- deployments/aurora.diamond.json | 19 +-- deployments/avalanche.diamond.json | 8 +- deployments/base.diamond.json | 12 +- deployments/blast.diamond.json | 7 +- deployments/boba.diamond.json | 1 + deployments/bsc.diamond.json | 9 +- deployments/celo.diamond.json | 4 +- deployments/cronos.diamond.json | 15 +-- deployments/fantom.diamond.json | 5 + deployments/fraxtal.diamond.json | 1 + deployments/fuse.diamond.json | 1 + deployments/gnosis.diamond.json | 5 + deployments/gravity.diamond.json | 13 +++ deployments/immutablezkevm.diamond.json | 1 + deployments/kaia.diamond.json | 1 + deployments/linea.diamond.json | 15 ++- deployments/mainnet.diamond.json | 12 +- deployments/mantle.diamond.json | 9 ++ deployments/metis.diamond.json | 5 + deployments/mode.diamond.json | 11 +- deployments/moonbeam.diamond.json | 1 + deployments/moonriver.diamond.json | 1 + deployments/opbnb.diamond.json | 3 +- deployments/optimism.diamond.json | 12 +- deployments/polygon.diamond.json | 12 +- deployments/polygonzkevm.diamond.json | 1 + deployments/rootstock.diamond.json | 1 + deployments/scroll.diamond.json | 11 +- deployments/sei.diamond.json | 1 + deployments/taiko.diamond.json | 1 + deployments/worldchain.diamond.json | 149 ++++++++++++------------ deployments/xlayer.diamond.json | 5 + deployments/zksync.diamond.json | 9 +- 34 files changed, 253 insertions(+), 116 deletions(-) diff --git a/deployments/arbitrum.diamond.json b/deployments/arbitrum.diamond.json index df5253c01..da1f6b8e2 100644 --- a/deployments/arbitrum.diamond.json +++ b/deployments/arbitrum.diamond.json @@ -144,6 +144,10 @@ "0x6F2baA7cd5F156CA1B132F7FF11E0fa2aD775F61": { "Name": "EmergencyPauseFacet", "Version": "1.0.0" + }, + "0xF5c923a087fb3c554579e2DD10AB6E37E0f6F849": { + "Name": "", + "Version": "" } }, "Periphery": { @@ -152,12 +156,12 @@ "FeeCollector": "0xB0210dE78E28e2633Ca200609D9f528c13c26cD9", "LiFiDEXAggregator": "0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc", "LiFuelFeeCollector": "0xc02FFcdD914DbA646704439c6090BAbaD521d04C", + "Permit2Proxy": "0x6307119078556Fc8aD77781DFC67df20d75FB4f9", "Receiver": "0x050e198E36A73a1e32F15C3afC58C4506d82f657", "ReceiverAcrossV3": "0xB9CEc304899037E661F49DdFa7f64943b5920072", "ReceiverStargateV2": "0x1493e7B8d4DfADe0a178dAD9335470337A3a219A", "RelayerCelerIM": "0x6a8b11bF29C0546991DEcD6E0Db8cC7Fda22bA97", - "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d", - "Permit2Proxy": "0x5c6e208468213999CB0Be4bAB6d1492c3139A4EE" + "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d" } } } \ No newline at end of file diff --git a/deployments/aurora.diamond.json b/deployments/aurora.diamond.json index 16e7d04dc..b1843e7eb 100644 --- a/deployments/aurora.diamond.json +++ b/deployments/aurora.diamond.json @@ -60,18 +60,23 @@ "0x6e378C84e657C57b2a8d183CFf30ee5CC8989b61": { "Name": "StargateFacetV2", "Version": "1.0.1" + }, + "0x6F2baA7cd5F156CA1B132F7FF11E0fa2aD775F61": { + "Name": "EmergencyPauseFacet", + "Version": "1.0.0" } }, "Periphery": { - "ERC20Proxy": "", - "Executor": "", + "ERC20Proxy": "0x5741A7FfE7c39Ca175546a54985fA79211290b51", + "Executor": "0x2dfaDAB8266483beD9Fd9A292Ce56596a2D1378D", "FeeCollector": "0xB0210dE78E28e2633Ca200609D9f528c13c26cD9", - "LiFiDEXAggregator": "", - "LiFuelFeeCollector": "", - "Receiver": "0x5439f8ca43f832DD21a28C5BF038dad4c07ad02c", + "LiFiDEXAggregator": "0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc", + "LiFuelFeeCollector": "0x8E92e662573CBC66B0AB6A93B5FE291925508085", + "Permit2Proxy": "", + "Receiver": "", "ReceiverAcrossV3": "", - "ReceiverStargateV2": "", - "RelayerCelerIM": "", + "ReceiverStargateV2": "0x1493e7B8d4DfADe0a178dAD9335470337A3a219A", + "RelayerCelerIM": "0x6a8b11bF29C0546991DEcD6E0Db8cC7Fda22bA97", "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d" } } diff --git a/deployments/avalanche.diamond.json b/deployments/avalanche.diamond.json index 2472ab479..44cc0e583 100644 --- a/deployments/avalanche.diamond.json +++ b/deployments/avalanche.diamond.json @@ -116,6 +116,10 @@ "0x6F2baA7cd5F156CA1B132F7FF11E0fa2aD775F61": { "Name": "EmergencyPauseFacet", "Version": "1.0.0" + }, + "0xF5c923a087fb3c554579e2DD10AB6E37E0f6F849": { + "Name": "", + "Version": "" } }, "Periphery": { @@ -124,12 +128,12 @@ "FeeCollector": "0xB0210dE78E28e2633Ca200609D9f528c13c26cD9", "LiFiDEXAggregator": "0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc", "LiFuelFeeCollector": "0xc02FFcdD914DbA646704439c6090BAbaD521d04C", + "Permit2Proxy": "0x6307119078556Fc8aD77781DFC67df20d75FB4f9", "Receiver": "0x050e198E36A73a1e32F15C3afC58C4506d82f657", "ReceiverAcrossV3": "", "ReceiverStargateV2": "0x1493e7B8d4DfADe0a178dAD9335470337A3a219A", "RelayerCelerIM": "0x6a8b11bF29C0546991DEcD6E0Db8cC7Fda22bA97", - "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d", - "Permit2Proxy": "" + "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d" } } } \ No newline at end of file diff --git a/deployments/base.diamond.json b/deployments/base.diamond.json index 72cdf5e49..1a4cfab47 100644 --- a/deployments/base.diamond.json +++ b/deployments/base.diamond.json @@ -132,6 +132,14 @@ "0x6F2baA7cd5F156CA1B132F7FF11E0fa2aD775F61": { "Name": "EmergencyPauseFacet", "Version": "1.0.0" + }, + "0xF5c923a087fb3c554579e2DD10AB6E37E0f6F849": { + "Name": "", + "Version": "" + }, + "0x6e00e0a7685Ca22c288d56D9E7924746B5043Ee7": { + "Name": "AcrossFacetV3", + "Version": "1.0.0" } }, "Periphery": { @@ -140,12 +148,12 @@ "FeeCollector": "0x0A6d96E7f4D7b96CFE42185DF61E64d255c12DFf", "LiFiDEXAggregator": "0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc", "LiFuelFeeCollector": "0xc02FFcdD914DbA646704439c6090BAbaD521d04C", + "Permit2Proxy": "0x6307119078556Fc8aD77781DFC67df20d75FB4f9", "Receiver": "0xeC03B65CbDc5f8858b02F44EBa54C90664249fb1", "ReceiverAcrossV3": "0xB9CEc304899037E661F49DdFa7f64943b5920072", "ReceiverStargateV2": "0x1493e7B8d4DfADe0a178dAD9335470337A3a219A", "RelayerCelerIM": "", - "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d", - "Permit2Proxy": "" + "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d" } } } \ No newline at end of file diff --git a/deployments/blast.diamond.json b/deployments/blast.diamond.json index 7b65cd88f..872130e8c 100644 --- a/deployments/blast.diamond.json +++ b/deployments/blast.diamond.json @@ -73,7 +73,7 @@ "Name": "AcrossFacetPackedV3", "Version": "1.0.0" }, - "0x00990C0FfBB7eAB014351652aFB65AaE00db43A4": { + "0xF5c923a087fb3c554579e2DD10AB6E37E0f6F849": { "Name": "", "Version": "" }, @@ -88,13 +88,12 @@ "FeeCollector": "0xF048e5816B0C7951AC179f656C5B86e5a79Bd7b5", "LiFiDEXAggregator": "0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc", "LiFuelFeeCollector": "0xc02FFcdD914DbA646704439c6090BAbaD521d04C", + "Permit2Proxy": "0x6307119078556Fc8aD77781DFC67df20d75FB4f9", "Receiver": "0x0561fFe9855541C02D17951c93405A4407Df74BC", "ReceiverAcrossV3": "0xB9CEc304899037E661F49DdFa7f64943b5920072", "ReceiverStargateV2": "", "RelayerCelerIM": "", - "TokenWrapper": "0xF2ee649caB7a0edEdED7a27821B0aCDF77778aeD", - "Permit2Proxy.flattened": "", - "Permit2Proxy": "" + "TokenWrapper": "0xF2ee649caB7a0edEdED7a27821B0aCDF77778aeD" } } } \ No newline at end of file diff --git a/deployments/boba.diamond.json b/deployments/boba.diamond.json index f9a009d95..6f989e752 100644 --- a/deployments/boba.diamond.json +++ b/deployments/boba.diamond.json @@ -72,6 +72,7 @@ "FeeCollector": "0xB0210dE78E28e2633Ca200609D9f528c13c26cD9", "LiFiDEXAggregator": "0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc", "LiFuelFeeCollector": "0xc02FFcdD914DbA646704439c6090BAbaD521d04C", + "Permit2Proxy": "", "Receiver": "0x5439f8ca43f832DD21a28C5BF038dad4c07ad02c", "ReceiverAcrossV3": "", "ReceiverStargateV2": "", diff --git a/deployments/bsc.diamond.json b/deployments/bsc.diamond.json index 78490b364..8a9a182c4 100644 --- a/deployments/bsc.diamond.json +++ b/deployments/bsc.diamond.json @@ -120,6 +120,10 @@ "0x6F2baA7cd5F156CA1B132F7FF11E0fa2aD775F61": { "Name": "EmergencyPauseFacet", "Version": "1.0.0" + }, + "0xF5c923a087fb3c554579e2DD10AB6E37E0f6F849": { + "Name": "", + "Version": "" } }, "Periphery": { @@ -128,13 +132,12 @@ "FeeCollector": "0xbD6C7B0d2f68c2b7805d88388319cfB6EcB50eA9", "LiFiDEXAggregator": "0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc", "LiFuelFeeCollector": "0xc02FFcdD914DbA646704439c6090BAbaD521d04C", + "Permit2Proxy": "0x6307119078556Fc8aD77781DFC67df20d75FB4f9", "Receiver": "0x050e198E36A73a1e32F15C3afC58C4506d82f657", "ReceiverAcrossV3": "", "ReceiverStargateV2": "0x1493e7B8d4DfADe0a178dAD9335470337A3a219A", "RelayerCelerIM": "0x6a8b11bF29C0546991DEcD6E0Db8cC7Fda22bA97", - "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d", - "Permit2Proxy.flattened": "", - "Permit2Proxy": "" + "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d" } } } \ No newline at end of file diff --git a/deployments/celo.diamond.json b/deployments/celo.diamond.json index aba8c1a2e..8805dfad4 100644 --- a/deployments/celo.diamond.json +++ b/deployments/celo.diamond.json @@ -72,12 +72,12 @@ "FeeCollector": "0xF048e5816B0C7951AC179f656C5B86e5a79Bd7b5", "LiFiDEXAggregator": "0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc", "LiFuelFeeCollector": "0xc02FFcdD914DbA646704439c6090BAbaD521d04C", + "Permit2Proxy": "0x6307119078556Fc8aD77781DFC67df20d75FB4f9", "Receiver": "0x0561fFe9855541C02D17951c93405A4407Df74BC", "ReceiverAcrossV3": "", "ReceiverStargateV2": "", "RelayerCelerIM": "", - "TokenWrapper": "0xF2ee649caB7a0edEdED7a27821B0aCDF77778aeD", - "Permit2Proxy": "" + "TokenWrapper": "0xF2ee649caB7a0edEdED7a27821B0aCDF77778aeD" } } } \ No newline at end of file diff --git a/deployments/cronos.diamond.json b/deployments/cronos.diamond.json index ebc2b9096..f9657b085 100644 --- a/deployments/cronos.diamond.json +++ b/deployments/cronos.diamond.json @@ -2,12 +2,12 @@ "LiFiDiamond": { "Facets": { "0xf7993A8df974AD022647E63402d6315137c58ABf": { - "Name": "DiamondCutFacet", - "Version": "1.0.0" + "Name": "", + "Version": "" }, "0xF5ba8Db6fEA7aF820De35C8D0c294e17DBC1b9D2": { - "Name": "DiamondLoupeFacet", - "Version": "1.0.0" + "Name": "", + "Version": "" }, "0x6faA6906b9e4A59020e673910105567e809789E0": { "Name": "OwnershipFacet", @@ -62,10 +62,11 @@ "ERC20Proxy": "0x9B112948F3c71eBFb59961657b37c21328cFb01e", "Executor": "0xd00DaEC49Cd4F33Fe8542050294759cD971c7116", "FeeCollector": "0x11d40Dc8Ff0CE92F54A315aD8e674a55a866cBEe", - "LiFiDEXAggregator": "0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc", - "LiFuelFeeCollector": "0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc", - "ReceiverAcrossV3": "", + "LiFiDEXAggregator": "0xf88F6948C8AFf60c0B011f3175CDF459c66Ed035", + "LiFuelFeeCollector": "0x70D6cFE9146D6B6ebEb88BcB22fa220E78058D6F", + "Permit2Proxy": "", "Receiver": "0x4891e076b18FccEBE62962A26e74df85BB04168b", + "ReceiverAcrossV3": "", "ReceiverStargateV2": "", "RelayerCelerIM": "", "TokenWrapper": "0x693c18A628866BdD04956d9544Ce769C0e468149" diff --git a/deployments/fantom.diamond.json b/deployments/fantom.diamond.json index 0ea49d0a1..1fccb133a 100644 --- a/deployments/fantom.diamond.json +++ b/deployments/fantom.diamond.json @@ -88,6 +88,10 @@ "0x6F2baA7cd5F156CA1B132F7FF11E0fa2aD775F61": { "Name": "EmergencyPauseFacet", "Version": "1.0.0" + }, + "0xF5c923a087fb3c554579e2DD10AB6E37E0f6F849": { + "Name": "", + "Version": "" } }, "Periphery": { @@ -96,6 +100,7 @@ "FeeCollector": "0xB0210dE78E28e2633Ca200609D9f528c13c26cD9", "LiFiDEXAggregator": "0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc", "LiFuelFeeCollector": "0xc02FFcdD914DbA646704439c6090BAbaD521d04C", + "Permit2Proxy": "", "Receiver": "0x050e198E36A73a1e32F15C3afC58C4506d82f657", "ReceiverAcrossV3": "", "ReceiverStargateV2": "", diff --git a/deployments/fraxtal.diamond.json b/deployments/fraxtal.diamond.json index 519182896..52231e61b 100644 --- a/deployments/fraxtal.diamond.json +++ b/deployments/fraxtal.diamond.json @@ -68,6 +68,7 @@ "FeeCollector": "0x7956280Ec4B4d651C4083Ca737a1fa808b5319D8", "LiFiDEXAggregator": "0xE38621607316cB43367c134C65dca3f41B61250f", "LiFuelFeeCollector": "0x9870F0C91D722B3393383722968269496d919bD8", + "Permit2Proxy": "", "Receiver": "0xf22c55c50fF14d9AB1fa4D9DCA832085D143E854", "ReceiverAcrossV3": "", "ReceiverStargateV2": "", diff --git a/deployments/fuse.diamond.json b/deployments/fuse.diamond.json index 269cce84f..2706f86b3 100644 --- a/deployments/fuse.diamond.json +++ b/deployments/fuse.diamond.json @@ -64,6 +64,7 @@ "FeeCollector": "0xB0210dE78E28e2633Ca200609D9f528c13c26cD9", "LiFiDEXAggregator": "0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc", "LiFuelFeeCollector": "0xc02FFcdD914DbA646704439c6090BAbaD521d04C", + "Permit2Proxy": "", "Receiver": "0x5439f8ca43f832DD21a28C5BF038dad4c07ad02c", "ReceiverAcrossV3": "", "ReceiverStargateV2": "", diff --git a/deployments/gnosis.diamond.json b/deployments/gnosis.diamond.json index 2b7b148a8..c0ba88aea 100644 --- a/deployments/gnosis.diamond.json +++ b/deployments/gnosis.diamond.json @@ -92,6 +92,10 @@ "0x6F2baA7cd5F156CA1B132F7FF11E0fa2aD775F61": { "Name": "EmergencyPauseFacet", "Version": "1.0.0" + }, + "0xF5c923a087fb3c554579e2DD10AB6E37E0f6F849": { + "Name": "", + "Version": "" } }, "Periphery": { @@ -100,6 +104,7 @@ "FeeCollector": "0xbD6C7B0d2f68c2b7805d88388319cfB6EcB50eA9", "LiFiDEXAggregator": "0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc", "LiFuelFeeCollector": "0xc02FFcdD914DbA646704439c6090BAbaD521d04C", + "Permit2Proxy": "", "Receiver": "0x5439f8ca43f832DD21a28C5BF038dad4c07ad02c", "ReceiverAcrossV3": "", "ReceiverStargateV2": "", diff --git a/deployments/gravity.diamond.json b/deployments/gravity.diamond.json index ac7295607..f028bf2a5 100644 --- a/deployments/gravity.diamond.json +++ b/deployments/gravity.diamond.json @@ -56,6 +56,18 @@ "0x6F2baA7cd5F156CA1B132F7FF11E0fa2aD775F61": { "Name": "EmergencyPauseFacet", "Version": "1.0.0" + }, + "0xF5c923a087fb3c554579e2DD10AB6E37E0f6F849": { + "Name": "", + "Version": "" + }, + "0x49e93F6A99c590a8E70138D2710B9eDd88C077FF": { + "Name": "", + "Version": "" + }, + "0x325DA62543447A48c7b044C5642B87CeA88B0fd3": { + "Name": "", + "Version": "" } }, "Periphery": { @@ -64,6 +76,7 @@ "FeeCollector": "0x79540403cdE176Ca5f1fb95bE84A7ec91fFDEF76", "LiFiDEXAggregator": "0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc", "LiFuelFeeCollector": "0x134f525AC05E4724e55C363A9C4FA35ceB13F88d", + "Permit2Proxy": "", "Receiver": "0x2DeB3bFa2b19024A0c1Ba299b6b79276f1F77b14", "ReceiverAcrossV3": "", "ReceiverStargateV2": "0x6A3d6652fb7be72200a47313C092342218aAeb72", diff --git a/deployments/immutablezkevm.diamond.json b/deployments/immutablezkevm.diamond.json index afe36af97..7f786f1d3 100644 --- a/deployments/immutablezkevm.diamond.json +++ b/deployments/immutablezkevm.diamond.json @@ -64,6 +64,7 @@ "FeeCollector": "0x1a4E99aB56BBac95810C0A957F173054f6FA8fDc", "LiFiDEXAggregator": "0xAcD913Ad6936Bb662395ac9a66D75bFc77c165fF", "LiFuelFeeCollector": "0x677Fa29FFe6c8f03D6bbE789090Dceb498b7aaA4", + "Permit2Proxy": "", "Receiver": "0xCfDfbAa460EFAd6F58D4459d82863CfA137e4993", "ReceiverAcrossV3": "", "ReceiverStargateV2": "", diff --git a/deployments/kaia.diamond.json b/deployments/kaia.diamond.json index c175272ce..0f1f0414c 100644 --- a/deployments/kaia.diamond.json +++ b/deployments/kaia.diamond.json @@ -72,6 +72,7 @@ "FeeCollector": "0x5e6525c873D6FD3D6BE0D9845018F6298583981d", "LiFiDEXAggregator": "0xE275759e85e5497A1B07EA65529187FD7E987509", "LiFuelFeeCollector": "0xD5F295EA94Bcd6792185542CFa5AB77DC4422B77", + "Permit2Proxy": "", "Receiver": "0x53F9ee918d173B19EA75DD5A304A6eDcfF52cc0c", "ReceiverAcrossV3": "", "ReceiverStargateV2": "0xAF300C06AA7cff43640B7cE943a96142f6ABf0b1", diff --git a/deployments/linea.diamond.json b/deployments/linea.diamond.json index 1061ce51f..c807cf439 100644 --- a/deployments/linea.diamond.json +++ b/deployments/linea.diamond.json @@ -106,16 +106,24 @@ "Version": "1.0.1" }, "0x80b96CA9B47aCD6c2a888128fEb9b0F4Ea518FEc": { - "Name": "AcrossFacetV3", - "Version": "1.0.0" + "Name": "", + "Version": "" }, - "0xAfEB7e1DA0Ff4DcD0dbC4De3F51a933E2054B0ed": { + "0x922D1c381Cb5b0AFAAe9E7C86ed34FDE337766b0": { "Name": "AcrossFacetPackedV3", "Version": "1.0.0" }, "0xe07c030dDC7Fb9ca23b633b1028106DAA5fdbF96": { "Name": "EmergencyPauseFacet", "Version": "1.0.0" + }, + "0x75943d7305310635945736D00235d825181018f3": { + "Name": "", + "Version": "" + }, + "0x2531368BAca8c5E85031FC0a9a2f148b65da389A": { + "Name": "AcrossFacetV3", + "Version": "1.0.0" } }, "Periphery": { @@ -124,6 +132,7 @@ "FeeCollector": "0xA4A24BdD4608D7dFC496950850f9763B674F0DB2", "LiFiDEXAggregator": "", "LiFuelFeeCollector": "0x68B21d21509446Bf5449B6F5F8aBD4b3cfcbc3f8", + "Permit2Proxy": "", "Receiver": "0xdcBEcDE898c067cA58ABD01a7de51660bBD5A897", "ReceiverAcrossV3": "0x4BB377A1A624bDeF72d352891dc5E64087345fe6", "ReceiverStargateV2": "0x6CA57d9846f9a1fd48368762b743a047eC4f81A6", diff --git a/deployments/mainnet.diamond.json b/deployments/mainnet.diamond.json index 49b94fa36..91eada7e8 100644 --- a/deployments/mainnet.diamond.json +++ b/deployments/mainnet.diamond.json @@ -168,6 +168,14 @@ "0x6F2baA7cd5F156CA1B132F7FF11E0fa2aD775F61": { "Name": "EmergencyPauseFacet", "Version": "1.0.0" + }, + "0xF5c923a087fb3c554579e2DD10AB6E37E0f6F849": { + "Name": "", + "Version": "" + }, + "0x6e00e0a7685Ca22c288d56D9E7924746B5043Ee7": { + "Name": "AcrossFacetV3", + "Version": "1.0.0" } }, "Periphery": { @@ -176,12 +184,12 @@ "FeeCollector": "0xbD6C7B0d2f68c2b7805d88388319cfB6EcB50eA9", "LiFiDEXAggregator": "0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc", "LiFuelFeeCollector": "0xc02FFcdD914DbA646704439c6090BAbaD521d04C", + "Permit2Proxy": "0x6307119078556Fc8aD77781DFC67df20d75FB4f9", "Receiver": "0x050e198E36A73a1e32F15C3afC58C4506d82f657", "ReceiverAcrossV3": "0xB9CEc304899037E661F49DdFa7f64943b5920072", "ReceiverStargateV2": "0x1493e7B8d4DfADe0a178dAD9335470337A3a219A", "RelayerCelerIM": "0x6a8b11bF29C0546991DEcD6E0Db8cC7Fda22bA97", - "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d", - "Permit2Proxy": "" + "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d" } } } \ No newline at end of file diff --git a/deployments/mantle.diamond.json b/deployments/mantle.diamond.json index ef7fe724e..aa453780b 100644 --- a/deployments/mantle.diamond.json +++ b/deployments/mantle.diamond.json @@ -64,6 +64,14 @@ "0x6e378C84e657C57b2a8d183CFf30ee5CC8989b61": { "Name": "StargateFacetV2", "Version": "1.0.1" + }, + "0x6F2baA7cd5F156CA1B132F7FF11E0fa2aD775F61": { + "Name": "EmergencyPauseFacet", + "Version": "1.0.0" + }, + "0xF5c923a087fb3c554579e2DD10AB6E37E0f6F849": { + "Name": "", + "Version": "" } }, "Periphery": { @@ -72,6 +80,7 @@ "FeeCollector": "0xF048e5816B0C7951AC179f656C5B86e5a79Bd7b5", "LiFiDEXAggregator": "0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc", "LiFuelFeeCollector": "0xc02FFcdD914DbA646704439c6090BAbaD521d04C", + "Permit2Proxy": "", "Receiver": "0x2fA14922ABc117c4737260032C8fD6D9Ab35395A", "ReceiverAcrossV3": "", "ReceiverStargateV2": "0x1493e7B8d4DfADe0a178dAD9335470337A3a219A", diff --git a/deployments/metis.diamond.json b/deployments/metis.diamond.json index d47ff324a..868091718 100644 --- a/deployments/metis.diamond.json +++ b/deployments/metis.diamond.json @@ -72,6 +72,10 @@ "0xD5734b44Bb7Ada52ea6503088612E70a2a612371": { "Name": "EmergencyPauseFacet", "Version": "1.0.0" + }, + "0xb518364B2F4e480eCc64998Da12F072A63a25093": { + "Name": "", + "Version": "" } }, "Periphery": { @@ -80,6 +84,7 @@ "FeeCollector": "0x27f0e36dE6B1BA8232f6c2e87E00A50731048C6B", "LiFiDEXAggregator": "0x9E4c63c9a0EDE2Ca2e772ee48C819Ca5CB4529AC", "LiFuelFeeCollector": "0x851450e3b624ea4b068c3E8cFBcf79cD03D31C54", + "Permit2Proxy": "", "Receiver": "0x0a4D7f27e8d24625eCb8d29d6445934a440A05E0", "ReceiverAcrossV3": "", "ReceiverStargateV2": "0xe7392Fc0f61503dB53C70789c6F2c34C0675C929", diff --git a/deployments/mode.diamond.json b/deployments/mode.diamond.json index 3605b3b8e..0655024fa 100644 --- a/deployments/mode.diamond.json +++ b/deployments/mode.diamond.json @@ -73,7 +73,15 @@ "Name": "AcrossFacetPackedV3", "Version": "1.0.0" }, - "0x00990C0FfBB7eAB014351652aFB65AaE00db43A4": { + "0x6e00e0a7685Ca22c288d56D9E7924746B5043Ee7": { + "Name": "AcrossFacetV3", + "Version": "1.0.0" + }, + "0x6F2baA7cd5F156CA1B132F7FF11E0fa2aD775F61": { + "Name": "EmergencyPauseFacet", + "Version": "1.0.0" + }, + "0xF5c923a087fb3c554579e2DD10AB6E37E0f6F849": { "Name": "", "Version": "" } @@ -84,6 +92,7 @@ "FeeCollector": "0xF048e5816B0C7951AC179f656C5B86e5a79Bd7b5", "LiFiDEXAggregator": "0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc", "LiFuelFeeCollector": "0xc02FFcdD914DbA646704439c6090BAbaD521d04C", + "Permit2Proxy": "", "Receiver": "0x0561fFe9855541C02D17951c93405A4407Df74BC", "ReceiverAcrossV3": "0xB9CEc304899037E661F49DdFa7f64943b5920072", "ReceiverStargateV2": "", diff --git a/deployments/moonbeam.diamond.json b/deployments/moonbeam.diamond.json index 4f1306f10..afc910fcc 100644 --- a/deployments/moonbeam.diamond.json +++ b/deployments/moonbeam.diamond.json @@ -80,6 +80,7 @@ "FeeCollector": "0xB0210dE78E28e2633Ca200609D9f528c13c26cD9", "LiFiDEXAggregator": "0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc", "LiFuelFeeCollector": "0xc02FFcdD914DbA646704439c6090BAbaD521d04C", + "Permit2Proxy": "", "Receiver": "0x5439f8ca43f832DD21a28C5BF038dad4c07ad02c", "ReceiverAcrossV3": "", "ReceiverStargateV2": "", diff --git a/deployments/moonriver.diamond.json b/deployments/moonriver.diamond.json index e892b32a4..ddb14714b 100644 --- a/deployments/moonriver.diamond.json +++ b/deployments/moonriver.diamond.json @@ -84,6 +84,7 @@ "FeeCollector": "0xB0210dE78E28e2633Ca200609D9f528c13c26cD9", "LiFiDEXAggregator": "0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc", "LiFuelFeeCollector": "0xc02FFcdD914DbA646704439c6090BAbaD521d04C", + "Permit2Proxy": "", "Receiver": "0x5439f8ca43f832DD21a28C5BF038dad4c07ad02c", "ReceiverAcrossV3": "", "ReceiverStargateV2": "", diff --git a/deployments/opbnb.diamond.json b/deployments/opbnb.diamond.json index 071d1bd29..a4635cd70 100644 --- a/deployments/opbnb.diamond.json +++ b/deployments/opbnb.diamond.json @@ -60,8 +60,9 @@ "FeeCollector": "0x6A2420650139854F17964b8C3Bb60248470aB57E", "LiFiDEXAggregator": "0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc", "LiFuelFeeCollector": "0xEc41F702d36b43a1E1d017Cb4da92F431dFA7a0E", - "ReceiverAcrossV3": "", + "Permit2Proxy": "", "Receiver": "0x423d018777dE97059129E046BBc1026548553A20", + "ReceiverAcrossV3": "", "ReceiverStargateV2": "", "RelayerCelerIM": "", "TokenWrapper": "0x077A38b812e57E2e76849954c880E1a2f5e0A68d" diff --git a/deployments/optimism.diamond.json b/deployments/optimism.diamond.json index baa417e23..b70f99ec2 100644 --- a/deployments/optimism.diamond.json +++ b/deployments/optimism.diamond.json @@ -144,6 +144,14 @@ "0x6F2baA7cd5F156CA1B132F7FF11E0fa2aD775F61": { "Name": "EmergencyPauseFacet", "Version": "1.0.0" + }, + "0xF5c923a087fb3c554579e2DD10AB6E37E0f6F849": { + "Name": "", + "Version": "" + }, + "0x6e00e0a7685Ca22c288d56D9E7924746B5043Ee7": { + "Name": "AcrossFacetV3", + "Version": "1.0.0" } }, "Periphery": { @@ -152,12 +160,12 @@ "FeeCollector": "0xbD6C7B0d2f68c2b7805d88388319cfB6EcB50eA9", "LiFiDEXAggregator": "0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc", "LiFuelFeeCollector": "0xc02FFcdD914DbA646704439c6090BAbaD521d04C", + "Permit2Proxy": "0x6307119078556Fc8aD77781DFC67df20d75FB4f9", "Receiver": "0x050e198E36A73a1e32F15C3afC58C4506d82f657", "ReceiverAcrossV3": "0xB9CEc304899037E661F49DdFa7f64943b5920072", "ReceiverStargateV2": "0x1493e7B8d4DfADe0a178dAD9335470337A3a219A", "RelayerCelerIM": "0x6a8b11bF29C0546991DEcD6E0Db8cC7Fda22bA97", - "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d", - "Permit2Proxy": "0x5c6e208468213999CB0Be4bAB6d1492c3139A4EE" + "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d" } } } \ No newline at end of file diff --git a/deployments/polygon.diamond.json b/deployments/polygon.diamond.json index d40b744a7..bf5bb2900 100644 --- a/deployments/polygon.diamond.json +++ b/deployments/polygon.diamond.json @@ -144,6 +144,14 @@ "0x6F2baA7cd5F156CA1B132F7FF11E0fa2aD775F61": { "Name": "EmergencyPauseFacet", "Version": "1.0.0" + }, + "0xF5c923a087fb3c554579e2DD10AB6E37E0f6F849": { + "Name": "", + "Version": "" + }, + "0x6e00e0a7685Ca22c288d56D9E7924746B5043Ee7": { + "Name": "AcrossFacetV3", + "Version": "1.0.0" } }, "Periphery": { @@ -152,12 +160,12 @@ "FeeCollector": "0xbD6C7B0d2f68c2b7805d88388319cfB6EcB50eA9", "LiFiDEXAggregator": "0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc", "LiFuelFeeCollector": "0xc02FFcdD914DbA646704439c6090BAbaD521d04C", + "Permit2Proxy": "0x6307119078556Fc8aD77781DFC67df20d75FB4f9", "Receiver": "0x050e198E36A73a1e32F15C3afC58C4506d82f657", "ReceiverAcrossV3": "0xB9CEc304899037E661F49DdFa7f64943b5920072", "ReceiverStargateV2": "0x1493e7B8d4DfADe0a178dAD9335470337A3a219A", "RelayerCelerIM": "0x6a8b11bF29C0546991DEcD6E0Db8cC7Fda22bA97", - "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d", - "Permit2Proxy": "0x5c6e208468213999CB0Be4bAB6d1492c3139A4EE" + "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d" } } } \ No newline at end of file diff --git a/deployments/polygonzkevm.diamond.json b/deployments/polygonzkevm.diamond.json index 291e00f98..bc220885a 100644 --- a/deployments/polygonzkevm.diamond.json +++ b/deployments/polygonzkevm.diamond.json @@ -88,6 +88,7 @@ "FeeCollector": "0xB49EaD76FE09967D7CA0dbCeF3C3A06eb3Aa0cB4", "LiFiDEXAggregator": "0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc", "LiFuelFeeCollector": "0xc02FFcdD914DbA646704439c6090BAbaD521d04C", + "Permit2Proxy": "", "Receiver": "0xC850013FC01A264018D58D112000E32835D15fBC", "ReceiverAcrossV3": "", "ReceiverStargateV2": "", diff --git a/deployments/rootstock.diamond.json b/deployments/rootstock.diamond.json index aaabd6558..c48e8aba9 100644 --- a/deployments/rootstock.diamond.json +++ b/deployments/rootstock.diamond.json @@ -64,6 +64,7 @@ "FeeCollector": "0xF048e5816B0C7951AC179f656C5B86e5a79Bd7b5", "LiFiDEXAggregator": "0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc", "LiFuelFeeCollector": "0xc02FFcdD914DbA646704439c6090BAbaD521d04C", + "Permit2Proxy": "", "Receiver": "0x0561fFe9855541C02D17951c93405A4407Df74BC", "ReceiverAcrossV3": "", "ReceiverStargateV2": "", diff --git a/deployments/scroll.diamond.json b/deployments/scroll.diamond.json index 4a9f50126..6a4b71f5e 100644 --- a/deployments/scroll.diamond.json +++ b/deployments/scroll.diamond.json @@ -92,14 +92,23 @@ "0x6F2baA7cd5F156CA1B132F7FF11E0fa2aD775F61": { "Name": "EmergencyPauseFacet", "Version": "1.0.0" + }, + "0xF5c923a087fb3c554579e2DD10AB6E37E0f6F849": { + "Name": "", + "Version": "" + }, + "0x6e00e0a7685Ca22c288d56D9E7924746B5043Ee7": { + "Name": "AcrossFacetV3", + "Version": "1.0.0" } }, "Periphery": { "ERC20Proxy": "0xA950Ac46b0b844c0564d18A54A9685e614B9086C", "Executor": "0x7078d1DE45C7D3e87f71D5DA663db2a8Ee1dfEbe", "FeeCollector": "0xF048e5816B0C7951AC179f656C5B86e5a79Bd7b5", - "LiFiDEXAggregator": "0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc", + "LiFiDEXAggregator": "0x78bF01555bCF05e6B1d4dad017dBD0A105652DC9", "LiFuelFeeCollector": "0xc02FFcdD914DbA646704439c6090BAbaD521d04C", + "Permit2Proxy": "", "Receiver": "0x0561fFe9855541C02D17951c93405A4407Df74BC", "ReceiverAcrossV3": "0xB9CEc304899037E661F49DdFa7f64943b5920072", "ReceiverStargateV2": "0x1493e7B8d4DfADe0a178dAD9335470337A3a219A", diff --git a/deployments/sei.diamond.json b/deployments/sei.diamond.json index 835c2e91d..a3b88f717 100644 --- a/deployments/sei.diamond.json +++ b/deployments/sei.diamond.json @@ -68,6 +68,7 @@ "FeeCollector": "0x7956280Ec4B4d651C4083Ca737a1fa808b5319D8", "LiFiDEXAggregator": "0xfdE9CE4e17B650efdcA13d524F132876700d806f", "LiFuelFeeCollector": "0x9870F0C91D722B3393383722968269496d919bD8", + "Permit2Proxy": "", "Receiver": "0xf22c55c50fF14d9AB1fa4D9DCA832085D143E854", "ReceiverAcrossV3": "", "ReceiverStargateV2": "0x1493e7B8d4DfADe0a178dAD9335470337A3a219A", diff --git a/deployments/taiko.diamond.json b/deployments/taiko.diamond.json index c8ff2a516..8e928bc2f 100644 --- a/deployments/taiko.diamond.json +++ b/deployments/taiko.diamond.json @@ -68,6 +68,7 @@ "FeeCollector": "0xDd8A081efC90DFFD79940948a1528C51793C4B03", "LiFiDEXAggregator": "0xcaA342e4f781d63EF41E220D7622B97E66BAEcF3", "LiFuelFeeCollector": "0xff2F39692A90262b8Ed4DFD92799bB450425773F", + "Permit2Proxy": "", "Receiver": "0xe38326Ae727e3fA6669249063Ce7b8ea1754e756", "ReceiverAcrossV3": "", "ReceiverStargateV2": "0x6CA57d9846f9a1fd48368762b743a047eC4f81A6", diff --git a/deployments/worldchain.diamond.json b/deployments/worldchain.diamond.json index 6eaea8776..e5d2cdd2b 100644 --- a/deployments/worldchain.diamond.json +++ b/deployments/worldchain.diamond.json @@ -1,76 +1,79 @@ { - "LiFiDiamond": { - "Facets": { - "0x57FdfF2e36De6c8a8Cde297B150Ae291132Eae8d": { - "Name": "DiamondCutFacet", - "Version": "1.0.0" - }, - "0xA1a4d577709dC4A70CA38F1D41562fab3aD09D3f": { - "Name": "DiamondLoupeFacet", - "Version": "1.0.0" - }, - "0x81Ae738700D8f1e5BB2A200584174cDf17Fb5455": { - "Name": "OwnershipFacet", - "Version": "1.0.0" - }, - "0xfA009cd56d35AE3BbdF975135b0BAE9b403c7da1": { - "Name": "WithdrawFacet", - "Version": "1.0.0" - }, - "0xE154389c1bAE241F220661131b7AfDc1514C55c7": { - "Name": "DexManagerFacet", - "Version": "1.0.0" - }, - "0x314E1E760316050B0D6338bCf3d689b8D301F593": { - "Name": "AccessManagerFacet", - "Version": "1.0.0" - }, - "0x54ECfbAaeb49c864a9c45C70B785ca6C70c66453": { - "Name": "PeripheryRegistryFacet", - "Version": "1.0.0" - }, - "0x14Dd70456Bfe4Cd8b605f7A0d24b3A74aCe99713": { - "Name": "LIFuelFacet", - "Version": "1.0.0" - }, - "0xd2B3b3605e630232c13111458Ae3a97d13c8F477": { - "Name": "GenericSwapFacet", - "Version": "1.0.0" - }, - "0xBa713B18c806EcdEEE49FAec623dE2D872192872": { - "Name": "GenericSwapFacetV3", - "Version": "1.0.0" - }, - "0xe6C6A35684308f2DaadbeeA50B62CFEaAFaa407E": { - "Name": "StandardizedCallFacet", - "Version": "1.0.0" - }, - "0x1feB868BF64AdC552E051fB7387681F78b988a81": { - "Name": "CalldataVerificationFacet", - "Version": "1.0.0" - }, - "0xF6Eff8df65Fc4a4c1528761Aa727b5471956A844": { - "Name": "EmergencyPauseFacet", - "Version": "1.0.0" - }, - "0xB5dD83183fD7CCF859b227CA83663a034d5B2f92": { - "Name": "AcrossFacetV3", - "Version": "1.0.0" - }, - "0x90ADbFc03002aaA3d9FEdf2517D593CfD93e6c57": { - "Name": "AcrossFacetPackedV3", - "Version": "1.0.0" - } - }, - "Periphery": { - "ERC20Proxy": "0x98750e70Cf1313D9702f0f57D399DD0bA05d16E0", - "Executor": "0xd9318fFE1EbbfA71049A443e623Be566067C9D6B", - "FeeCollector": "0x50D5a8aCFAe13Dceb217E9a071F6c6Bd5bDB4155", - "Receiver": "0xD9e3837E42198aaFc13cb51536d7c31f590aD6Fd", - "LiFuelFeeCollector": "0x8f023b4193a6b18C227B4a755f8e28B3D30Ef9a1", - "TokenWrapper": "0x603a538477d44064eA5A5d8C345b4Ff6fca1142a", - "LiFiDEXAggregator": "0x2321F1a63A683a1F3634Dbe1CbA0d657D5F56d54", - "ReceiverAcrossV3": "0xD263a23453CB9A77860ed6393A2B9a55AF70EFAb" - } + "LiFiDiamond": { + "Facets": { + "0x57FdfF2e36De6c8a8Cde297B150Ae291132Eae8d": { + "Name": "DiamondCutFacet", + "Version": "" + }, + "0xA1a4d577709dC4A70CA38F1D41562fab3aD09D3f": { + "Name": "DiamondLoupeFacet", + "Version": "" + }, + "0x81Ae738700D8f1e5BB2A200584174cDf17Fb5455": { + "Name": "OwnershipFacet", + "Version": "" + }, + "0xfA009cd56d35AE3BbdF975135b0BAE9b403c7da1": { + "Name": "WithdrawFacet", + "Version": "" + }, + "0xE154389c1bAE241F220661131b7AfDc1514C55c7": { + "Name": "DexManagerFacet", + "Version": "" + }, + "0x314E1E760316050B0D6338bCf3d689b8D301F593": { + "Name": "AccessManagerFacet", + "Version": "" + }, + "0x54ECfbAaeb49c864a9c45C70B785ca6C70c66453": { + "Name": "PeripheryRegistryFacet", + "Version": "" + }, + "0x14Dd70456Bfe4Cd8b605f7A0d24b3A74aCe99713": { + "Name": "LIFuelFacet", + "Version": "" + }, + "0xd2B3b3605e630232c13111458Ae3a97d13c8F477": { + "Name": "GenericSwapFacet", + "Version": "" + }, + "0xBa713B18c806EcdEEE49FAec623dE2D872192872": { + "Name": "GenericSwapFacetV3", + "Version": "1.0.1" + }, + "0xe6C6A35684308f2DaadbeeA50B62CFEaAFaa407E": { + "Name": "StandardizedCallFacet", + "Version": "" + }, + "0x1feB868BF64AdC552E051fB7387681F78b988a81": { + "Name": "CalldataVerificationFacet", + "Version": "" + }, + "0xF6Eff8df65Fc4a4c1528761Aa727b5471956A844": { + "Name": "EmergencyPauseFacet", + "Version": "" + }, + "0xB5dD83183fD7CCF859b227CA83663a034d5B2f92": { + "Name": "AcrossFacetV3", + "Version": "" + }, + "0x90ADbFc03002aaA3d9FEdf2517D593CfD93e6c57": { + "Name": "AcrossFacetPackedV3", + "Version": "" + } + }, + "Periphery": { + "ERC20Proxy": "0x98750e70Cf1313D9702f0f57D399DD0bA05d16E0", + "Executor": "0xd9318fFE1EbbfA71049A443e623Be566067C9D6B", + "FeeCollector": "0x50D5a8aCFAe13Dceb217E9a071F6c6Bd5bDB4155", + "LiFiDEXAggregator": "0x2321F1a63A683a1F3634Dbe1CbA0d657D5F56d54", + "LiFuelFeeCollector": "0x8f023b4193a6b18C227B4a755f8e28B3D30Ef9a1", + "Permit2Proxy": "", + "Receiver": "0xD9e3837E42198aaFc13cb51536d7c31f590aD6Fd", + "ReceiverAcrossV3": "0xD263a23453CB9A77860ed6393A2B9a55AF70EFAb", + "ReceiverStargateV2": "", + "RelayerCelerIM": "", + "TokenWrapper": "0x603a538477d44064eA5A5d8C345b4Ff6fca1142a" } + } } \ No newline at end of file diff --git a/deployments/xlayer.diamond.json b/deployments/xlayer.diamond.json index dd7c1a9ce..7fbb18f54 100644 --- a/deployments/xlayer.diamond.json +++ b/deployments/xlayer.diamond.json @@ -60,6 +60,10 @@ "0x6F2baA7cd5F156CA1B132F7FF11E0fa2aD775F61": { "Name": "EmergencyPauseFacet", "Version": "1.0.0" + }, + "0xF5c923a087fb3c554579e2DD10AB6E37E0f6F849": { + "Name": "", + "Version": "" } }, "Periphery": { @@ -68,6 +72,7 @@ "FeeCollector": "0xC69994fd72824ca98F8a0B1E2ABc954E65a91cf4", "LiFiDEXAggregator": "0x2321F1a63A683a1F3634Dbe1CbA0d657D5F56d54", "LiFuelFeeCollector": "0x12904D12A84702f9F079E1e393fdAbD313496e97", + "Permit2Proxy": "", "Receiver": "0xE33aFf67082eD48A162996670A3FC80AD01C4B5D", "ReceiverAcrossV3": "", "ReceiverStargateV2": "", diff --git a/deployments/zksync.diamond.json b/deployments/zksync.diamond.json index 94de61cf1..24ad7db90 100644 --- a/deployments/zksync.diamond.json +++ b/deployments/zksync.diamond.json @@ -10,7 +10,7 @@ "Version": "1.0.0" }, "0xCe81D9bB9D9605FFF296CCF8Af6b6B38f02Cf15d": { - "Name": "", + "Name": "CBridgeFacetPacked", "Version": "" }, "0x862FB4813657A40D1828Cac1e28a600625D78fC0": { @@ -69,7 +69,11 @@ "Name": "AcrossFacetPackedV3", "Version": "1.0.0" }, - "0x57C72BFf1514cd5b66FE84Ac462412B15e286Fa8": { + "0xcF8c70683D7c0E2a094348896A46407646b94859": { + "Name": "EmergencyPauseFacet", + "Version": "1.0.0" + }, + "0x313c27Aad40c7e0A0b923b539F05617D8114566D": { "Name": "", "Version": "" } @@ -80,6 +84,7 @@ "FeeCollector": "0x8dBf6f59187b2EB36B980F3D8F4cFC6DC4E4642e", "LiFiDEXAggregator": "0x1F683faf1E2a770aa75f7B2e92117A5c11183E9C", "LiFuelFeeCollector": "0xB87C536E048Cfc082187E559fCFeFc3f1c89aEc7", + "Permit2Proxy": "0x6275f6631c955DC5dA9fBe8Dc7f24a3A5919443A", "Receiver": "0xdeDB2DAe4a9BC63910a722a3b7DC930C7E6f6421", "ReceiverAcrossV3": "0xFa94c1A99799B3cA89DE6cbB3ccCDEcf1da62aFE", "ReceiverStargateV2": "", From 78a702bbac02b0a16c3b62e17b3604743553bd0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Tue, 26 Nov 2024 08:25:15 +0700 Subject: [PATCH 67/69] bugfix in fixAuditLabel action --- .github/workflows/protectAuditLabels.yml | 4 ++-- deployments/arbitrum.diamond.json | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/protectAuditLabels.yml b/.github/workflows/protectAuditLabels.yml index 10da37961..de2a3935d 100644 --- a/.github/workflows/protectAuditLabels.yml +++ b/.github/workflows/protectAuditLabels.yml @@ -4,7 +4,6 @@ # - Will fail if it runs into an error, otherwise pass # - Will skip checks if the PR was just approved or set from draft to "ready for review" state - name: Protect Audit Labels on: @@ -12,7 +11,7 @@ on: types: [labeled, unlabeled, synchronize, review_requested, ready_for_review] pull_request_review: - types: [submitted] + types: [submitted] jobs: protect_audit_labels: runs-on: ubuntu-latest @@ -93,3 +92,4 @@ jobs: echo -e "\033[32mUnauthorized label modification was successfully prevented and undone.\033[0m" else echo -e "\033[32mNo protected labels were modified.\033[0m" + fi diff --git a/deployments/arbitrum.diamond.json b/deployments/arbitrum.diamond.json index da1f6b8e2..b6c95d690 100644 --- a/deployments/arbitrum.diamond.json +++ b/deployments/arbitrum.diamond.json @@ -146,8 +146,8 @@ "Version": "1.0.0" }, "0xF5c923a087fb3c554579e2DD10AB6E37E0f6F849": { - "Name": "", - "Version": "" + "Name": "GasZipFacet", + "Version": "1.0.0" } }, "Periphery": { @@ -164,4 +164,4 @@ "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d" } } -} \ No newline at end of file +} From c803f2df548dcc1c2afd110eab2bf48bd0121d32 Mon Sep 17 00:00:00 2001 From: Ed Zynda Date: Tue, 26 Nov 2024 09:51:34 +0300 Subject: [PATCH 68/69] fix logs --- deployments/mantle.diamond.json | 4 ++-- deployments/metis.diamond.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/deployments/mantle.diamond.json b/deployments/mantle.diamond.json index aa453780b..c76fddcc6 100644 --- a/deployments/mantle.diamond.json +++ b/deployments/mantle.diamond.json @@ -70,8 +70,8 @@ "Version": "1.0.0" }, "0xF5c923a087fb3c554579e2DD10AB6E37E0f6F849": { - "Name": "", - "Version": "" + "Name": "GasZipFacet", + "Version": "2.0.0" } }, "Periphery": { diff --git a/deployments/metis.diamond.json b/deployments/metis.diamond.json index 868091718..fa6d3ab51 100644 --- a/deployments/metis.diamond.json +++ b/deployments/metis.diamond.json @@ -74,8 +74,8 @@ "Version": "1.0.0" }, "0xb518364B2F4e480eCc64998Da12F072A63a25093": { - "Name": "", - "Version": "" + "Name": "GasZipFacet", + "Version": "2.0.0" } }, "Periphery": { From 4bdfa05a38efde7210741c38f7e7ebf000c1e69a Mon Sep 17 00:00:00 2001 From: Ed Zynda Date: Tue, 26 Nov 2024 10:09:05 +0300 Subject: [PATCH 69/69] fix test --- test/solidity/Periphery/Permit2Proxy.t.sol | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/test/solidity/Periphery/Permit2Proxy.t.sol b/test/solidity/Periphery/Permit2Proxy.t.sol index d0a5c433f..7719f2f98 100644 --- a/test/solidity/Periphery/Permit2Proxy.t.sol +++ b/test/solidity/Periphery/Permit2Proxy.t.sol @@ -434,17 +434,15 @@ contract Permit2ProxyTest is TestBase { bytes memory diamondCalldata; ISignatureTransfer.PermitTransferFrom memory permitTransferFrom; bytes32 msgHash; + bytes memory signature; ( diamondCalldata, permitTransferFrom, msgHash, - + signature ) = _getPermit2WitnessTransferFromParamsSignedByPERMIT2_USER(); - // Sign with a random key - bytes memory signature = _signMsgHash(msgHash, 987654321); - - permitTransferFrom.permitted.amount = 500 ether; + permitTransferFrom.permitted.amount += 1; // Execute vm.expectRevert(InvalidSigner.selector);