├── .devcontainer └── devcontainer.json ├── .github └── dependabot.yml ├── .gitignore ├── .gitmodules ├── README.md ├── foundry.toml ├── package-lock.json ├── package.json ├── src ├── Gas.sol └── Ownable.sol └── test └── Gas.UnitTests.t.sol /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Gas Optimizations", 3 | "image": "mcr.microsoft.com/devcontainers/rust:1-1-bullseye", 4 | // FIXME: Uncomment feature if foundry adds this https://github.com/foundry-rs/foundry/issues/7290 5 | // "features": { 6 | // "ghcr.io/foundry-rs/foundry": {} 7 | // } 8 | // "postCreateCommand": "forge test --gas-report", 9 | 10 | // TODO: Remove commands if this feature is added https://github.com/foundry-rs/foundry/issues/7290 11 | "postCreateCommand": "curl -L https://foundry.paradigm.xyz | bash", 12 | "postStartCommand": "/home/vscode/.foundry/bin/foundryup", 13 | "postAttachCommand": "/home/vscode/.foundry/bin/forge test --gas-report", 14 | 15 | "customizations": { 16 | "vscode": { 17 | "extensions": [ 18 | "JuanBlanco.solidity", 19 | "hosho.solidity-debugger", 20 | "wayou.vscode-todo-highlight" 21 | ] 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for more information: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | # https://containers.dev/guide/dependabot 6 | 7 | version: 2 8 | updates: 9 | - package-ecosystem: "devcontainers" 10 | directory: "/" 11 | schedule: 12 | interval: weekly 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiler files 2 | cache/ 3 | out/ 4 | 5 | # Ignores development broadcast logs 6 | !/broadcast 7 | /broadcast/*/31337/ 8 | /broadcast/**/dry-run/ 9 | 10 | # Docs 11 | docs/ 12 | 13 | # Dotenv file 14 | .env 15 | .vscode/* 16 | !.vscode/settings.json 17 | !.vscode/tasks.json 18 | !.vscode/launch.json 19 | !.vscode/extensions.json 20 | !.vscode/*.code-snippets 21 | 22 | .github/workflows 23 | 24 | *lcov.info 25 | 26 | node_modules/.bin/ 27 | 28 | node_modules/ 29 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/forge-std"] 2 | path = lib/forge-std 3 | url = https://github.com/foundry-rs/forge-std 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GAS OPTIMSATION 2 | 3 | - Your task is to edit and optimise the Gas.sol contract. 4 | - You cannot edit the tests & 5 | - All the tests must pass. 6 | - You can change the functionality of the contract as long as the tests pass. 7 | - Try to get the gas usage as low as possible. 8 | 9 | 10 | 11 | ## To run tests & gas report with verbatim trace 12 | Run: `forge test --gas-report -vvvv` 13 | 14 | ## To run tests & gas report 15 | Run: `forge test --gas-report` 16 | 17 | ## To run a specific test 18 | RUN:`forge test --match-test {TESTNAME} -vvvv` 19 | EG: `forge test --match-test test_onlyOwner -vvvv` -------------------------------------------------------------------------------- /foundry.toml: -------------------------------------------------------------------------------- 1 | [profile.default] 2 | src = 'src' 3 | out = 'out' 4 | libs = ['lib'] 5 | 6 | 7 | # See more config options https://github.com/foundry-rs/foundry/tree/master/config -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "GasOptimisationFoundry", 3 | "lockfileVersion": 3, 4 | "requires": true, 5 | "packages": { 6 | "": { 7 | "dependencies": { 8 | "solc": "^0.8.26" 9 | } 10 | }, 11 | "node_modules/command-exists": { 12 | "version": "1.2.9", 13 | "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz", 14 | "integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==" 15 | }, 16 | "node_modules/commander": { 17 | "version": "8.3.0", 18 | "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", 19 | "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", 20 | "engines": { 21 | "node": ">= 12" 22 | } 23 | }, 24 | "node_modules/follow-redirects": { 25 | "version": "1.15.6", 26 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", 27 | "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", 28 | "funding": [ 29 | { 30 | "type": "individual", 31 | "url": "https://github.com/sponsors/RubenVerborgh" 32 | } 33 | ], 34 | "engines": { 35 | "node": ">=4.0" 36 | }, 37 | "peerDependenciesMeta": { 38 | "debug": { 39 | "optional": true 40 | } 41 | } 42 | }, 43 | "node_modules/js-sha3": { 44 | "version": "0.8.0", 45 | "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", 46 | "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" 47 | }, 48 | "node_modules/memorystream": { 49 | "version": "0.3.1", 50 | "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", 51 | "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", 52 | "engines": { 53 | "node": ">= 0.10.0" 54 | } 55 | }, 56 | "node_modules/os-tmpdir": { 57 | "version": "1.0.2", 58 | "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", 59 | "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", 60 | "engines": { 61 | "node": ">=0.10.0" 62 | } 63 | }, 64 | "node_modules/semver": { 65 | "version": "5.7.2", 66 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", 67 | "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", 68 | "bin": { 69 | "semver": "bin/semver" 70 | } 71 | }, 72 | "node_modules/solc": { 73 | "version": "0.8.26", 74 | "resolved": "https://registry.npmjs.org/solc/-/solc-0.8.26.tgz", 75 | "integrity": "sha512-yiPQNVf5rBFHwN6SIf3TUUvVAFKcQqmSUFeq+fb6pNRCo0ZCgpYOZDi3BVoezCPIAcKrVYd/qXlBLUP9wVrZ9g==", 76 | "dependencies": { 77 | "command-exists": "^1.2.8", 78 | "commander": "^8.1.0", 79 | "follow-redirects": "^1.12.1", 80 | "js-sha3": "0.8.0", 81 | "memorystream": "^0.3.1", 82 | "semver": "^5.5.0", 83 | "tmp": "0.0.33" 84 | }, 85 | "bin": { 86 | "solcjs": "solc.js" 87 | }, 88 | "engines": { 89 | "node": ">=10.0.0" 90 | } 91 | }, 92 | "node_modules/tmp": { 93 | "version": "0.0.33", 94 | "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", 95 | "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", 96 | "dependencies": { 97 | "os-tmpdir": "~1.0.2" 98 | }, 99 | "engines": { 100 | "node": ">=0.6.0" 101 | } 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "solc": "^0.8.26" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/Gas.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.0; 3 | 4 | import "./Ownable.sol"; 5 | 6 | contract Constants { 7 | uint256 public tradeFlag = 1; 8 | uint256 public basicFlag = 0; 9 | uint256 public dividendFlag = 1; 10 | } 11 | 12 | contract GasContract is Ownable, Constants { 13 | uint256 public totalSupply = 0; // cannot be updated 14 | uint256 public paymentCounter = 0; 15 | mapping(address => uint256) public balances; 16 | uint256 public tradePercent = 12; 17 | address public contractOwner; 18 | uint256 public tradeMode = 0; 19 | mapping(address => Payment[]) public payments; 20 | mapping(address => uint256) public whitelist; 21 | address[5] public administrators; 22 | bool public isReady = false; 23 | enum PaymentType { 24 | Unknown, 25 | BasicPayment, 26 | Refund, 27 | Dividend, 28 | GroupPayment 29 | } 30 | PaymentType constant defaultPayment = PaymentType.Unknown; 31 | 32 | History[] public paymentHistory; // when a payment was updated 33 | 34 | struct Payment { 35 | PaymentType paymentType; 36 | uint256 paymentID; 37 | bool adminUpdated; 38 | string recipientName; // max 8 characters 39 | address recipient; 40 | address admin; // administrators address 41 | uint256 amount; 42 | } 43 | 44 | struct History { 45 | uint256 lastUpdate; 46 | address updatedBy; 47 | uint256 blockNumber; 48 | } 49 | uint256 wasLastOdd = 1; 50 | mapping(address => uint256) public isOddWhitelistUser; 51 | 52 | struct ImportantStruct { 53 | uint256 amount; 54 | uint256 valueA; // max 3 digits 55 | uint256 bigValue; 56 | uint256 valueB; // max 3 digits 57 | bool paymentStatus; 58 | address sender; 59 | } 60 | mapping(address => ImportantStruct) public whiteListStruct; 61 | 62 | event AddedToWhitelist(address userAddress, uint256 tier); 63 | 64 | modifier onlyAdminOrOwner() { 65 | address senderOfTx = msg.sender; 66 | if (checkForAdmin(senderOfTx)) { 67 | require( 68 | checkForAdmin(senderOfTx), 69 | "Gas Contract Only Admin Check- Caller not admin" 70 | ); 71 | _; 72 | } else if (senderOfTx == contractOwner) { 73 | _; 74 | } else { 75 | revert( 76 | "Error in Gas contract - onlyAdminOrOwner modifier : revert happened because the originator of the transaction was not the admin, and furthermore he wasn't the owner of the contract, so he cannot run this function" 77 | ); 78 | } 79 | } 80 | 81 | modifier checkIfWhiteListed(address sender) { 82 | address senderOfTx = msg.sender; 83 | require( 84 | senderOfTx == sender, 85 | "Gas Contract CheckIfWhiteListed modifier : revert happened because the originator of the transaction was not the sender" 86 | ); 87 | uint256 usersTier = whitelist[senderOfTx]; 88 | require( 89 | usersTier > 0, 90 | "Gas Contract CheckIfWhiteListed modifier : revert happened because the user is not whitelisted" 91 | ); 92 | require( 93 | usersTier < 4, 94 | "Gas Contract CheckIfWhiteListed modifier : revert happened because the user's tier is incorrect, it cannot be over 4 as the only tier we have are: 1, 2, 3; therfore 4 is an invalid tier for the whitlist of this contract. make sure whitlist tiers were set correctly" 95 | ); 96 | _; 97 | } 98 | 99 | event supplyChanged(address indexed, uint256 indexed); 100 | event Transfer(address recipient, uint256 amount); 101 | event PaymentUpdated( 102 | address admin, 103 | uint256 ID, 104 | uint256 amount, 105 | string recipient 106 | ); 107 | event WhiteListTransfer(address indexed); 108 | 109 | constructor(address[] memory _admins, uint256 _totalSupply) { 110 | contractOwner = msg.sender; 111 | totalSupply = _totalSupply; 112 | 113 | for (uint256 ii = 0; ii < administrators.length; ii++) { 114 | if (_admins[ii] != address(0)) { 115 | administrators[ii] = _admins[ii]; 116 | if (_admins[ii] == contractOwner) { 117 | balances[contractOwner] = totalSupply; 118 | } else { 119 | balances[_admins[ii]] = 0; 120 | } 121 | if (_admins[ii] == contractOwner) { 122 | emit supplyChanged(_admins[ii], totalSupply); 123 | } else if (_admins[ii] != contractOwner) { 124 | emit supplyChanged(_admins[ii], 0); 125 | } 126 | } 127 | } 128 | } 129 | 130 | function getPaymentHistory() 131 | public 132 | payable 133 | returns (History[] memory paymentHistory_) 134 | { 135 | return paymentHistory; 136 | } 137 | 138 | function checkForAdmin(address _user) public view returns (bool admin_) { 139 | bool admin = false; 140 | for (uint256 ii = 0; ii < administrators.length; ii++) { 141 | if (administrators[ii] == _user) { 142 | admin = true; 143 | } 144 | } 145 | return admin; 146 | } 147 | 148 | function balanceOf(address _user) public view returns (uint256 balance_) { 149 | uint256 balance = balances[_user]; 150 | return balance; 151 | } 152 | 153 | function getTradingMode() public view returns (bool mode_) { 154 | bool mode = false; 155 | if (tradeFlag == 1 || dividendFlag == 1) { 156 | mode = true; 157 | } else { 158 | mode = false; 159 | } 160 | return mode; 161 | } 162 | 163 | 164 | function addHistory(address _updateAddress, bool _tradeMode) 165 | public 166 | returns (bool status_, bool tradeMode_) 167 | { 168 | History memory history; 169 | history.blockNumber = block.number; 170 | history.lastUpdate = block.timestamp; 171 | history.updatedBy = _updateAddress; 172 | paymentHistory.push(history); 173 | bool[] memory status = new bool[](tradePercent); 174 | for (uint256 i = 0; i < tradePercent; i++) { 175 | status[i] = true; 176 | } 177 | return ((status[0] == true), _tradeMode); 178 | } 179 | 180 | function getPayments(address _user) 181 | public 182 | view 183 | returns (Payment[] memory payments_) 184 | { 185 | require( 186 | _user != address(0), 187 | "Gas Contract - getPayments function - User must have a valid non zero address" 188 | ); 189 | return payments[_user]; 190 | } 191 | 192 | function transfer( 193 | address _recipient, 194 | uint256 _amount, 195 | string calldata _name 196 | ) public returns (bool status_) { 197 | address senderOfTx = msg.sender; 198 | require( 199 | balances[senderOfTx] >= _amount, 200 | "Gas Contract - Transfer function - Sender has insufficient Balance" 201 | ); 202 | require( 203 | bytes(_name).length < 9, 204 | "Gas Contract - Transfer function - The recipient name is too long, there is a max length of 8 characters" 205 | ); 206 | balances[senderOfTx] -= _amount; 207 | balances[_recipient] += _amount; 208 | emit Transfer(_recipient, _amount); 209 | Payment memory payment; 210 | payment.admin = address(0); 211 | payment.adminUpdated = false; 212 | payment.paymentType = PaymentType.BasicPayment; 213 | payment.recipient = _recipient; 214 | payment.amount = _amount; 215 | payment.recipientName = _name; 216 | payment.paymentID = ++paymentCounter; 217 | payments[senderOfTx].push(payment); 218 | bool[] memory status = new bool[](tradePercent); 219 | for (uint256 i = 0; i < tradePercent; i++) { 220 | status[i] = true; 221 | } 222 | return (status[0] == true); 223 | } 224 | 225 | function updatePayment( 226 | address _user, 227 | uint256 _ID, 228 | uint256 _amount, 229 | PaymentType _type 230 | ) public onlyAdminOrOwner { 231 | require( 232 | _ID > 0, 233 | "Gas Contract - Update Payment function - ID must be greater than 0" 234 | ); 235 | require( 236 | _amount > 0, 237 | "Gas Contract - Update Payment function - Amount must be greater than 0" 238 | ); 239 | require( 240 | _user != address(0), 241 | "Gas Contract - Update Payment function - Administrator must have a valid non zero address" 242 | ); 243 | 244 | address senderOfTx = msg.sender; 245 | 246 | for (uint256 ii = 0; ii < payments[_user].length; ii++) { 247 | if (payments[_user][ii].paymentID == _ID) { 248 | payments[_user][ii].adminUpdated = true; 249 | payments[_user][ii].admin = _user; 250 | payments[_user][ii].paymentType = _type; 251 | payments[_user][ii].amount = _amount; 252 | bool tradingMode = getTradingMode(); 253 | addHistory(_user, tradingMode); 254 | emit PaymentUpdated( 255 | senderOfTx, 256 | _ID, 257 | _amount, 258 | payments[_user][ii].recipientName 259 | ); 260 | } 261 | } 262 | } 263 | 264 | function addToWhitelist(address _userAddrs, uint256 _tier) 265 | public 266 | onlyAdminOrOwner 267 | { 268 | require( 269 | _tier < 255, 270 | "Gas Contract - addToWhitelist function - tier level should not be greater than 255" 271 | ); 272 | whitelist[_userAddrs] = _tier; 273 | if (_tier > 3) { 274 | whitelist[_userAddrs] -= _tier; 275 | whitelist[_userAddrs] = 3; 276 | } else if (_tier == 1) { 277 | whitelist[_userAddrs] -= _tier; 278 | whitelist[_userAddrs] = 1; 279 | } else if (_tier > 0 && _tier < 3) { 280 | whitelist[_userAddrs] -= _tier; 281 | whitelist[_userAddrs] = 2; 282 | } 283 | uint256 wasLastAddedOdd = wasLastOdd; 284 | if (wasLastAddedOdd == 1) { 285 | wasLastOdd = 0; 286 | isOddWhitelistUser[_userAddrs] = wasLastAddedOdd; 287 | } else if (wasLastAddedOdd == 0) { 288 | wasLastOdd = 1; 289 | isOddWhitelistUser[_userAddrs] = wasLastAddedOdd; 290 | } else { 291 | revert("Contract hacked, imposible, call help"); 292 | } 293 | emit AddedToWhitelist(_userAddrs, _tier); 294 | } 295 | 296 | function whiteTransfer( 297 | address _recipient, 298 | uint256 _amount 299 | ) public checkIfWhiteListed(msg.sender) { 300 | address senderOfTx = msg.sender; 301 | whiteListStruct[senderOfTx] = ImportantStruct(_amount, 0, 0, 0, true, msg.sender); 302 | 303 | require( 304 | balances[senderOfTx] >= _amount, 305 | "Gas Contract - whiteTransfers function - Sender has insufficient Balance" 306 | ); 307 | require( 308 | _amount > 3, 309 | "Gas Contract - whiteTransfers function - amount to send have to be bigger than 3" 310 | ); 311 | balances[senderOfTx] -= _amount; 312 | balances[_recipient] += _amount; 313 | balances[senderOfTx] += whitelist[senderOfTx]; 314 | balances[_recipient] -= whitelist[senderOfTx]; 315 | 316 | emit WhiteListTransfer(_recipient); 317 | } 318 | 319 | function getPaymentStatus(address sender) public view returns (bool, uint256) { 320 | return (whiteListStruct[sender].paymentStatus, whiteListStruct[sender].amount); 321 | } 322 | 323 | receive() external payable { 324 | payable(msg.sender).transfer(msg.value); 325 | } 326 | 327 | 328 | fallback() external payable { 329 | payable(msg.sender).transfer(msg.value); 330 | } 331 | } -------------------------------------------------------------------------------- /src/Ownable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | /** 5 | * @dev Provides information about the current execution context, including the 6 | * sender of the transaction and its data. While these are generally available 7 | * via msg.sender and msg.data, they should not be accessed in such a direct 8 | * manner, since when dealing with meta-transactions the account sending and 9 | * paying for execution may not be the actual sender (as far as an application 10 | * is concerned). 11 | * 12 | * This contract is only required for intermediate, library-like contracts. 13 | */ 14 | abstract contract Context { 15 | function _msgSender() internal view virtual returns (address) { 16 | return msg.sender; 17 | } 18 | 19 | function _msgData() internal view virtual returns (bytes calldata) { 20 | return msg.data; 21 | } 22 | } 23 | 24 | /** 25 | * @dev Contract module which provides a basic access control mechanism, where 26 | * there is an account (an owner) that can be granted exclusive access to 27 | * specific functions. 28 | * 29 | * By default, the owner account will be the one that deploys the contract. This 30 | * can later be changed with {transferOwnership}. 31 | * 32 | * This module is used through inheritance. It will make available the modifier 33 | * `onlyOwner`, which can be applied to your functions to restrict their use to 34 | * the owner. 35 | */ 36 | abstract contract Ownable is Context { 37 | address private _owner; 38 | 39 | event OwnershipTransferred( 40 | address indexed previousOwner, 41 | address indexed newOwner 42 | ); 43 | 44 | /** 45 | * @dev Initializes the contract setting the deployer as the initial owner. 46 | */ 47 | constructor() { 48 | _transferOwnership(_msgSender()); 49 | } 50 | 51 | /** 52 | * @dev Throws if called by any account other than the owner. 53 | */ 54 | modifier onlyOwner() { 55 | _checkOwner(); 56 | _; 57 | } 58 | 59 | /** 60 | * @dev Returns the address of the current owner. 61 | */ 62 | function owner() public view virtual returns (address) { 63 | return _owner; 64 | } 65 | 66 | /** 67 | * @dev Throws if the sender is not the owner. 68 | */ 69 | function _checkOwner() internal view virtual { 70 | require(owner() == _msgSender(), "Ownable: caller is not the owner"); 71 | } 72 | 73 | /** 74 | * @dev Leaves the contract without owner. It will not be possible to call 75 | * `onlyOwner` functions anymore. Can only be called by the current owner. 76 | * 77 | * NOTE: Renouncing ownership will leave the contract without an owner, 78 | * thereby removing any functionality that is only available to the owner. 79 | */ 80 | function renounceOwnership() public virtual onlyOwner { 81 | _transferOwnership(address(0)); 82 | } 83 | 84 | /** 85 | * @dev Transfers ownership of the contract to a new account (`newOwner`). 86 | * Can only be called by the current owner. 87 | */ 88 | function transferOwnership(address newOwner) public virtual onlyOwner { 89 | require( 90 | newOwner != address(0), 91 | "Ownable: new owner is the zero address" 92 | ); 93 | _transferOwnership(newOwner); 94 | } 95 | 96 | /** 97 | * @dev Transfers ownership of the contract to a new account (`newOwner`). 98 | * Internal function without access restriction. 99 | */ 100 | function _transferOwnership(address newOwner) internal virtual { 101 | address oldOwner = _owner; 102 | _owner = newOwner; 103 | emit OwnershipTransferred(oldOwner, newOwner); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /test/Gas.UnitTests.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.0; 3 | 4 | import "forge-std/Test.sol"; 5 | import "../src/Gas.sol"; 6 | 7 | 8 | 9 | 10 | contract GasTest is Test { 11 | GasContract public gas; 12 | uint256 public totalSupply = 1000000000; 13 | address owner = address(0x1234); 14 | address addr1 = address(0x5678); 15 | address addr2 = address(0x9101); 16 | address addr3 = address(0x1213); 17 | 18 | address[] admins = [ 19 | address(0x1), 20 | address(0x2), 21 | address(0x3), 22 | address(0x4), 23 | owner 24 | ]; 25 | 26 | 27 | function get_random_address(uint256 offset) internal returns (address) { 28 | uint time_now = vm.unixTime(); 29 | return (vm.addr(time_now + offset)); 30 | } 31 | 32 | 33 | function setUp() public { 34 | 35 | 36 | 37 | for (uint8 ii = 0; ii < 4 ; ii++){ 38 | admins[ii] = get_random_address(ii); 39 | } 40 | 41 | vm.startPrank(owner); 42 | gas = new GasContract(admins, totalSupply); 43 | vm.stopPrank(); 44 | 45 | } 46 | 47 | function test_admins() public view { 48 | for (uint8 i = 0; i < admins.length; ++i) { 49 | assertEq(admins[i], gas.administrators(i)); 50 | } 51 | } 52 | 53 | function test_onlyOwner(address _userAddrs, uint256 _tier) public { 54 | vm.assume(_userAddrs != address(gas)); 55 | _tier = bound( _tier, 1, 244); 56 | vm.expectRevert(); 57 | gas.addToWhitelist(_userAddrs, _tier); 58 | } 59 | 60 | function test_tiers(address _userAddrs, uint256 _tier) public { 61 | vm.assume(_userAddrs != address(gas)); 62 | _tier = bound( _tier, 1, 244); 63 | vm.prank(owner); 64 | gas.addToWhitelist(_userAddrs, _tier); 65 | } 66 | 67 | // Expect Event --> 68 | event AddedToWhitelist(address userAddress, uint256 tier); 69 | function test_whitelistEvents(address _userAddrs, uint256 _tier) public { 70 | vm.startPrank(owner); 71 | vm.assume(_userAddrs != address(gas)); 72 | _tier = bound( _tier, 1, 244); 73 | vm.expectEmit(true, true, false, true); 74 | emit AddedToWhitelist(_userAddrs, _tier); 75 | gas.addToWhitelist(_userAddrs, _tier); 76 | vm.stopPrank(); 77 | } 78 | 79 | 80 | //----------------------------------------------------// 81 | //------------- Test whitelist Transfers -------------// 82 | //----------------------------------------------------// 83 | 84 | function test_whitelistTransfer( 85 | address _recipient, 86 | address _sender, 87 | uint256 _amount, 88 | string calldata _name, 89 | uint256 _tier 90 | ) public { 91 | _amount = bound(_amount,0 , gas.balanceOf(owner)); 92 | vm.assume(_amount > 3); 93 | vm.assume(bytes(_name).length < 9 ); 94 | _tier = bound( _tier, 1, 244); 95 | vm.startPrank(owner); 96 | gas.transfer(_sender, _amount, _name); 97 | gas.addToWhitelist(_sender, _tier); 98 | vm.stopPrank(); 99 | vm.prank(_sender); 100 | gas.whiteTransfer(_recipient, _amount); 101 | (bool a, uint256 b) = gas.getPaymentStatus(address(_sender)); 102 | // console.log(a); 103 | assertEq(a, true); 104 | assertEq(b, _amount); 105 | } 106 | 107 | // Reverts if tiers out of bounds 108 | function test_tiersReverts(address _userAddrs, uint256 _tier) public { 109 | vm.assume(_userAddrs != address(gas)); 110 | vm.assume(_tier > 254); 111 | vm.prank(owner); 112 | vm.expectRevert(); 113 | gas.addToWhitelist(_userAddrs, _tier); 114 | } 115 | 116 | // Expect Event --> 117 | event WhiteListTransfer(address indexed); 118 | 119 | function test_whitelistEvents( 120 | address _recipient, 121 | address _sender, 122 | uint256 _amount, 123 | string calldata _name, 124 | uint256 _tier 125 | ) public { 126 | 127 | _amount = bound(_amount,0 , gas.balanceOf(owner)); 128 | vm.assume(_amount > 3); 129 | vm.assume(bytes(_name).length < 9 ); 130 | _tier = bound( _tier, 1, 244); 131 | vm.startPrank(owner); 132 | gas.transfer(_sender, _amount, _name); 133 | gas.addToWhitelist(_sender, _tier); 134 | vm.stopPrank(); 135 | vm.startPrank(_sender); 136 | vm.expectEmit(true, false, false, true); 137 | emit WhiteListTransfer(_recipient); 138 | gas.whiteTransfer(_recipient, _amount); 139 | vm.stopPrank(); 140 | } 141 | 142 | /* whiteTranfer balance logic. 143 | balances[senderOfTx] -= _amount; 144 | balances[_recipient] += _amount; 145 | balances[senderOfTx] += whitelist[senderOfTx]; 146 | balances[_recipient] -= whitelist[senderOfTx]; 147 | */ 148 | 149 | // check balances update 150 | 151 | 152 | function testWhiteTranferAmountUpdate( 153 | uint256 _amount, 154 | string calldata _name, 155 | uint256 _tier 156 | ) public { 157 | address _recipient = get_random_address(23); 158 | address _sender = get_random_address(37); 159 | 160 | uint256 _preRecipientAmount = gas.balances(_recipient) + 0; 161 | vm.assume(_recipient != address(0)); 162 | vm.assume(_sender != address(0)); 163 | _amount = bound(_amount,0 , gas.balanceOf(owner)); 164 | _tier = bound( _tier, 1, 244); 165 | vm.assume(_amount > 3); 166 | vm.assume(bytes(_name).length < 9 && bytes(_name).length >0); 167 | vm.startPrank(owner); 168 | gas.transfer(_sender, _amount, _name); 169 | uint256 _preSenderAmount = gas.balances(_sender); 170 | gas.addToWhitelist(_sender, _tier); 171 | vm.stopPrank(); 172 | vm.prank(_sender); 173 | gas.whiteTransfer(_recipient, _amount); 174 | assertEq(gas.balances(_sender), (_preSenderAmount - _amount) + gas.whitelist(_sender)); 175 | assertEq(gas.balances(_recipient),(_preRecipientAmount + _amount) - gas.whitelist(_sender)); 176 | } 177 | 178 | function testBalanceOf() public view { 179 | uint256 bal = gas.balanceOf(owner); 180 | assertEq(bal, totalSupply); 181 | } 182 | 183 | function testCheckForAdmin() public view { 184 | bool isAdmin = gas.checkForAdmin(owner); 185 | assertEq(isAdmin, true); 186 | } 187 | 188 | 189 | function testTransfer(uint256 _amount, address _recipient) public { 190 | vm.assume(_amount <= totalSupply); 191 | vm.startPrank(owner); 192 | 193 | uint256 ownerBal = gas.balanceOf(owner); 194 | uint256 balBefore = gas.balanceOf(_recipient); 195 | gas.transfer(_recipient, _amount, "name"); 196 | uint256 balAfter = gas.balanceOf(_recipient); 197 | 198 | if(_recipient == owner) { 199 | assertEq(balAfter, balBefore); 200 | assertEq(gas.balanceOf(owner), ownerBal); 201 | } else { 202 | assertEq(balAfter, balBefore + _amount); 203 | assertEq(gas.balanceOf(owner), ownerBal - _amount); 204 | } 205 | } 206 | 207 | function testAddToWhitelist(uint256 tier) public { 208 | vm.expectRevert(); 209 | address user = get_random_address(13); 210 | vm.startPrank(user); 211 | vm.assume(user != owner); 212 | gas.addToWhitelist(user, tier); 213 | vm.stopPrank(); 214 | } 215 | 216 | 217 | 218 | } 219 | --------------------------------------------------------------------------------