diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ffc204a..b7a23b4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -21,8 +21,5 @@ jobs: with: version: nightly - - name: Check format - run: forge fmt --check - - name: Run tests run: forge test -vvv \ No newline at end of file diff --git a/foundry.toml b/foundry.toml index 87563c5..7538c0a 100644 --- a/foundry.toml +++ b/foundry.toml @@ -6,4 +6,5 @@ libs = ["lib"] # See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options [rpc_endpoints] +mainnet = "https://mainnet.infura.io/v3/${INFURA_PROJECT_ID}" op_goerli = "https://endpoints.omniatech.io/v1/op/goerli/public" diff --git a/script/Deploy.s.sol b/script/Deploy.s.sol index cc92787..410e654 100644 --- a/script/Deploy.s.sol +++ b/script/Deploy.s.sol @@ -34,8 +34,17 @@ contract Deploy is Script { config.socketVault ); + LyraSelfPayingForwarder selfPayingForwarder = new LyraSelfPayingForwarder( + config.usdcLocal, + config.usdcRemote, + config.bridge, + config.socketVault + ); + console2.log("LyraSponsoredForwarder deployed at: ", address(sponsoredForwarder)); + console2.log("LyraSelfPayingForwarder deployed at: ", address(selfPayingForwarder)); + vm.stopBroadcast(); } @@ -47,9 +56,18 @@ contract Deploy is Script { trustedForwarder: 0xd8253782c45a12053594b9deB72d8e8aB2Fca54c, usdcLocal: 0xe05606174bac4A6364B31bd0eCA4bf4dD368f8C6, // official USDC op goerli usdcRemote: 0x0000000000000000000000000000000000000000, // - bridge: 0x0000000000000000000000000000000000000000, // no standard bridge on goerli + bridge: 0x0000000000000000000000000000000000000000, + // no standard bridge on goerli socketVault: 0x0000000000000000000000000000000000000000 // todo: add socket vault }); + } else if (block.chainid == 1) { + return DeploymentConfig({ + trustedForwarder: 0x3CACa7b48D0573D793d3b0279b5F0029180E83b6, + usdcLocal: 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48, // mainnet USDC + usdcRemote: 0x7F5c764cBc14f9669B88837ca1490cCa17c31607, // OP USDC (Bridged) + bridge: 0x99C9fc46f92E8a1c0deC1b1747d010903E884bE1, // OP bridge on mainnet + socketVault: 0x0000000000000000000000000000000000000000 + }); } revert("Need config set! Please set config in script/Deploy.s.sol"); diff --git a/src/gelato/LyraForwarderBase.sol b/src/gelato/LyraForwarderBase.sol index e93344c..d959944 100644 --- a/src/gelato/LyraForwarderBase.sol +++ b/src/gelato/LyraForwarderBase.sol @@ -49,7 +49,7 @@ abstract contract LyraForwarderBase { usdcSocketVault = _socketVault; IERC20(_usdcLocal).approve(_l1standardBridge, type(uint256).max); - IERC20(_usdcLocal).approve(_socketVault, type(uint256).max); + if (_socketVault != address(0)) IERC20(_usdcLocal).approve(_socketVault, type(uint256).max); } /** diff --git a/src/gelato/LyraSelfPayingForwarder.sol b/src/gelato/LyraSelfPayingForwarder.sol index 36160be..f1f8800 100644 --- a/src/gelato/LyraSelfPayingForwarder.sol +++ b/src/gelato/LyraSelfPayingForwarder.sol @@ -8,6 +8,7 @@ import {LyraForwarderBase} from "./LyraForwarderBase.sol"; /** * @title LyraSelfPayingForwarder * @notice Use this contract to allow gasless transactions, but users pay for their own gas with ERC20s + * @dev This contract can only be called by GELATO_RELAY_ERC2771 or GELATO_RELAY_CONCURRENT_ERC2771 */ contract LyraSelfPayingForwarder is LyraForwarderBase, GelatoRelayContextERC2771 { constructor(address _usdcLocal, address _usdcRemote, address _bridge, address _socketVault) diff --git a/src/mocks/USDC.sol b/src/mocks/USDC.sol index 91dc055..475df8a 100644 --- a/src/mocks/USDC.sol +++ b/src/mocks/USDC.sol @@ -1,12 +1,12 @@ /** - *Submitted for verification at Etherscan.io on 2021-04-17 -*/ + * Submitted for verification at Etherscan.io on 2021-04-17 + */ // File: @openzeppelin/contracts/math/SafeMath.sol // SPDX-License-Identifier: MIT -pragma solidity ^0.6.0; +pragma solidity ^0.8.0; /** * @dev Wrappers over Solidity's arithmetic operations with added overflow @@ -63,11 +63,7 @@ library SafeMath { * * - Subtraction cannot overflow. */ - function sub( - uint256 a, - uint256 b, - string memory errorMessage - ) internal pure returns (uint256) { + function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b <= a, errorMessage); uint256 c = a - b; @@ -126,11 +122,7 @@ library SafeMath { * * - The divisor cannot be zero. */ - function div( - uint256 a, - uint256 b, - string memory errorMessage - ) internal pure returns (uint256) { + function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b > 0, errorMessage); uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold @@ -166,11 +158,7 @@ library SafeMath { * * - The divisor cannot be zero. */ - function mod( - uint256 a, - uint256 b, - string memory errorMessage - ) internal pure returns (uint256) { + function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b != 0, errorMessage); return a % b; } @@ -178,7 +166,7 @@ library SafeMath { // File: @openzeppelin/contracts/token/ERC20/IERC20.sol -pragma solidity ^0.6.0; +pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. @@ -201,9 +189,7 @@ interface IERC20 { * * Emits a {Transfer} event. */ - function transfer(address recipient, uint256 amount) - external - returns (bool); + function transfer(address recipient, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be @@ -212,10 +198,7 @@ interface IERC20 { * * This value changes when {approve} or {transferFrom} are called. */ - function allowance(address owner, address spender) - external - view - returns (uint256); + function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. @@ -242,11 +225,7 @@ interface IERC20 { * * Emits a {Transfer} event. */ - function transferFrom( - address sender, - address recipient, - uint256 amount - ) external returns (bool); + function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to @@ -260,11 +239,7 @@ interface IERC20 { * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ - event Approval( - address indexed owner, - address indexed spender, - uint256 value - ); + event Approval(address indexed owner, address indexed spender, uint256 value); } // File: contracts/v1/AbstractFiatTokenV1.sol @@ -291,20 +266,12 @@ interface IERC20 { * SOFTWARE. */ -pragma solidity 0.6.12; +pragma solidity ^0.8.0; abstract contract AbstractFiatTokenV1 is IERC20 { - function _approve( - address owner, - address spender, - uint256 value - ) internal virtual; + function _approve(address owner, address spender, uint256 value) internal virtual; - function _transfer( - address from, - address to, - uint256 value - ) internal virtual; + function _transfer(address from, address to, uint256 value) internal virtual; } // File: contracts/v1/Ownable.sol @@ -331,7 +298,7 @@ abstract contract AbstractFiatTokenV1 is IERC20 { * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -pragma solidity 0.6.12; +pragma solidity ^0.8.0; /** * @notice The Ownable contract has an owner address, and provides basic @@ -356,7 +323,7 @@ contract Ownable { /** * @dev The constructor sets the original owner of the contract to the sender account. */ - constructor() public { + constructor() { setOwner(msg.sender); } @@ -388,10 +355,7 @@ contract Ownable { * @param newOwner The address to transfer ownership to. */ function transferOwnership(address newOwner) external onlyOwner { - require( - newOwner != address(0), - "Ownable: new owner is the zero address" - ); + require(newOwner != address(0), "Ownable: new owner is the zero address"); emit OwnershipTransferred(_owner, newOwner); setOwner(newOwner); } @@ -422,7 +386,7 @@ contract Ownable { * SOFTWARE. */ -pragma solidity 0.6.12; +pragma solidity ^0.8.0; /** * @notice Base contract which allows children to implement an emergency stop @@ -481,10 +445,7 @@ contract Pausable is Ownable { * @dev update the pauser role */ function updatePauser(address _newPauser) external onlyOwner { - require( - _newPauser != address(0), - "Pausable: new pauser is the zero address" - ); + require(_newPauser != address(0), "Pausable: new pauser is the zero address"); pauser = _newPauser; emit PauserChanged(pauser); } @@ -514,7 +475,7 @@ contract Pausable is Ownable { * SOFTWARE. */ -pragma solidity 0.6.12; +pragma solidity ^0.8.0; /** * @title Blacklistable Token @@ -532,10 +493,7 @@ contract Blacklistable is Ownable { * @dev Throws if called by any account other than the blacklister */ modifier onlyBlacklister() { - require( - msg.sender == blacklister, - "Blacklistable: caller is not the blacklister" - ); + require(msg.sender == blacklister, "Blacklistable: caller is not the blacklister"); _; } @@ -544,10 +502,7 @@ contract Blacklistable is Ownable { * @param _account The address to check */ modifier notBlacklisted(address _account) { - require( - !blacklisted[_account], - "Blacklistable: account is blacklisted" - ); + require(!blacklisted[_account], "Blacklistable: account is blacklisted"); _; } @@ -578,10 +533,7 @@ contract Blacklistable is Ownable { } function updateBlacklister(address _newBlacklister) external onlyOwner { - require( - _newBlacklister != address(0), - "Blacklistable: new blacklister is the zero address" - ); + require(_newBlacklister != address(0), "Blacklistable: new blacklister is the zero address"); blacklister = _newBlacklister; emit BlacklisterChanged(blacklister); } @@ -612,7 +564,7 @@ contract Blacklistable is Ownable { * SOFTWARE. */ -pragma solidity 0.6.12; +pragma solidity ^0.8.0; /** * @title FiatToken @@ -651,22 +603,10 @@ contract FiatTokenV1 is AbstractFiatTokenV1, Ownable, Pausable, Blacklistable { address newOwner ) public { require(!initialized, "FiatToken: contract is already initialized"); - require( - newMasterMinter != address(0), - "FiatToken: new masterMinter is the zero address" - ); - require( - newPauser != address(0), - "FiatToken: new pauser is the zero address" - ); - require( - newBlacklister != address(0), - "FiatToken: new blacklister is the zero address" - ); - require( - newOwner != address(0), - "FiatToken: new owner is the zero address" - ); + require(newMasterMinter != address(0), "FiatToken: new masterMinter is the zero address"); + require(newPauser != address(0), "FiatToken: new pauser is the zero address"); + require(newBlacklister != address(0), "FiatToken: new blacklister is the zero address"); + require(newOwner != address(0), "FiatToken: new owner is the zero address"); name = tokenName; symbol = tokenSymbol; @@ -706,10 +646,7 @@ contract FiatTokenV1 is AbstractFiatTokenV1, Ownable, Pausable, Blacklistable { require(_amount > 0, "FiatToken: mint amount not greater than 0"); uint256 mintingAllowedAmount = minterAllowed[msg.sender]; - require( - _amount <= mintingAllowedAmount, - "FiatToken: mint amount exceeds minterAllowance" - ); + require(_amount <= mintingAllowedAmount, "FiatToken: mint amount exceeds minterAllowance"); totalSupply_ = totalSupply_.add(_amount); balances[_to] = balances[_to].add(_amount); @@ -723,10 +660,7 @@ contract FiatTokenV1 is AbstractFiatTokenV1, Ownable, Pausable, Blacklistable { * @dev Throws if called by any account other than the masterMinter */ modifier onlyMasterMinter() { - require( - msg.sender == masterMinter, - "FiatToken: caller is not the masterMinter" - ); + require(msg.sender == masterMinter, "FiatToken: caller is not the masterMinter"); _; } @@ -753,19 +687,14 @@ contract FiatTokenV1 is AbstractFiatTokenV1, Ownable, Pausable, Blacklistable { * @param spender Spender's address * @return Allowance amount */ - function allowance(address owner, address spender) - external - override - view - returns (uint256) - { + function allowance(address owner, address spender) external view override returns (uint256) { return allowed[owner][spender]; } /** * @dev Get totalSupply of token */ - function totalSupply() external override view returns (uint256) { + function totalSupply() external view override returns (uint256) { return totalSupply_; } @@ -773,12 +702,7 @@ contract FiatTokenV1 is AbstractFiatTokenV1, Ownable, Pausable, Blacklistable { * @dev Get token balance of an account * @param account address The account */ - function balanceOf(address account) - external - override - view - returns (uint256) - { + function balanceOf(address account) external view override returns (uint256) { return balances[account]; } @@ -807,11 +731,7 @@ contract FiatTokenV1 is AbstractFiatTokenV1, Ownable, Pausable, Blacklistable { * @param spender Spender's address * @param value Allowance amount */ - function _approve( - address owner, - address spender, - uint256 value - ) internal override { + function _approve(address owner, address spender, uint256 value) internal override { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); allowed[owner][spender] = value; @@ -825,11 +745,7 @@ contract FiatTokenV1 is AbstractFiatTokenV1, Ownable, Pausable, Blacklistable { * @param value Transfer amount * @return True if successful */ - function transferFrom( - address from, - address to, - uint256 value - ) + function transferFrom(address from, address to, uint256 value) external override whenNotPaused @@ -838,10 +754,7 @@ contract FiatTokenV1 is AbstractFiatTokenV1, Ownable, Pausable, Blacklistable { notBlacklisted(to) returns (bool) { - require( - value <= allowed[from][msg.sender], - "ERC20: transfer amount exceeds allowance" - ); + require(value <= allowed[from][msg.sender], "ERC20: transfer amount exceeds allowance"); _transfer(from, to, value); allowed[from][msg.sender] = allowed[from][msg.sender].sub(value); return true; @@ -871,17 +784,10 @@ contract FiatTokenV1 is AbstractFiatTokenV1, Ownable, Pausable, Blacklistable { * @param to Payee's address * @param value Transfer amount */ - function _transfer( - address from, - address to, - uint256 value - ) internal override { + function _transfer(address from, address to, uint256 value) internal override { require(from != address(0), "ERC20: transfer from the zero address"); require(to != address(0), "ERC20: transfer to the zero address"); - require( - value <= balances[from], - "ERC20: transfer amount exceeds balance" - ); + require(value <= balances[from], "ERC20: transfer amount exceeds balance"); balances[from] = balances[from].sub(value); balances[to] = balances[to].add(value); @@ -911,11 +817,7 @@ contract FiatTokenV1 is AbstractFiatTokenV1, Ownable, Pausable, Blacklistable { * @param minter The address of the minter to remove * @return True if the operation was successful. */ - function removeMinter(address minter) - external - onlyMasterMinter - returns (bool) - { + function removeMinter(address minter) external onlyMasterMinter returns (bool) { minters[minter] = false; minterAllowed[minter] = 0; emit MinterRemoved(minter); @@ -928,12 +830,7 @@ contract FiatTokenV1 is AbstractFiatTokenV1, Ownable, Pausable, Blacklistable { * amount is less than or equal to the minter's account balance * @param _amount uint256 the amount of tokens to be burned */ - function burn(uint256 _amount) - external - whenNotPaused - onlyMinters - notBlacklisted(msg.sender) - { + function burn(uint256 _amount) external whenNotPaused onlyMinters notBlacklisted(msg.sender) { uint256 balance = balances[msg.sender]; require(_amount > 0, "FiatToken: burn amount not greater than 0"); require(balance >= _amount, "FiatToken: burn amount exceeds balance"); @@ -945,10 +842,7 @@ contract FiatTokenV1 is AbstractFiatTokenV1, Ownable, Pausable, Blacklistable { } function updateMasterMinter(address _newMasterMinter) external onlyOwner { - require( - _newMasterMinter != address(0), - "FiatToken: new masterMinter is the zero address" - ); + require(_newMasterMinter != address(0), "FiatToken: new masterMinter is the zero address"); masterMinter = _newMasterMinter; emit MasterMinterChanged(masterMinter); } @@ -956,7 +850,7 @@ contract FiatTokenV1 is AbstractFiatTokenV1, Ownable, Pausable, Blacklistable { // File: @openzeppelin/contracts/utils/Address.sol -pragma solidity ^0.6.2; +pragma solidity ^0.8.0; /** * @dev Collection of functions related to the address type @@ -985,8 +879,7 @@ library Address { // for accounts without code, i.e. `keccak256('')` bytes32 codehash; - bytes32 accountHash - = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470; + bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470; // solhint-disable-next-line no-inline-assembly assembly { codehash := extcodehash(account) @@ -1011,17 +904,11 @@ library Address { * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { - require( - address(this).balance >= amount, - "Address: insufficient balance" - ); + require(address(this).balance >= amount, "Address: insufficient balance"); // solhint-disable-next-line avoid-low-level-calls, avoid-call-value - (bool success, ) = recipient.call{ value: amount }(""); - require( - success, - "Address: unable to send value, recipient may have reverted" - ); + (bool success,) = recipient.call{value: amount}(""); + require(success, "Address: unable to send value, recipient may have reverted"); } /** @@ -1042,10 +929,7 @@ library Address { * * _Available since v3.1._ */ - function functionCall(address target, bytes memory data) - internal - returns (bytes memory) - { + function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } @@ -1055,11 +939,10 @@ library Address { * * _Available since v3.1._ */ - function functionCall( - address target, - bytes memory data, - string memory errorMessage - ) internal returns (bytes memory) { + function functionCall(address target, bytes memory data, string memory errorMessage) + internal + returns (bytes memory) + { return _functionCallWithValue(target, data, 0, errorMessage); } @@ -1074,18 +957,8 @@ library Address { * * _Available since v3.1._ */ - function functionCallWithValue( - address target, - bytes memory data, - uint256 value - ) internal returns (bytes memory) { - return - functionCallWithValue( - target, - data, - value, - "Address: low-level call with value failed" - ); + function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { + return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** @@ -1094,31 +967,22 @@ library Address { * * _Available since v3.1._ */ - function functionCallWithValue( - address target, - bytes memory data, - uint256 value, - string memory errorMessage - ) internal returns (bytes memory) { - require( - address(this).balance >= value, - "Address: insufficient balance for call" - ); + function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) + internal + returns (bytes memory) + { + require(address(this).balance >= value, "Address: insufficient balance for call"); return _functionCallWithValue(target, data, value, errorMessage); } - function _functionCallWithValue( - address target, - bytes memory data, - uint256 weiValue, - string memory errorMessage - ) private returns (bytes memory) { + function _functionCallWithValue(address target, bytes memory data, uint256 weiValue, string memory errorMessage) + private + returns (bytes memory) + { require(isContract(target), "Address: call to non-contract"); // solhint-disable-next-line avoid-low-level-calls - (bool success, bytes memory returndata) = target.call{ - value: weiValue - }(data); + (bool success, bytes memory returndata) = target.call{value: weiValue}(data); if (success) { return returndata; } else { @@ -1140,7 +1004,7 @@ library Address { // File: @openzeppelin/contracts/token/ERC20/SafeERC20.sol -pragma solidity ^0.6.0; +pragma solidity ^0.8.0; /** * @title SafeERC20 @@ -1155,27 +1019,12 @@ library SafeERC20 { using SafeMath for uint256; using Address for address; - function safeTransfer( - IERC20 token, - address to, - uint256 value - ) internal { - _callOptionalReturn( - token, - abi.encodeWithSelector(token.transfer.selector, to, value) - ); + function safeTransfer(IERC20 token, address to, uint256 value) internal { + _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } - function safeTransferFrom( - IERC20 token, - address from, - address to, - uint256 value - ) internal { - _callOptionalReturn( - token, - abi.encodeWithSelector(token.transferFrom.selector, from, to, value) - ); + function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { + _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } /** @@ -1185,11 +1034,7 @@ library SafeERC20 { * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ - function safeApprove( - IERC20 token, - address spender, - uint256 value - ) internal { + function safeApprove(IERC20 token, address spender, uint256 value) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' @@ -1198,47 +1043,18 @@ library SafeERC20 { (value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); - _callOptionalReturn( - token, - abi.encodeWithSelector(token.approve.selector, spender, value) - ); + _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } - function safeIncreaseAllowance( - IERC20 token, - address spender, - uint256 value - ) internal { - uint256 newAllowance = token.allowance(address(this), spender).add( - value - ); - _callOptionalReturn( - token, - abi.encodeWithSelector( - token.approve.selector, - spender, - newAllowance - ) - ); + function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { + uint256 newAllowance = token.allowance(address(this), spender).add(value); + _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } - function safeDecreaseAllowance( - IERC20 token, - address spender, - uint256 value - ) internal { - uint256 newAllowance = token.allowance(address(this), spender).sub( - value, - "SafeERC20: decreased allowance below zero" - ); - _callOptionalReturn( - token, - abi.encodeWithSelector( - token.approve.selector, - spender, - newAllowance - ) - ); + function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { + uint256 newAllowance = + token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero"); + _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } /** @@ -1252,17 +1068,11 @@ library SafeERC20 { // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. - bytes memory returndata = address(token).functionCall( - data, - "SafeERC20: low-level call failed" - ); + bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional // solhint-disable-next-line max-line-length - require( - abi.decode(returndata, (bool)), - "SafeERC20: ERC20 operation did not succeed" - ); + require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } } @@ -1291,7 +1101,7 @@ library SafeERC20 { * SOFTWARE. */ -pragma solidity 0.6.12; +pragma solidity ^0.8.0; contract Rescuable is Ownable { using SafeERC20 for IERC20; @@ -1322,11 +1132,7 @@ contract Rescuable is Ownable { * @param to Recipient address * @param amount Amount to withdraw */ - function rescueERC20( - IERC20 tokenContract, - address to, - uint256 amount - ) external onlyRescuer { + function rescueERC20(IERC20 tokenContract, address to, uint256 amount) external onlyRescuer { tokenContract.safeTransfer(to, amount); } @@ -1335,10 +1141,7 @@ contract Rescuable is Ownable { * @param newRescuer New rescuer's address */ function updateRescuer(address newRescuer) external onlyOwner { - require( - newRescuer != address(0), - "Rescuable: new rescuer is the zero address" - ); + require(newRescuer != address(0), "Rescuable: new rescuer is the zero address"); _rescuer = newRescuer; emit RescuerChanged(newRescuer); } @@ -1368,15 +1171,13 @@ contract Rescuable is Ownable { * SOFTWARE. */ -pragma solidity 0.6.12; +pragma solidity ^0.8.0; /** * @title FiatTokenV1_1 * @dev ERC20 Token backed by fiat reserves */ -contract FiatTokenV1_1 is FiatTokenV1, Rescuable { - -} +contract FiatTokenV1_1 is FiatTokenV1, Rescuable {} // File: contracts/v2/AbstractFiatTokenV2.sol @@ -1402,20 +1203,12 @@ contract FiatTokenV1_1 is FiatTokenV1, Rescuable { * SOFTWARE. */ -pragma solidity 0.6.12; +pragma solidity ^0.8.0; abstract contract AbstractFiatTokenV2 is AbstractFiatTokenV1 { - function _increaseAllowance( - address owner, - address spender, - uint256 increment - ) internal virtual; - - function _decreaseAllowance( - address owner, - address spender, - uint256 decrement - ) internal virtual; + function _increaseAllowance(address owner, address spender, uint256 increment) internal virtual; + + function _decreaseAllowance(address owner, address spender, uint256 decrement) internal virtual; } // File: contracts/util/ECRecover.sol @@ -1443,7 +1236,7 @@ abstract contract AbstractFiatTokenV2 is AbstractFiatTokenV1 { * SOFTWARE. */ -pragma solidity 0.6.12; +pragma solidity ^0.8.0; /** * @title ECRecover @@ -1460,12 +1253,7 @@ library ECRecover { * @param s s of the signature * @return Signer address */ - function recover( - bytes32 digest, - uint8 v, - bytes32 r, - bytes32 s - ) internal pure returns (address) { + function recover(bytes32 digest, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) { // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines // the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most @@ -1475,10 +1263,7 @@ library ECRecover { // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept // these malleable signatures as well. - if ( - uint256(s) > - 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0 - ) { + if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) { revert("ECRecover: invalid signature 's' value"); } @@ -1518,7 +1303,7 @@ library ECRecover { * SOFTWARE. */ -pragma solidity 0.6.12; +pragma solidity ^0.8.0; /** * @title EIP712 @@ -1531,26 +1316,21 @@ library EIP712 { * @param version Contract version * @return Domain separator */ - function makeDomainSeparator(string memory name, string memory version) - internal - view - returns (bytes32) - { + function makeDomainSeparator(string memory name, string memory version) internal view returns (bytes32) { uint256 chainId; assembly { chainId := chainid() } - return - keccak256( - abi.encode( - // keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)") - 0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f, - keccak256(bytes(name)), - keccak256(bytes(version)), - chainId, - address(this) - ) - ); + return keccak256( + abi.encode( + // keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)") + 0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f, + keccak256(bytes(name)), + keccak256(bytes(version)), + chainId, + address(this) + ) + ); } /** @@ -1562,20 +1342,12 @@ library EIP712 { * @param typeHashAndData Type hash concatenated with data * @return Signer's address */ - function recover( - bytes32 domainSeparator, - uint8 v, - bytes32 r, - bytes32 s, - bytes memory typeHashAndData - ) internal pure returns (address) { - bytes32 digest = keccak256( - abi.encodePacked( - "\x19\x01", - domainSeparator, - keccak256(typeHashAndData) - ) - ); + function recover(bytes32 domainSeparator, uint8 v, bytes32 r, bytes32 s, bytes memory typeHashAndData) + internal + pure + returns (address) + { + bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, keccak256(typeHashAndData))); return ECRecover.recover(digest, v, r, s); } } @@ -1604,7 +1376,7 @@ library EIP712 { * SOFTWARE. */ -pragma solidity 0.6.12; +pragma solidity ^0.8.0; /** * @title EIP712 Domain @@ -1640,7 +1412,7 @@ contract EIP712Domain { * SOFTWARE. */ -pragma solidity 0.6.12; +pragma solidity ^0.8.0; /** * @title EIP-3009 @@ -1650,16 +1422,16 @@ pragma solidity 0.6.12; */ abstract contract EIP3009 is AbstractFiatTokenV2, EIP712Domain { // keccak256("TransferWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)") - bytes32 - public constant TRANSFER_WITH_AUTHORIZATION_TYPEHASH = 0x7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a2267; + bytes32 public constant TRANSFER_WITH_AUTHORIZATION_TYPEHASH = + 0x7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a2267; // keccak256("ReceiveWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)") - bytes32 - public constant RECEIVE_WITH_AUTHORIZATION_TYPEHASH = 0xd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de8; + bytes32 public constant RECEIVE_WITH_AUTHORIZATION_TYPEHASH = + 0xd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de8; // keccak256("CancelAuthorization(address authorizer,bytes32 nonce)") - bytes32 - public constant CANCEL_AUTHORIZATION_TYPEHASH = 0x158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a1597429; + bytes32 public constant CANCEL_AUTHORIZATION_TYPEHASH = + 0x158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a1597429; /** * @dev authorizer address => nonce => bool (true if nonce is used) @@ -1667,10 +1439,7 @@ abstract contract EIP3009 is AbstractFiatTokenV2, EIP712Domain { mapping(address => mapping(bytes32 => bool)) private _authorizationStates; event AuthorizationUsed(address indexed authorizer, bytes32 indexed nonce); - event AuthorizationCanceled( - address indexed authorizer, - bytes32 indexed nonce - ); + event AuthorizationCanceled(address indexed authorizer, bytes32 indexed nonce); /** * @notice Returns the state of an authorization @@ -1680,11 +1449,7 @@ abstract contract EIP3009 is AbstractFiatTokenV2, EIP712Domain { * @param nonce Nonce of the authorization * @return True if the nonce is used */ - function authorizationState(address authorizer, bytes32 nonce) - external - view - returns (bool) - { + function authorizationState(address authorizer, bytes32 nonce) external view returns (bool) { return _authorizationStates[authorizer][nonce]; } @@ -1713,19 +1478,9 @@ abstract contract EIP3009 is AbstractFiatTokenV2, EIP712Domain { ) internal { _requireValidAuthorization(from, nonce, validAfter, validBefore); - bytes memory data = abi.encode( - TRANSFER_WITH_AUTHORIZATION_TYPEHASH, - from, - to, - value, - validAfter, - validBefore, - nonce - ); - require( - EIP712.recover(DOMAIN_SEPARATOR, v, r, s, data) == from, - "FiatTokenV2: invalid signature" - ); + bytes memory data = + abi.encode(TRANSFER_WITH_AUTHORIZATION_TYPEHASH, from, to, value, validAfter, validBefore, nonce); + require(EIP712.recover(DOMAIN_SEPARATOR, v, r, s, data) == from, "FiatTokenV2: invalid signature"); _markAuthorizationAsUsed(from, nonce); _transfer(from, to, value); @@ -1759,19 +1514,9 @@ abstract contract EIP3009 is AbstractFiatTokenV2, EIP712Domain { require(to == msg.sender, "FiatTokenV2: caller must be the payee"); _requireValidAuthorization(from, nonce, validAfter, validBefore); - bytes memory data = abi.encode( - RECEIVE_WITH_AUTHORIZATION_TYPEHASH, - from, - to, - value, - validAfter, - validBefore, - nonce - ); - require( - EIP712.recover(DOMAIN_SEPARATOR, v, r, s, data) == from, - "FiatTokenV2: invalid signature" - ); + bytes memory data = + abi.encode(RECEIVE_WITH_AUTHORIZATION_TYPEHASH, from, to, value, validAfter, validBefore, nonce); + require(EIP712.recover(DOMAIN_SEPARATOR, v, r, s, data) == from, "FiatTokenV2: invalid signature"); _markAuthorizationAsUsed(from, nonce); _transfer(from, to, value); @@ -1785,24 +1530,11 @@ abstract contract EIP3009 is AbstractFiatTokenV2, EIP712Domain { * @param r r of the signature * @param s s of the signature */ - function _cancelAuthorization( - address authorizer, - bytes32 nonce, - uint8 v, - bytes32 r, - bytes32 s - ) internal { + function _cancelAuthorization(address authorizer, bytes32 nonce, uint8 v, bytes32 r, bytes32 s) internal { _requireUnusedAuthorization(authorizer, nonce); - bytes memory data = abi.encode( - CANCEL_AUTHORIZATION_TYPEHASH, - authorizer, - nonce - ); - require( - EIP712.recover(DOMAIN_SEPARATOR, v, r, s, data) == authorizer, - "FiatTokenV2: invalid signature" - ); + bytes memory data = abi.encode(CANCEL_AUTHORIZATION_TYPEHASH, authorizer, nonce); + require(EIP712.recover(DOMAIN_SEPARATOR, v, r, s, data) == authorizer, "FiatTokenV2: invalid signature"); _authorizationStates[authorizer][nonce] = true; emit AuthorizationCanceled(authorizer, nonce); @@ -1813,14 +1545,8 @@ abstract contract EIP3009 is AbstractFiatTokenV2, EIP712Domain { * @param authorizer Authorizer's address * @param nonce Nonce of the authorization */ - function _requireUnusedAuthorization(address authorizer, bytes32 nonce) - private - view - { - require( - !_authorizationStates[authorizer][nonce], - "FiatTokenV2: authorization is used or canceled" - ); + function _requireUnusedAuthorization(address authorizer, bytes32 nonce) private view { + require(!_authorizationStates[authorizer][nonce], "FiatTokenV2: authorization is used or canceled"); } /** @@ -1830,17 +1556,12 @@ abstract contract EIP3009 is AbstractFiatTokenV2, EIP712Domain { * @param validAfter The time after which this is valid (unix time) * @param validBefore The time before which this is valid (unix time) */ - function _requireValidAuthorization( - address authorizer, - bytes32 nonce, - uint256 validAfter, - uint256 validBefore - ) private view { - require( - now > validAfter, - "FiatTokenV2: authorization is not yet valid" - ); - require(now < validBefore, "FiatTokenV2: authorization is expired"); + function _requireValidAuthorization(address authorizer, bytes32 nonce, uint256 validAfter, uint256 validBefore) + private + view + { + require(block.timestamp > validAfter, "FiatTokenV2: authorization is not yet valid"); + require(block.timestamp < validBefore, "FiatTokenV2: authorization is expired"); _requireUnusedAuthorization(authorizer, nonce); } @@ -1849,9 +1570,7 @@ abstract contract EIP3009 is AbstractFiatTokenV2, EIP712Domain { * @param authorizer Authorizer's address * @param nonce Nonce of the authorization */ - function _markAuthorizationAsUsed(address authorizer, bytes32 nonce) - private - { + function _markAuthorizationAsUsed(address authorizer, bytes32 nonce) private { _authorizationStates[authorizer][nonce] = true; emit AuthorizationUsed(authorizer, nonce); } @@ -1881,7 +1600,7 @@ abstract contract EIP3009 is AbstractFiatTokenV2, EIP712Domain { * SOFTWARE. */ -pragma solidity 0.6.12; +pragma solidity ^0.8.0; /** * @title EIP-2612 @@ -1889,8 +1608,7 @@ pragma solidity 0.6.12; */ abstract contract EIP2612 is AbstractFiatTokenV2, EIP712Domain { // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)") - bytes32 - public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9; + bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9; mapping(address => uint256) private _permitNonces; @@ -1913,29 +1631,13 @@ abstract contract EIP2612 is AbstractFiatTokenV2, EIP712Domain { * @param r r of the signature * @param s s of the signature */ - function _permit( - address owner, - address spender, - uint256 value, - uint256 deadline, - uint8 v, - bytes32 r, - bytes32 s - ) internal { - require(deadline >= now, "FiatTokenV2: permit is expired"); - - bytes memory data = abi.encode( - PERMIT_TYPEHASH, - owner, - spender, - value, - _permitNonces[owner]++, - deadline - ); - require( - EIP712.recover(DOMAIN_SEPARATOR, v, r, s, data) == owner, - "EIP2612: invalid signature" - ); + function _permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) + internal + { + require(deadline >= block.timestamp, "FiatTokenV2: permit is expired"); + + bytes memory data = abi.encode(PERMIT_TYPEHASH, owner, spender, value, _permitNonces[owner]++, deadline); + require(EIP712.recover(DOMAIN_SEPARATOR, v, r, s, data) == owner, "EIP2612: invalid signature"); _approve(owner, spender, value); } @@ -1965,7 +1667,7 @@ abstract contract EIP2612 is AbstractFiatTokenV2, EIP712Domain { * SOFTWARE. */ -pragma solidity 0.6.12; +pragma solidity ^0.8.0; /** * @title FiatToken V2 @@ -2043,17 +1745,7 @@ contract FiatTokenV2 is FiatTokenV1_1, EIP3009, EIP2612 { bytes32 r, bytes32 s ) external whenNotPaused notBlacklisted(from) notBlacklisted(to) { - _transferWithAuthorization( - from, - to, - value, - validAfter, - validBefore, - nonce, - v, - r, - s - ); + _transferWithAuthorization(from, to, value, validAfter, validBefore, nonce, v, r, s); } /** @@ -2081,17 +1773,7 @@ contract FiatTokenV2 is FiatTokenV1_1, EIP3009, EIP2612 { bytes32 r, bytes32 s ) external whenNotPaused notBlacklisted(from) notBlacklisted(to) { - _receiveWithAuthorization( - from, - to, - value, - validAfter, - validBefore, - nonce, - v, - r, - s - ); + _receiveWithAuthorization(from, to, value, validAfter, validBefore, nonce, v, r, s); } /** @@ -2103,13 +1785,10 @@ contract FiatTokenV2 is FiatTokenV1_1, EIP3009, EIP2612 { * @param r r of the signature * @param s s of the signature */ - function cancelAuthorization( - address authorizer, - bytes32 nonce, - uint8 v, - bytes32 r, - bytes32 s - ) external whenNotPaused { + function cancelAuthorization(address authorizer, bytes32 nonce, uint8 v, bytes32 r, bytes32 s) + external + whenNotPaused + { _cancelAuthorization(authorizer, nonce, v, r, s); } @@ -2123,15 +1802,12 @@ contract FiatTokenV2 is FiatTokenV1_1, EIP3009, EIP2612 { * @param r r of the signature * @param s s of the signature */ - function permit( - address owner, - address spender, - uint256 value, - uint256 deadline, - uint8 v, - bytes32 r, - bytes32 s - ) external whenNotPaused notBlacklisted(owner) notBlacklisted(spender) { + function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) + external + whenNotPaused + notBlacklisted(owner) + notBlacklisted(spender) + { _permit(owner, spender, value, deadline, v, r, s); } @@ -2141,12 +1817,8 @@ contract FiatTokenV2 is FiatTokenV1_1, EIP3009, EIP2612 { * @param spender Spender's address * @param increment Amount of increase */ - function _increaseAllowance( - address owner, - address spender, - uint256 increment - ) internal override { - _approve(owner, spender, allowed[owner][spender].add(increment)); + function _increaseAllowance(address owner, address spender, uint256 increment) internal override { + _approve(owner, spender, allowed[owner][spender] + increment); } /** @@ -2155,19 +1827,8 @@ contract FiatTokenV2 is FiatTokenV1_1, EIP3009, EIP2612 { * @param spender Spender's address * @param decrement Amount of decrease */ - function _decreaseAllowance( - address owner, - address spender, - uint256 decrement - ) internal override { - _approve( - owner, - spender, - allowed[owner][spender].sub( - decrement, - "ERC20: decreased allowance below zero" - ) - ); + function _decreaseAllowance(address owner, address spender, uint256 decrement) internal override { + _approve(owner, spender, allowed[owner][spender] - decrement); } } @@ -2195,7 +1856,7 @@ contract FiatTokenV2 is FiatTokenV1_1, EIP3009, EIP2612 { * SOFTWARE. */ -pragma solidity 0.6.12; +pragma solidity ^0.8.0; // solhint-disable func-name-mixedcase @@ -2203,7 +1864,7 @@ pragma solidity 0.6.12; * @title FiatToken V2.1 * @notice ERC20 Token backed by fiat reserves, version 2.1 */ -contract FiatTokenV2_1 is FiatTokenV2 { +contract USDC is FiatTokenV2 { /** * @notice Initialize v2.1 * @param lostAndFound The address to which the locked funds are sent @@ -2225,7 +1886,7 @@ contract FiatTokenV2_1 is FiatTokenV2 { * @notice Version string for the EIP712 domain separator * @return Version string */ - function version() external view returns (string memory) { + function version() external pure returns (string memory) { return "2"; } -} \ No newline at end of file +} diff --git a/test/integration-tests/gelato/.gitkeep b/test/fork-tests/gelato/.gitkeep similarity index 100% rename from test/integration-tests/gelato/.gitkeep rename to test/fork-tests/gelato/.gitkeep diff --git a/test/fork-tests/gelato/SelfPayingForwarder.t.sol b/test/fork-tests/gelato/SelfPayingForwarder.t.sol new file mode 100644 index 0000000..bc5484d --- /dev/null +++ b/test/fork-tests/gelato/SelfPayingForwarder.t.sol @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.18; + +import "lib/forge-std/src/Test.sol"; +import "lib/forge-std/src/console2.sol"; + +import "src/gelato/LyraSelfPayingForwarder.sol"; +import {USDC} from "src/mocks/USDC.sol"; + +contract FORK_SelfPayingForwarderTest is Test { + address public immutable gelato = address(0x3CACa7b48D0573D793d3b0279b5F0029180E83b6); + + address public immutable gelatoRelayer = address(0xb539068872230f20456CF38EC52EF2f91AF4AE49); + + address public immutable usdcMainnet = address(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48); + + // all transactions go from gelato => gelatoRelayer -> forwarder + + LyraSelfPayingForwarder public forwarder; + + uint256 public alicePk = 0xbabebabe; + address public alice = vm.addr(alicePk); + + function setUp() public { + if (block.chainid != 1) revert("Please run against mainnet fork"); + + // deploy test contract + forwarder = new LyraSelfPayingForwarder( + 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48, // mainnet USDC + 0x7F5c764cBc14f9669B88837ca1490cCa17c31607, // OP USDC (Bridged) + 0x99C9fc46f92E8a1c0deC1b1747d010903E884bE1, // OP bridge on mainnet + 0x0000000000000000000000000000000000000000 + ); + + _mintMainnetUSDC(alice, 1e6 * 1e6); + } + + function test_fork_SelfPayingForwarder() public { + assertFalse(address(forwarder) == address(0)); + } + + function test_fork_depositFromEOA() public { + // alice sign transfer with auth + + // call forwarder + } + + function _mintMainnetUSDC(address account, uint256 amount) public { + vm.prank(0xE982615d461DD5cD06575BbeA87624fda4e3de17); // masterMinter for USDC + USDC(usdcMainnet).configureMinter(address(this), 5000e18); + + // mint from address(this) + USDC(usdcMainnet).mint(account, amount); + } + + function _sendTxAsGelatoRelayer() public { + vm.startPrank(gelatoRelayer); + // attach sender info at end of tx + vm.stopPrank(); + } +}