-
Notifications
You must be signed in to change notification settings - Fork 160
[PT1-347] Feature/permit2 without witness #400
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
e2a464c
Add Permit2Proxy contract and IPermit2TransferFrom interface
ifelsedeveloper 92204f2
Refactor Permit2Proxy contract and remove unused ABI selector script
ifelsedeveloper e09f89b
fix: rename test file permit
ifelsedeveloper 8396b0a
Add Development Guide for Selector Bruteforce Tool
ifelsedeveloper ebc3965
Add Permit2Proxy documentation
ifelsedeveloper 2ad61ba
Updated nonce generation in tests to ensure uniqueness and avoid conf…
ifelsedeveloper db632a5
lint: updated spacing in function declarations
ifelsedeveloper 5210885
feat: test permit fix nounce
ifelsedeveloper ff5a678
feat: implement unique nonce generation for Permit2 in tests
ifelsedeveloper 2d8ea9d
refactor: simplify test structure for Permit2 and WitnessProxy examples
ifelsedeveloper File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| // SPDX-License-Identifier: MIT | ||
|
|
||
| pragma solidity 0.8.30; | ||
|
|
||
| import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; | ||
|
|
||
| import "../interfaces/IPermit2TransferFrom.sol"; | ||
| import "./ImmutableOwner.sol"; | ||
|
|
||
| /* solhint-disable func-name-mixedcase */ | ||
|
|
||
| contract Permit2Proxy is ImmutableOwner { | ||
| error Permit2ProxyBadSelector(); | ||
|
|
||
| IPermit2TransferFrom private constant _PERMIT2 = IPermit2TransferFrom(0x000000000022D473030F116dDEE9F6B43aC78BA3); | ||
|
|
||
| constructor(address _immutableOwner) ImmutableOwner(_immutableOwner) { | ||
| if (Permit2Proxy.func_nZHTch.selector != IERC20.transferFrom.selector) revert Permit2ProxyBadSelector(); | ||
| } | ||
|
|
||
| /// @notice Proxy transfer method for `Permit2.permitTransferFrom`. Selector must match `IERC20.transferFrom` | ||
| // keccak256("func_nZHTch(address,address,uint256,((address,uint256),uint256,uint256),bytes)") == 0x23b872dd (IERC20.transferFrom) | ||
| function func_nZHTch( | ||
| address from, | ||
| address to, | ||
| uint256 amount, | ||
| IPermit2TransferFrom.PermitTransferFrom calldata permit, | ||
| bytes calldata sig | ||
| ) external onlyImmutableOwner { | ||
| _PERMIT2.permitTransferFrom( | ||
| permit, | ||
| IPermit2TransferFrom.SignatureTransferDetails({ | ||
| to: to, | ||
| requestedAmount: amount | ||
| }), | ||
| from, | ||
| sig | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| /* solhint-enable func-name-mixedcase */ | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| // SPDX-License-Identifier: MIT | ||
|
|
||
| pragma solidity ^0.8.0; | ||
|
|
||
| interface IPermit2TransferFrom { | ||
| struct TokenPermissions { | ||
| // ERC20 token address | ||
| address token; | ||
| // the maximum amount that can be spent | ||
| uint256 amount; | ||
| } | ||
|
|
||
| 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; | ||
| } | ||
|
|
||
| struct SignatureTransferDetails { | ||
| // recipient address | ||
| address to; | ||
| // spender requested amount | ||
| uint256 requestedAmount; | ||
| } | ||
|
|
||
| function permitTransferFrom( | ||
| PermitTransferFrom calldata permit, | ||
| SignatureTransferDetails calldata transferDetails, | ||
| address owner, | ||
| bytes calldata signature | ||
| ) external; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| # Development Guide | ||
|
|
||
| ## Selector Bruteforce Tool | ||
|
|
||
| When developing proxy contracts that need specific function selectors (e.g., matching `IERC20.transferFrom` selector `0x23b872dd`), use the selector bruteforce tool from: | ||
|
|
||
| **https://github.com/1inch/smart-contract-helper-utils/src** | ||
|
|
||
| ### Usage Example | ||
|
|
||
| To find a function name with custom suffix that produces the `transferFrom` selector: | ||
|
|
||
| ```bash | ||
| python selector_bruteforce.py \ | ||
| --target 0x23b872dd \ | ||
| --params "address,address,uint256,((address,uint256),uint256,uint256),bytes" \ | ||
| --prefix "func_" \ | ||
| --fast | ||
| ``` | ||
|
|
||
| This is used for contracts like `Permit2Proxy` and `Permit2WitnessProxy` where the proxy function must have the same selector as `transferFrom` to work with the limit order protocol's extension mechanism. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
|
|
||
| ## Permit2Proxy | ||
|
|
||
| A proxy contract that enables using Uniswap's Permit2 `permitTransferFrom` within the limit order protocol without the witness functionality. | ||
|
|
||
| ### Functions list | ||
| - [constructor(_immutableOwner) public](#constructor) | ||
| - [func_nZHTch(from, to, amount, permit, sig) external](#func_nzhtch) | ||
|
|
||
| ### Errors list | ||
| - [Permit2ProxyBadSelector()](#permit2proxybadselector) | ||
|
|
||
| ### Functions | ||
| ### constructor | ||
|
|
||
| ```solidity | ||
| constructor(address _immutableOwner) public | ||
| ``` | ||
|
|
||
| ### func_nZHTch | ||
|
|
||
| ```solidity | ||
| function func_nZHTch(address from, address to, uint256 amount, struct IPermit2TransferFrom.PermitTransferFrom permit, bytes sig) external | ||
| ``` | ||
| Proxy transfer method for `Permit2.permitTransferFrom`. Selector must match `IERC20.transferFrom` | ||
|
|
||
| The function name `func_nZHTch` is chosen so that its selector equals `0x23b872dd` (same as `IERC20.transferFrom`), allowing it to be used as a maker asset in limit orders. | ||
|
|
||
| ### Errors | ||
| ### Permit2ProxyBadSelector | ||
|
|
||
| ```solidity | ||
| error Permit2ProxyBadSelector() | ||
| ``` | ||
|
|
||
| Thrown in the constructor if the function selector doesn't match `IERC20.transferFrom.selector`. | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,85 @@ | ||
| const { constants, permit2Contract } = require('@1inch/solidity-utils'); | ||
| const { SignatureTransfer, PERMIT2_ADDRESS } = require('@uniswap/permit2-sdk'); | ||
| const { ether } = require('./helpers/utils'); | ||
| const { signOrder, buildOrder, buildTakerTraits, buildMakerTraitsRFQ } = require('./helpers/orderUtils'); | ||
| const { deploySwapTokens } = require('./helpers/fixtures'); | ||
| const { nextPermit2Nonce } = require('./helpers/nonce'); | ||
| const hre = require('hardhat'); | ||
| const { ethers } = hre; | ||
|
|
||
| describe('Permit2Proxy', function () { | ||
| let addr, addr1; | ||
|
|
||
| before(async function () { | ||
| [addr, addr1] = await ethers.getSigners(); | ||
| }); | ||
|
|
||
| it('permit2 example (without witness)', async function () { | ||
| const { dai, weth, swap, chainId } = await deploySwapTokens(); | ||
|
|
||
| await dai.mint(addr, ether('2000')); | ||
| await weth.connect(addr1).deposit({ value: ether('1') }); | ||
| await dai.approve(swap, ether('2000')); | ||
| await weth.connect(addr1).approve(PERMIT2_ADDRESS, ether('1')); | ||
|
|
||
| const Permit2Proxy = await ethers.getContractFactory('Permit2Proxy'); | ||
| const permit2Proxy = await Permit2Proxy.deploy(await swap.getAddress()); | ||
| await permit2Proxy.waitForDeployment(); | ||
|
|
||
| await permit2Contract(); | ||
SevenSwen marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| const permit = { | ||
| permitted: { | ||
| token: await weth.getAddress(), | ||
| amount: ether('1'), | ||
| }, | ||
| spender: await permit2Proxy.getAddress(), | ||
| nonce: nextPermit2Nonce(), | ||
| deadline: 0xffffffff, | ||
| }; | ||
|
|
||
| const data = SignatureTransfer.getPermitData( | ||
| permit, | ||
| PERMIT2_ADDRESS, | ||
| chainId, | ||
| ); | ||
|
|
||
| const sig = ethers.Signature.from(await addr1.signTypedData(data.domain, data.types, data.values)); | ||
|
|
||
| const makerAssetSuffix = '0x' + permit2Proxy.interface.encodeFunctionData('func_nZHTch', [ | ||
| constants.ZERO_ADDRESS, constants.ZERO_ADDRESS, 0, | ||
| { | ||
| permitted: { | ||
| token: permit.permitted.token, | ||
| amount: permit.permitted.amount, | ||
| }, | ||
| nonce: permit.nonce, | ||
| deadline: permit.deadline, | ||
| }, | ||
| sig.compactSerialized, | ||
| ]).substring(202); | ||
|
|
||
| const order = buildOrder( | ||
| { | ||
| maker: addr1.address, | ||
| makerAsset: await permit2Proxy.getAddress(), | ||
| takerAsset: await dai.getAddress(), | ||
| makingAmount: ether('1'), | ||
| takingAmount: ether('2000'), | ||
| makerTraits: buildMakerTraitsRFQ(), | ||
| }, | ||
| { | ||
| makerAssetSuffix, | ||
| }, | ||
| ); | ||
|
|
||
| const { r, yParityAndS: vs } = ethers.Signature.from(await signOrder(order, chainId, await swap.getAddress(), addr1)); | ||
| const takerTraits = buildTakerTraits({ | ||
| makingAmount: true, | ||
| extension: order.extension, | ||
| threshold: order.takingAmount, | ||
| }); | ||
|
|
||
| await swap.fillOrderArgs(order, r, vs, order.makingAmount, takerTraits.traits, takerTraits.args); | ||
| }); | ||
| }); | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| let currentNonce = 0n; | ||
|
|
||
| /** | ||
| * Returns a unique nonce for Permit2 SignatureTransfer. | ||
| * Uses global state to ensure each call returns a different nonce. | ||
| * @returns {bigint} A unique nonce | ||
| */ | ||
| function nextPermit2Nonce () { | ||
| return currentNonce++; | ||
| } | ||
|
|
||
| module.exports = { | ||
| nextPermit2Nonce, | ||
| }; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.