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 }