/ test / GasBroker.t.sol
GasBroker.t.sol
  1  // SPDX-License-Identifier: MIT
  2  pragma solidity ^0.8.20;
  3  
  4  import "forge-std/Test.sol";
  5  
  6  import './PermitSigUtils.sol';
  7  import './RewardSigUtils.sol';
  8  import './TestToken.sol';
  9  import "../src/GasBroker.sol";
 10  
 11  contract MockPriceOracle is IPriceOracle {
 12    function getPriceInEth(address token, uint amount) external pure returns (uint256) {
 13      return 1 ether;
 14    }
 15  }
 16  
 17  contract GasBrokerTest is Test {
 18    uint256 constant SIGNER_PRIVATE_KEY = 0xA11CE;
 19    uint256 constant SIGNER_TOKEN_BALANCE = 150e6;
 20    uint256 constant VALUE = 110e6;
 21    uint256 constant REWARD = 10e6;
 22  
 23    PermitSigUtils permitSigUtils;
 24    RewardSigUtils rewardSigUtils;
 25    GasBroker gasBroker;
 26    TestToken token;
 27    IPriceOracle priceOracle;
 28    address signer;
 29    uint256 deadline;
 30  
 31    uint8 permitV;
 32    bytes32 permitR;
 33    bytes32 permitS;
 34    uint8 rewardV;
 35    bytes32 rewardR;
 36    bytes32 rewardS;
 37  
 38    bytes32 permitHash;
 39  
 40    function setUp() public {
 41      deadline = block.timestamp + 1 days;
 42      // deploy PriceOracle
 43      priceOracle = new MockPriceOracle();
 44      // deploy GasBroker
 45      gasBroker = new GasBroker(block.chainid, address(priceOracle));
 46      signer = vm.addr(SIGNER_PRIVATE_KEY);
 47      // deploy test token
 48      token = new TestToken();
 49  
 50      // fund wallet with tokens
 51      token.transfer(signer, SIGNER_TOKEN_BALANCE);
 52      // burn the rest of tokens
 53      token.transfer(address(0), token.balanceOf(address(this)));
 54  
 55      // deploy sigUtils
 56      permitSigUtils = new PermitSigUtils(token.DOMAIN_SEPARATOR());
 57      rewardSigUtils = new RewardSigUtils(gasBroker.DOMAIN_SEPARATOR());
 58  
 59  
 60  
 61      // prepare signature for permit
 62      (permitV, permitR, permitS) = getPermitSignature(signer, VALUE);
 63  
 64      permitHash = keccak256(abi.encodePacked(permitR,permitS,permitV));
 65      // prepare signature for reward
 66      (rewardV, rewardR, rewardS) = getRewardSignature(REWARD, permitHash);
 67    }
 68  
 69    function getPermitSignature(address _signer, uint256 _value) internal view returns (uint8 v, bytes32 r, bytes32 s) {
 70      PermitSigUtils.Permit memory permit = PermitSigUtils.Permit({
 71        owner: _signer,
 72        spender: address(gasBroker),
 73        value: _value,
 74        nonce: 0,
 75        deadline: deadline
 76      });
 77      bytes32 digest = permitSigUtils.getTypedDataHash(permit);
 78      (v, r, s) = vm.sign(SIGNER_PRIVATE_KEY, digest);
 79    }
 80  
 81    function getRewardSignature(uint256 reward, bytes32 permitHash) internal view returns (uint8 v, bytes32 r, bytes32 s) {
 82      Reward memory reward = Reward({
 83        value: reward,
 84        permitHash: permitHash
 85      });
 86      bytes32 digest = rewardSigUtils.getTypedDataHash(reward);
 87  
 88      (v, r, s) = vm.sign(SIGNER_PRIVATE_KEY, digest);
 89    }
 90  
 91    function test_shouldRevertWhenRewardExceedsValue() public {
 92      (uint8 rewardV, bytes32 rewardR, bytes32 rewardS) = getRewardSignature(VALUE + 1, permitHash);
 93      vm.expectRevert("Reward could not exceed value");
 94      gasBroker.swap{ value: 1 ether }(
 95        signer,
 96        address(token),
 97        VALUE,
 98        deadline,
 99        VALUE + 1,
100        permitV,
101        permitR,
102        permitS,
103        rewardV,
104        rewardR,
105        rewardS
106      );
107    }
108  
109    function test_shouldRevertWhenPermitHashInRewardMessageIsInvalid() public {
110      // prepare signature for permit
111      (uint8 v, bytes32 r, bytes32 s) = getPermitSignature(signer, VALUE + 1);
112  
113  
114      bytes32 permitHash = keccak256(abi.encode(v,r,s));
115      // prepare signature for reward
116      (uint8 rewardV, bytes32 rewardR, bytes32 rewardS) = getRewardSignature(REWARD, permitHash);
117      vm.expectRevert("Reward signature is invalid");
118      gasBroker.swap{ value: 1 ether }(
119        signer,
120        address(token),
121        VALUE,
122        deadline,
123        REWARD,
124        permitV,
125        permitR,
126        permitS,
127        rewardV,
128        rewardR,
129        rewardS
130      );
131    }
132  
133    function test_shouldRevertWhenSignerInRewardMessageIsInvalid() public {
134      // prepare signature for reward
135      Reward memory reward = Reward({
136        value: REWARD,
137        permitHash: permitHash
138      });
139      bytes32 digest = rewardSigUtils.getTypedDataHash(reward);
140  
141      (uint8 rewardV, bytes32 rewardR, bytes32 rewardS) = vm.sign(0xB22DF, digest);
142      vm.expectRevert("Reward signature is invalid");
143      gasBroker.swap{ value: 1 ether }(
144        signer,
145        address(token),
146        VALUE,
147        deadline,
148        REWARD,
149        permitV,
150        permitR,
151        permitS,
152        rewardV,
153        rewardR,
154        rewardS
155      );
156    }
157  
158  
159    function test_shouldRevertWhenValueInRewardMessageIsInvalid() public {
160      (uint8 rewardV, bytes32 rewardR, bytes32 rewardS) = getRewardSignature(REWARD + 1, permitHash);
161      vm.expectRevert("Reward signature is invalid");
162      gasBroker.swap{ value: 1 ether }(
163        signer,
164        address(token),
165        VALUE,
166        deadline,
167        REWARD,
168        permitV,
169        permitR,
170        permitS,
171        rewardV,
172        rewardR,
173        rewardS
174      );
175    }
176  
177    function test_shouldRevertWhenNotEnouthETHisProvided() public {
178      vm.expectRevert("Not enough ETH provided");
179      gasBroker.swap{ value: 1 ether - 1 }(
180        signer,
181        address(token),
182        VALUE,
183        deadline,
184        REWARD,
185        permitV,
186        permitR,
187        permitS,
188        rewardV,
189        rewardR,
190        rewardS
191      );
192    }
193  
194  
195    function test_shouldSwapTokensToETH() public {
196      gasBroker.swap{ value: 1 ether }(
197        signer,
198        address(token),
199        VALUE,
200        deadline,
201        REWARD,
202        permitV,
203        permitR,
204        permitS,
205        rewardV,
206        rewardR,
207        rewardS
208      );
209  
210      assertEq(token.balanceOf(address(this)), VALUE);
211      assertEq(token.balanceOf(signer), SIGNER_TOKEN_BALANCE - VALUE);
212      assertEq(signer.balance, 1 ether);
213    }
214  }