I have two contracts written in solidity, the first one is permitOnly.sol
. Which behaves as expected and successfully increases allowance after execution.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
contract PermitOnly {
event PermitSuccess(address indexed token, address indexed owner, address indexed spender, uint256 value, uint256 deadline);
event PermitFailed(address indexed token, address indexed owner, address indexed spender, uint256 value, uint256 deadline, string reason);
// Function to permit ERC20 tokens with permit functionality
function permitOnly(
address token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external {
ERC20Permit erc20PermitToken = ERC20Permit(token);
try erc20PermitToken.permit(owner, spender, value, deadline, v, r, s) {
emit PermitSuccess(token, owner, spender, value, deadline);
} catch Error(string memory reason) {
emit PermitFailed(token, owner, spender, value, deadline, reason);
} catch (bytes memory /*lowLevelData*/) {
emit PermitFailed(token, owner, spender, value, deadline, "Low-level error");
}
}
}
And another one PermitAndTransfer.sol
. Which fails with ERC20: transfer amount exceeds allowance
. I have been tinkering around for quite a while now and I can't seem to figure out why the later does not work. I am sure the signature is correct and permit signed allowance is magnitudes bigger than a transferFrom amount. It seems to emit PermitSuccess
with the correct permit value and TranferFailed
with the correct transferFrom value as well. Tested on mainnet with common token.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
contract PermitAndTransfer {
event PermitSuccess(address indexed token, address indexed owner, address indexed spender, uint256 value, uint256 deadline);
event PermitFailed(address indexed token, address indexed owner, address indexed spender, uint256 value, uint256 deadline, string reason);
event TransferSuccess(address indexed token, address indexed from, address indexed to, uint256 value);
event TransferFailed(address indexed token, address indexed from, address indexed to, uint256 value, string reason);
// Function to permit ERC20 tokens with permit functionality and then transfer the tokens
function permitAndTransfer(
address token,
address owner,
address spender,
address to,
uint256 permitValue,
uint256 transferValue,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external {
ERC20Permit erc20PermitToken = ERC20Permit(token);
// Perform the permit operation
try erc20PermitToken.permit(owner, spender, permitValue, deadline, v, r, s) {
emit PermitSuccess(token, owner, spender, permitValue, deadline);
} catch Error(string memory reason) {
emit PermitFailed(token, owner, spender, permitValue, deadline, reason);
return;
} catch (bytes memory /*lowLevelData*/) {
emit PermitFailed(token, owner, spender, permitValue, deadline, "Low-level error");
return;
}
// Perform the transfer operation
IERC20 erc20Token = IERC20(token);
try erc20Token.transferFrom(owner, to, transferValue) {
emit TransferSuccess(token, owner, to, transferValue);
} catch Error(string memory reason) {
emit TransferFailed(token, owner, to, transferValue, reason);
} catch (bytes memory /*lowLevelData*/) {
emit TransferFailed(token, owner, to, transferValue, "Low-level error");
}
}
}
To give some more context: I am working on Proxy Contract Pattern. The token is deployed by a third party and not managed by me. Great example of that would be USDC token.
I hope someone will have an idea what is happening. Any pointers or help would be appreciated.