diff --git a/contracts/integration-test/GasSwap.spec.ts b/contracts/integration-test/GasSwap.spec.ts index 4c98ab4ab1..86cb0992a0 100644 --- a/contracts/integration-test/GasSwap.spec.ts +++ b/contracts/integration-test/GasSwap.spec.ts @@ -189,6 +189,45 @@ describe("GasSwap.spec", async () => { ).to.revertedWith("insufficient output amount"); }); + it("should succeed, when attacker frontrun permit", async () => { + const amountIn = ethers.parseEther("1"); + const amountOut = ethers.parseEther("2"); + await token.mint(signer.address, amountIn); + await deployer.sendTransaction({ to: target.getAddress(), value: amountOut }); + const signature = await permit(amountIn); + + await token.permit( + signer.address, + swap.getAddress(), + amountIn, + MaxUint256, + signature.v, + signature.r, + signature.s + ); + + await target.setToken(token.getAddress()); + await target.setAmountIn(amountIn); + + await swap.updateApprovedTarget(target.getAddress(), true); + + swap.connect(signer).swap( + { + token: token.getAddress(), + value: amountIn, + deadline: MaxUint256, + r: signature.r, + s: signature.s, + v: signature.v, + }, + { + target: target.getAddress(), + data: "0x8119c065", + minOutput: 0, + } + ); + }); + for (const refundRatio of [0n, 1n, 5n]) { for (const feeRatio of ["0", "5", "50"]) { it(`should succeed, when swap by signer directly, with feeRatio[${feeRatio}%] refundRatio[${refundRatio}%]`, async () => { diff --git a/contracts/src/gas-swap/GasSwap.sol b/contracts/src/gas-swap/GasSwap.sol index 61bea33c44..5be004f3c4 100644 --- a/contracts/src/gas-swap/GasSwap.sol +++ b/contracts/src/gas-swap/GasSwap.sol @@ -14,7 +14,6 @@ import {Context} from "@openzeppelin/contracts/utils/Context.sol"; contract GasSwap is ERC2771Context, Ownable, ReentrancyGuard { using SafeERC20 for IERC20; - using SafeERC20 for IERC20Permit; /********** * Events * @@ -92,15 +91,19 @@ contract GasSwap is ERC2771Context, Ownable, ReentrancyGuard { address _sender = _msgSender(); // do permit - IERC20Permit(_permit.token).safePermit( - _sender, - address(this), - _permit.value, - _permit.deadline, - _permit.v, - _permit.r, - _permit.s - ); + try + IERC20Permit(_permit.token).permit( + _sender, + address(this), + _permit.value, + _permit.deadline, + _permit.v, + _permit.r, + _permit.s + ) + {} catch { + require(IERC20(_permit.token).allowance(_sender, address(this)) >= _permit.value, "Permit failed"); + } // record token balance in this contract uint256 _balance = IERC20(_permit.token).balanceOf(address(this));