├── .env.example ├── .eslintignore ├── .eslintrc.js ├── .github ├── cover.png ├── semantic.yml └── workflows │ ├── install │ └── action.yml │ ├── lint.yml │ └── unit_test.yml ├── .gitignore ├── .prettierignore ├── .prettierrc ├── .solcover.js ├── README.md ├── contracts ├── examples │ ├── nft-auction │ │ ├── NFTAuction.sol │ │ └── components │ │ │ ├── Errors.sol │ │ │ └── NFTAuctionConfig.sol │ └── simple-storage │ │ └── SimpleStorage.sol └── vendor │ ├── fee │ ├── FeeContract.sol │ └── interfaces │ │ ├── IFeeContract.sol │ │ └── IFeeOracle.sol │ ├── h1-developed-application │ ├── H1DevelopedApplication.sol │ ├── components │ │ ├── Errors.sol │ │ ├── H1DevelopedAccessControl.sol │ │ ├── H1DevelopedPausable.sol │ │ └── H1DevelopedUtils.sol │ └── interfaces │ │ ├── IH1DevelopedApplication.sol │ │ └── IH1DevelopedPausable.sol │ ├── mocks │ ├── FixedFeeOracle.sol │ ├── MockAccountManager.sol │ ├── MockNFT.sol │ └── MockPermissionsInterface.sol │ └── proof-of-identity │ ├── ProofOfIdentity.sol │ ├── interfaces │ ├── IProofOfIdentity.sol │ └── vendor │ │ ├── IAccountManager.sol │ │ └── IPermissionsInterface.sol │ └── libraries │ ├── AttributeUtils.sol │ └── BytesConversion.sol ├── environment.d.ts ├── hardhat.config.ts ├── package-lock.json ├── package.json ├── scripts ├── deployLocal.ts └── deployTestnet.ts ├── tasks ├── accounts.ts └── index.ts ├── test ├── constants.ts ├── nft-auction │ ├── auction.test.ts │ └── setup.ts └── simple-storage │ ├── setup.ts │ └── simpleStorage.test.ts ├── tsconfig.json ├── utils ├── checkENV.ts ├── deploy │ ├── README.md │ ├── deployWrapper.ts │ ├── fee │ │ ├── deployFeeContract.ts │ │ └── index.ts │ ├── nft-auction │ │ ├── deployNFTAuction.ts │ │ └── index.ts │ ├── proof-of-identity │ │ ├── deployProofOfIdentity.ts │ │ └── index.ts │ ├── simple-storage │ │ ├── deploySimpleStorage.ts │ │ └── index.ts │ ├── upgrade.ts │ └── verifyWrapper.ts ├── dummyAddresses.ts ├── fnSelector.ts ├── json.ts ├── random.ts ├── time.ts ├── token.ts └── transaction.ts └── vendor-docs ├── fee └── FeeContract.md ├── h1-developed-application ├── H1DevelopedApplication.md └── components │ ├── Errors.md │ ├── H1DevelopedAccessControl.md │ ├── H1DevelopedPausable.md │ └── H1DevelopedUtils.md └── proof-of-identity ├── ProofOfIdentity.md └── libraries ├── AttributeUtils.md └── BytesConversion.md /.env.example: -------------------------------------------------------------------------------- 1 | # testnet 2 | TESTNET_CHAIN_ID=810 3 | HAVEN_TESTNET_RPC="https://testnet-rpc.haven1.org" 4 | 5 | TESTNET_EXPLORER="https://testnet-explorer.haven1.org" 6 | TESTNET_EXPLORER_API="" 7 | TESTNET_EXPLORER_API_KEY="" 8 | 9 | TESTNET_DEPLOYER="" 10 | TESTNET_ASSOCIATION_ADDRESS="" 11 | TESTNET_OPERATOR_ADDRESS="" 12 | TESTNET_DEV_ADDRESS="" 13 | TESTNET_FEE_CONTRACT="0xe003BdBa0529ED4E1F2B7f40E4E136904fcaD435" 14 | TESTNET_POI_CONTRACT="0xDb5ecc06e7C82d3ed4dBA699b8E8D0433A3ED729" 15 | 16 | # ============================ ADD YOUR ENVS HERE ============================ # 17 | 18 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | .git 2 | .github 3 | .openzeppelin 4 | node_modules 5 | artifacts 6 | cache 7 | typechain-types 8 | coverage 9 | coverage.json 10 | docs 11 | README.md 12 | renovate.json 13 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | es2021: true, 4 | node: true, 5 | }, 6 | extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"], 7 | parser: "@typescript-eslint/parser", 8 | parserOptions: { 9 | ecmaVersion: "latest", 10 | sourceType: "module", 11 | }, 12 | plugins: ["@typescript-eslint"], 13 | rules: { 14 | indent: "off", 15 | "@typescript-eslint/indent": ["error"], 16 | "linebreak-style": ["error", "unix"], 17 | quotes: ["error", "double"], 18 | semi: ["error", "always"], 19 | "prefer-const": ["off"], 20 | }, 21 | }; 22 | -------------------------------------------------------------------------------- /.github/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haven1network/dev-onboarding-template/cd0e3dcea6d177ade58d17485ee8fc11be227863/.github/cover.png -------------------------------------------------------------------------------- /.github/semantic.yml: -------------------------------------------------------------------------------- 1 | # Enable/disable creation of status checks 2 | enabled: true 3 | # Validate the PR title and all commit messages 4 | titleAndCommits: true 5 | # If commitsOnly or titleAndCommits is set to true, then only a single commit needs to pass validation instead of every commit 6 | anyCommit: true 7 | 8 | # Allow merge commits (e.g. 'Merge branch "master" into fix/delete-all-tests') 9 | allowMergeCommits: true 10 | # Allow revert commits (e.g. 'Revert "fix: delete all tests"') 11 | allowRevertCommits: true 12 | -------------------------------------------------------------------------------- /.github/workflows/install/action.yml: -------------------------------------------------------------------------------- 1 | name: Checkout and Setup 2 | description: 'Installs dependencies' 3 | 4 | runs: 5 | using: 'composite' 6 | steps: 7 | - name: Setup Node 8 | uses: actions/setup-node@v3 9 | with: 10 | node-version: 18 11 | cache: "npm" 12 | 13 | - name: Install node modules from npm 14 | shell: bash 15 | run: npm ci 16 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | types: 9 | - opened 10 | - edited 11 | - synchronize 12 | 13 | jobs: 14 | lint: 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - name: Checkout Repo 19 | uses: actions/checkout@v3 20 | 21 | - name: Install deps 22 | uses: ./.github/workflows/install 23 | 24 | - name: Run lint 25 | run: npm run lint 26 | 27 | -------------------------------------------------------------------------------- /.github/workflows/unit_test.yml: -------------------------------------------------------------------------------- 1 | name: Run Unit Tests 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | types: 9 | - opened 10 | - edited 11 | - synchronize 12 | 13 | jobs: 14 | unit-test: 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - name: Checkout Repo 19 | uses: actions/checkout@v3 20 | 21 | - name: Install deps 22 | uses: ./.github/workflows/install 23 | 24 | - name: Run the tests 25 | run: npm test 26 | 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | coverage 4 | coverage.json 5 | typechain 6 | typechain-types 7 | **/gas-report.txt 8 | 9 | # Hardhat files 10 | cache 11 | artifacts 12 | 13 | **/.DS_Store 14 | .vscode 15 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .git 2 | .github 3 | .openzeppelin 4 | node_modules 5 | artifacts 6 | cache 7 | typechain-types 8 | coverage 9 | coverage.json 10 | docs 11 | README.md 12 | renovate.json 13 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "es5", 3 | "tabWidth": 4, 4 | "semi": true, 5 | "singleQuote": false, 6 | "printWidth": 80, 7 | "arrowParens": "avoid", 8 | "overrides": [ 9 | { 10 | "files": "*.sol", 11 | "options": { 12 | "printWidth": 80, 13 | "tabWidth": 4, 14 | "useTabs": false, 15 | "singleQuote": false, 16 | "bracketSpacing": false, 17 | "trailingComma": "none" 18 | } 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /.solcover.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // See: 3 | // https://github.com/sc-forks/solidity-coverage 4 | skipFiles: ["vendor"], 5 | }; 6 | -------------------------------------------------------------------------------- /contracts/examples/nft-auction/NFTAuction.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | import "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol"; 5 | import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; 6 | 7 | import "../../vendor/h1-developed-application/H1DevelopedApplication.sol"; 8 | import "../../vendor/proof-of-identity/interfaces/IProofOfIdentity.sol"; 9 | 10 | import "./components/Errors.sol"; 11 | import {Config, ConfigUtils} from "./components/NFTAuctionConfig.sol"; 12 | 13 | /** 14 | * @title NFTAuction 15 | * @author Haven1 Development Team 16 | * @dev Example implementation of using the `ProofOfIdentity` and 17 | * `H1DevelopedApplication` contracts to create and permission an auction. 18 | */ 19 | contract NFTAuction is H1DevelopedApplication, ReentrancyGuardUpgradeable { 20 | /* TYPE DECLARATIONS 21 | ==================================================*/ 22 | using ConfigUtils for Config; 23 | 24 | /* STATE VARIABLES 25 | ==================================================*/ 26 | /** 27 | * @dev The address that will receive the funds after the auction is 28 | * finished. 29 | */ 30 | address private _beneficiary; 31 | 32 | /** 33 | * @dev The kind of the auction, either 1, 2 or 3. 34 | */ 35 | uint256 private _auctionKind; 36 | 37 | /** 38 | * @dev Whether the auction has started. 39 | */ 40 | bool private _started; 41 | 42 | /** 43 | * @dev Whether the auction has ended. 44 | */ 45 | bool private _finished; 46 | 47 | /** 48 | * @dev The length, in seconds, of the auction. 49 | */ 50 | uint256 private _auctionLength; 51 | 52 | /** 53 | * @dev The unix timestamp of when the auction ends. 54 | * If 0, the auction has not started. 55 | * End time = _auctionStartTime + _auctionLength; 56 | */ 57 | uint256 private _auctionEndTime; 58 | 59 | /** 60 | * @dev The Proof of Identity Contract. 61 | */ 62 | IProofOfIdentity private _proofOfIdentity; 63 | 64 | /** 65 | * @dev The address of the highest bidder. 66 | */ 67 | address private _highestBidder; 68 | 69 | /** 70 | * @dev The highest bid. 71 | */ 72 | uint256 private _highestBid; 73 | 74 | /** 75 | * @dev The NFT prize. 76 | */ 77 | IERC721Upgradeable private _nft; 78 | 79 | /** 80 | * @dev The ID of the NFT prize. 81 | */ 82 | uint256 private _nftId; 83 | 84 | /* EVENTS 85 | ==================================================*/ 86 | /** 87 | * @notice Notifies the start of an auction. 88 | * @param endTime The unix timestamp of the end of the auction. 89 | */ 90 | event AuctionStarted(uint256 endTime); 91 | 92 | /** 93 | * @notice Notifies the end of an auction. 94 | * @param winner The address of the winner. 95 | * @param bid The winning bid. 96 | */ 97 | event AuctionEnded(address indexed winner, uint256 bid); 98 | 99 | /** 100 | * @notice Emits the address of the bidder and their bid. 101 | * @param bidder The address of the bidder. 102 | * @param amount The bid amount. 103 | */ 104 | event BidPlaced(address indexed bidder, uint256 amount); 105 | 106 | /** 107 | * Emits the address of the winner and the winning bid. 108 | * @param winner The address of the winner. 109 | * @param amount The winning bid. 110 | */ 111 | event NFTSent(address indexed winner, uint256 amount); 112 | 113 | /* MODIFIERS 114 | ==================================================*/ 115 | /** 116 | * @dev Modifier to be used on any functions that require a user be 117 | * permissioned per this contract's definition. 118 | * 119 | * Ensures that the account: 120 | * - has a Proof of Identity NFT; 121 | * - is not suspended; and 122 | * - is of the requisite `userType`. 123 | * 124 | * May revert with `Auction__NoIdentityNFT`. 125 | * May revert with `Auction__Suspended`. 126 | * May revert with `Auction__UserType`. 127 | * May revert with `Auction__AttributeExpired`. 128 | */ 129 | modifier onlyPermissioned(address account) { 130 | // ensure the account has a Proof of Identity NFT 131 | if (!_hasID(account)) { 132 | revert Auction__NoIdentityNFT(); 133 | } 134 | 135 | // ensure the account is not suspended 136 | if (_isSuspended(account)) { 137 | revert Auction__Suspended(); 138 | } 139 | 140 | // ensure the account has a valid `userType` 141 | _validateUserTypeExn(account); 142 | _; 143 | } 144 | 145 | /* FUNCTIONS 146 | ==================================================*/ 147 | /* Constructor 148 | ========================================*/ 149 | 150 | /// @custom:oz-upgrades-unsafe-allow constructor 151 | constructor() { 152 | _disableInitializers(); 153 | } 154 | 155 | /** 156 | * @notice Initializes the `NFTAuction` contract. 157 | * 158 | * @param feeContract_ The address of the `FeeContract`. 159 | * 160 | * @param proofOfIdentity_ The address of the `ProofOfIdentity` contract. 161 | * 162 | * @param association_ The Association address. 163 | * 164 | * @param developer_ The developer address. 165 | * 166 | * @param feeCollector_ The address that is sent the earned developer fees. 167 | * 168 | * @param fnSigs_ An array of function signatures for which specific fees 169 | * will be set. 170 | * 171 | * @param fnFees_ An array of fees that will be set for their `fnSelector` 172 | * counterparts. 173 | * 174 | * @param auctionConfig_ A struct containing the auction config. 175 | */ 176 | function initialize( 177 | address feeContract_, 178 | address proofOfIdentity_, 179 | address association_, 180 | address developer_, 181 | address feeCollector_, 182 | string[] memory fnSigs_, 183 | uint256[] memory fnFees_, 184 | Config memory auctionConfig_ 185 | ) external initializer { 186 | _validateAddrExn(feeContract_); 187 | _validateAddrExn(proofOfIdentity_); 188 | _validateAddrExn(association_); 189 | _validateAddrExn(developer_); 190 | _validateAddrExn(feeCollector_); 191 | 192 | auctionConfig_.validateExn(); 193 | 194 | __H1DevelopedApplication_init( 195 | feeContract_, 196 | association_, 197 | developer_, 198 | feeCollector_, 199 | fnSigs_, 200 | fnFees_ 201 | ); 202 | 203 | __ReentrancyGuard_init(); 204 | __AccessControl_init(); 205 | __UUPSUpgradeable_init(); 206 | 207 | _proofOfIdentity = IProofOfIdentity(proofOfIdentity_); 208 | 209 | _auctionKind = auctionConfig_.kind; 210 | _auctionLength = auctionConfig_.length; 211 | _highestBid = auctionConfig_.startingBid; 212 | _nft = IERC721Upgradeable(auctionConfig_.nft); 213 | _nftId = auctionConfig_.nftID; 214 | _beneficiary = auctionConfig_.beneficiary; 215 | } 216 | 217 | /* External 218 | ========================================*/ 219 | /** 220 | * @notice Starts the auction. 221 | * @dev Only callable by an account with the role: `DEFAULT_ADMIN_ROLE`. 222 | * May revert with: 223 | * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ 224 | * May revert with `Auction__AuctionActive`. 225 | * May revert with `Auction__AuctionFinished`. 226 | * May emit an `AuctionStarted` event. 227 | */ 228 | function startAuction() external whenNotPaused onlyRole(DEV_ADMIN_ROLE) { 229 | // No need to check _finished as once started, `_started` does not get 230 | // flipped back false 231 | if (_started) { 232 | revert Auction__AuctionActive(); 233 | } 234 | 235 | _nft.transferFrom(msg.sender, address(this), _nftId); 236 | 237 | _started = true; 238 | _auctionEndTime = block.timestamp + _auctionLength; 239 | 240 | emit AuctionStarted(_auctionEndTime); 241 | } 242 | 243 | /** 244 | * @notice Places a bid. If the bid placed is not higher than the current 245 | * highest bid, this function will revert. 246 | * 247 | * If the bid is sufficiently high, the previous bid will be refunded to the 248 | * previous highest bidder. 249 | * 250 | * @dev May revert with `Auction__NoIdentityNFT`. 251 | * May revert with `Auction__Suspended`. 252 | * May revert with `Auction__UserType`. 253 | * May revert with `Auction__AttributeExpired`. 254 | * May revert with `Auction__AuctionNotStarted`. 255 | * May revert with `Auction__AuctionFinished`. 256 | * May revert with `Auction__BidTooLow`. 257 | * May emit a `BidPlaced` event. 258 | */ 259 | function bid() 260 | external 261 | payable 262 | nonReentrant 263 | whenNotPaused 264 | onlyPermissioned(msg.sender) 265 | developerFee(true, false) 266 | { 267 | uint256 val = msgValueAfterFee(); 268 | if (val == 0) { 269 | revert Auction__ZeroValue(); 270 | } 271 | 272 | if (!hasStarted()) { 273 | revert Auction__AuctionNotStarted(); 274 | } 275 | 276 | if (hasFinished()) { 277 | revert Auction__AuctionFinished(); 278 | } 279 | 280 | if (val <= _highestBid) { 281 | revert Auction__BidTooLow(val, _highestBid); 282 | } 283 | 284 | if (msg.sender == _highestBidder) { 285 | revert Auction__AlreadyHighestBidder(); 286 | } 287 | 288 | _refundBid(); 289 | 290 | _highestBidder = msg.sender; 291 | _highestBid = val; 292 | 293 | emit BidPlaced(msg.sender, val); 294 | } 295 | 296 | /** 297 | * @notice Ends the auction. 298 | * 299 | * @dev May revert with `Auction__AuctionNotStarted`. 300 | * May revert with `Auction__AuctionActive`. 301 | * May revert with `Auction__AuctionFinished`. 302 | * May revert with `Auction__TransferFailed`. 303 | * May emit an `NFTSent` event. 304 | * May emit an `AuctionEnded` event. 305 | */ 306 | function endAuction() external whenNotPaused nonReentrant { 307 | // test if the auction has started 308 | if (!hasStarted()) { 309 | revert Auction__AuctionNotStarted(); 310 | } 311 | 312 | // if the auction has started but the current ts is less than the end 313 | // time then the auction is still in progress 314 | if (inProgress()) { 315 | revert Auction__AuctionActive(); 316 | } 317 | 318 | // test if the auction has finished 319 | if (_finished) { 320 | revert Auction__AuctionFinished(); 321 | } 322 | 323 | _finished = true; 324 | 325 | if (_highestBidder != address(0)) { 326 | _nft.safeTransferFrom(address(this), _highestBidder, _nftId); 327 | 328 | bool success = _withdraw(_beneficiary, address(this).balance); 329 | 330 | if (!success) { 331 | revert Auction__TransferFailed(); 332 | } 333 | 334 | emit NFTSent(_highestBidder, _highestBid); 335 | } else { 336 | _nft.safeTransferFrom(address(this), _beneficiary, _nftId); 337 | } 338 | 339 | emit AuctionEnded(_highestBidder, _highestBid); 340 | } 341 | 342 | /** 343 | * @notice Returns whether an `account` is eligible to participate in the 344 | * auction. 345 | * 346 | * @param acc The account to check. 347 | * 348 | * @return True if the account can place a bid, false otherwise. 349 | * 350 | * @dev Requires that the account: 351 | * - has a Proof of Identity NFT; 352 | * - is not suspended; and 353 | * - has the requisite `userType`. 354 | */ 355 | function accountEligible(address acc) external view returns (bool) { 356 | return _hasID(acc) && !_isSuspended(acc) && _validateUserType(acc); 357 | } 358 | 359 | /** 360 | * @notice Returns the highest bidder. If the auction has ended, returns 361 | * the winner of the auction. 362 | * @return The address of the highest / winning bidder. 363 | */ 364 | function getHighestBidder() external view returns (address) { 365 | return _highestBidder; 366 | } 367 | 368 | /** 369 | * @notice Returns the highest bid. If the auction has ended, returns the 370 | * winning bid. 371 | * @return The highest / winning bid. 372 | */ 373 | function getHighestBid() external view returns (uint256) { 374 | return _highestBid; 375 | } 376 | 377 | /** 378 | * @notice Returns the address of the prize NFT and the NFT ID. 379 | * @return The address of the prize NFT and its ID. 380 | */ 381 | function getNFT() external view returns (address, uint256) { 382 | return (address(_nft), _nftId); 383 | } 384 | 385 | /** 386 | * @notice Returns the unix timestamp of when the auction is finished. 387 | * @return The unix timestamp of when the auction is finished. 388 | */ 389 | function getFinishTime() external view returns (uint256) { 390 | return _auctionEndTime; 391 | } 392 | 393 | /** 394 | * @notice Returns the kind of the auction: 395 | * - 1: Retail 396 | * - 2: Institution 397 | * - 3: All 398 | * 399 | * @return The kind of the auction. 400 | */ 401 | function getAuctionKind() external view returns (uint256) { 402 | return _auctionKind; 403 | } 404 | 405 | /** 406 | * @notice Returns the length, in seconds, of the auction. 407 | * @return The length, in seconds, of the auction. 408 | */ 409 | function getAuctionLength() external view returns (uint256) { 410 | return _auctionLength; 411 | } 412 | 413 | /** 414 | * @notice Returns the address of the auction's beneficiary. 415 | * @return The address of the auction's beneficiary. 416 | */ 417 | function getBeneficiary() external view returns (address) { 418 | return address(_beneficiary); 419 | } 420 | 421 | /* Public 422 | ========================================*/ 423 | /** 424 | * @notice Returns whether the auction has started. 425 | * @return True if it has started, false otherwise. 426 | */ 427 | function hasStarted() public view returns (bool) { 428 | return _started; 429 | } 430 | 431 | /** 432 | * @notice Returns whether the auction has finished. 433 | * @return True if it has finished, false otherwise. 434 | */ 435 | function hasFinished() public view returns (bool) { 436 | return _finished || block.timestamp > _auctionEndTime; 437 | } 438 | 439 | /** 440 | * @notice Returns whether the auction is in progress. 441 | * @return True if it is in progress, false otherwise. 442 | */ 443 | function inProgress() public view returns (bool) { 444 | return _started && block.timestamp < _auctionEndTime; 445 | } 446 | 447 | /* Private 448 | ========================================*/ 449 | /** 450 | * @notice Refunds the previous highest bidder. 451 | * 452 | * @dev Will set the current highest bidder to the zero (0) address. 453 | * Will set the highest bid to zero (0). 454 | * The calling code must implement `nonReentrant` as this call transfers 455 | * control to the `_highestBidder`. 456 | * May revert with `Auction__TransferFailed`. 457 | */ 458 | function _refundBid() private { 459 | if (_highestBidder == address(0)) return; 460 | 461 | address prevAddr = _highestBidder; 462 | uint256 prevBid = _highestBid; 463 | 464 | _highestBidder = address(0); 465 | _highestBid = 0; 466 | 467 | bool success = _withdraw(prevAddr, prevBid); 468 | 469 | if (!success) { 470 | revert Auction__TransferFailed(); 471 | } 472 | } 473 | 474 | /** 475 | * @notice Sends an `amount` of H1 to the `to` address. 476 | * @param to The address to send the H1 to. 477 | * @param amount The amount to send. 478 | * 479 | * @return True if transfer succeeded, false otherwise. 480 | * 481 | * @dev The calling code must implement `nonReentrant` as this call 482 | * transfers control to the `_highestBidder`. 483 | */ 484 | function _withdraw(address to, uint256 amount) private returns (bool) { 485 | (bool success, ) = payable(to).call{value: amount}(""); 486 | return success; 487 | } 488 | 489 | /** 490 | * @notice Validates that a given address is not the zero address. 491 | * @dev Reverts with `Auction__ZeroAddress` if the given address is the 492 | * zero address. 493 | */ 494 | function _validateAddrExn(address addr) private pure { 495 | if (addr == address(0)) { 496 | revert Auction__ZeroAddress(); 497 | } 498 | } 499 | 500 | /** 501 | * @notice Validates that a given `expiry` is greater than the current 502 | * `block.timestamp`. 503 | * 504 | * @param expiry The expiry to check. 505 | * 506 | * @return True if the expiry is greater than the current timestamp, false 507 | * otherwise. 508 | */ 509 | function _validateExpiry(uint256 expiry) private view returns (bool) { 510 | return expiry > block.timestamp; 511 | } 512 | 513 | /** 514 | * @notice Returns whether an account holds a Proof of Identity NFT. 515 | * @param account The account to check. 516 | * @return True if the account holds a Proof of Identity NFT, else false. 517 | */ 518 | function _hasID(address account) private view returns (bool) { 519 | return _proofOfIdentity.balanceOf(account) > 0; 520 | } 521 | 522 | /** 523 | * @notice Returns whether an account is suspended. 524 | * @param account The account to check. 525 | * @return True if the account is suspended, false otherwise. 526 | */ 527 | function _isSuspended(address account) private view returns (bool) { 528 | return _proofOfIdentity.isSuspended(account); 529 | } 530 | 531 | /** 532 | * @dev Determines whether a given user type meets the requirements for 533 | * the action. 534 | * Note: Does not validate the expiry. 535 | */ 536 | function _hasType(uint256 userType) private view returns (bool) { 537 | return (_auctionKind & userType) > 0; 538 | } 539 | 540 | /** 541 | * @notice Helper function to check whether a given `account`'s `userType` 542 | * is valid. 543 | * 544 | * @param account The account to check. 545 | * 546 | * @return True if the check is valid, false otherwise. 547 | * 548 | * @dev For a `userType` to be valid, it must: 549 | * - not be expired; and 550 | * - the `_auctionType` must either match the `userType`, or be set to 551 | * - `_ALL` (`3`). 552 | */ 553 | function _validateUserType(address account) private view returns (bool) { 554 | (uint256 user, uint256 exp, ) = _proofOfIdentity.getUserType(account); 555 | return _hasType(user) && _validateExpiry(exp); 556 | } 557 | 558 | /** 559 | * @notice Similar to `_checkUserType`, but rather than returning a `bool`, 560 | * will revert if the check fails. 561 | * 562 | * @param account The account to check. 563 | * 564 | * @dev For a `userType` to be valid, it must: 565 | * - not be expired; and 566 | * - the `_auctionType` must either match the `userType`, or be set to 567 | * - `_ALL` (`3`). 568 | * 569 | * May revert with `Auction__UserType`. 570 | * May revert with `Auction__AttributeExpired`. 571 | */ 572 | function _validateUserTypeExn(address account) private view { 573 | (uint256 user, uint256 exp, ) = _proofOfIdentity.getUserType(account); 574 | 575 | if (!_hasType(user)) { 576 | revert Auction__UserType(user, _auctionKind); 577 | } 578 | 579 | if (!_validateExpiry(exp)) { 580 | revert Auction__AttributeExpired("userType", exp); 581 | } 582 | } 583 | } 584 | -------------------------------------------------------------------------------- /contracts/examples/nft-auction/components/Errors.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | /** 5 | * @notice Error to throw when the zero address has been supplied and it 6 | * is not allowed. 7 | */ 8 | error Auction__ZeroAddress(); 9 | 10 | /** 11 | * @notice Error to throw when an invalid auction kind has been provided. 12 | * @param provided The auction kind that was provided. 13 | */ 14 | error Auction__InvalidAuctionKind(uint256 provided); 15 | 16 | /** 17 | * @notice Error to throw when an invalid auction length has been provided. 18 | * @param length The auction length that was provided. 19 | * @param min The minimum length that is required. 20 | */ 21 | error Auction__InvalidAuctionLength(uint256 length, uint256 min); 22 | 23 | /** 24 | * @notice Error to throw when a feature that requires the auction to be 25 | * started is accessed while it is inactive. 26 | */ 27 | error Auction__AuctionNotStarted(); 28 | 29 | /** 30 | * @notice Error to throw when a feature that requires the auction to be 31 | * inactive is accessed while it is active. 32 | */ 33 | error Auction__AuctionActive(); 34 | 35 | /** 36 | * @notice Error to throw when a feature that requires the auction to be 37 | * active is accessed after it has finished. 38 | */ 39 | error Auction__AuctionFinished(); 40 | 41 | /** 42 | * @notice Error to throw when an account does not have a Proof of Identity 43 | * NFT. 44 | */ 45 | error Auction__NoIdentityNFT(); 46 | 47 | /** 48 | * @notice Error to throw when an account is suspended. 49 | */ 50 | error Auction__Suspended(); 51 | 52 | /** 53 | * @notice Error to throw when an attribute has expired. 54 | * @param attribute The name of the required attribute. 55 | * @param expiry The expiry of the attribute. 56 | */ 57 | error Auction__AttributeExpired(string attribute, uint256 expiry); 58 | 59 | /** 60 | * @notice Error to throw when an the user type is invalid. 61 | * @param userType The `userType` of the account. 62 | * @param required The required `userType`. 63 | */ 64 | error Auction__UserType(uint256 userType, uint256 required); 65 | 66 | /** 67 | * @notice Error to throw when a bid is placed but it is not high enough. 68 | * @param bid The bid placed. 69 | * @param highestBid The current highest bid. 70 | */ 71 | error Auction__BidTooLow(uint256 bid, uint256 highestBid); 72 | 73 | /** 74 | * @notice Error to throw when a bidder tries to outbid (/raise) themselves. 75 | */ 76 | error Auction__AlreadyHighestBidder(); 77 | 78 | /** 79 | * @notice Error to throw when payable function receives no value. 80 | */ 81 | error Auction__ZeroValue(); 82 | 83 | /** 84 | * @notice Error to throw when a transfer has failed. 85 | */ 86 | error Auction__TransferFailed(); 87 | -------------------------------------------------------------------------------- /contracts/examples/nft-auction/components/NFTAuctionConfig.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | import {Auction__InvalidAuctionKind, Auction__InvalidAuctionLength, Auction__ZeroAddress} from "./Errors.sol"; 4 | 5 | struct Config { 6 | uint256 kind; 7 | uint256 length; 8 | uint256 startingBid; 9 | address nft; 10 | uint256 nftID; 11 | address beneficiary; 12 | } 13 | 14 | library ConfigUtils { 15 | /** 16 | * @dev The minimum time an auction has to last for. 17 | */ 18 | uint256 internal constant _MIN_AUCTION_LENGTH = 1 days; 19 | 20 | /** 21 | * @dev Auction Type: Retail. 22 | * This value means that only accounts marked as `retail` (`1`) on the 23 | * `ProofOfIdentity` contract will be allowed to participate in the auction. 24 | */ 25 | uint256 internal constant _RETAIL = 1; 26 | 27 | /** 28 | * @dev Auction Type: Institution. 29 | * This value means that only accounts marked as `institution` (`2`) on the 30 | * `ProofOfIdentity` contract will be allowed to participate in the auction. 31 | */ 32 | uint256 internal constant _INSTITUTION = 2; 33 | 34 | /** 35 | * @dev Auction Type: All. 36 | * Means that both `retial` (`1`) and `institution` (`2`) accounts as will 37 | * be allowed to participate in the auction. 38 | */ 39 | uint256 internal constant _ALL = 3; 40 | 41 | /** 42 | * @notice Validates a given auction configuration. 43 | * 44 | * @dev Will revert if any of the configuration items are not validated. 45 | * May revert with: `Auction__InvalidAuctionType` 46 | * May revert with: `Auction__InvalidAuctionLength` 47 | * May revert with: `Auction__ZeroAddress` 48 | */ 49 | function validateExn(Config memory cfg) internal pure { 50 | if (cfg.kind == 0 || cfg.kind > _ALL) { 51 | revert Auction__InvalidAuctionKind(cfg.kind); 52 | } 53 | 54 | if (cfg.length < _MIN_AUCTION_LENGTH) { 55 | revert Auction__InvalidAuctionLength( 56 | cfg.length, 57 | _MIN_AUCTION_LENGTH 58 | ); 59 | } 60 | 61 | if (cfg.nft == address(0)) { 62 | revert Auction__ZeroAddress(); 63 | } 64 | 65 | if (cfg.beneficiary == address(0)) { 66 | revert Auction__ZeroAddress(); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /contracts/examples/simple-storage/SimpleStorage.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import "../../vendor/h1-developed-application/H1DevelopedApplication.sol"; 6 | 7 | /** 8 | * @title SimpleStorage 9 | * @author Haven1 Development Team 10 | * 11 | * @notice A Simple Storage contract to demonstrate the features of the 12 | * `H1DevelopedApplication` contract. 13 | * 14 | * A developer fee is added to the increment and decrement functions. 15 | * 16 | * The developer account can reset the count. 17 | */ 18 | contract SimpleStorage is H1DevelopedApplication { 19 | /* TYPE DECLARATIONS 20 | ==================================================*/ 21 | enum Direction { 22 | DECR, 23 | INCR 24 | } 25 | 26 | /* STATE VARIABLES 27 | ==================================================*/ 28 | uint256 private _count; 29 | 30 | /* EVENTS 31 | ==================================================*/ 32 | /** 33 | * @notice Alerts that the count has been incremented. Emits the new count. 34 | * @param addr The address that incremented the count. 35 | * @param dir The count direction. 36 | * @param count The new count. 37 | * @param fee The fee paid. 38 | */ 39 | event Count( 40 | address indexed addr, 41 | Direction indexed dir, 42 | uint256 count, 43 | uint256 fee 44 | ); 45 | 46 | /* FUNCTIONS 47 | ==================================================*/ 48 | /* Constructor 49 | ========================================*/ 50 | /// @custom:oz-upgrades-unsafe-allow constructor 51 | constructor() { 52 | _disableInitializers(); 53 | } 54 | 55 | /* External 56 | ========================================*/ 57 | /** 58 | * @notice Initializes the `SimpleStorage` contract. 59 | * 60 | * @param feeContract The FeeContract address. 61 | * 62 | * @param association The Association address. 63 | * 64 | * @param developer The developer address. 65 | * 66 | * @param feeCollector The address that is sent the earned developer fees. 67 | * 68 | * @param fnSigs An array of function signatures for which specific fees 69 | * will be set. 70 | * 71 | * @param fnFees An array of fees that will be set for their `fnSelector` 72 | * counterparts. 73 | */ 74 | function initialize( 75 | address feeContract, 76 | address association, 77 | address developer, 78 | address feeCollector, 79 | string[] memory fnSigs, 80 | uint256[] memory fnFees 81 | ) external initializer { 82 | __H1DevelopedApplication_init( 83 | feeContract, 84 | association, 85 | developer, 86 | feeCollector, 87 | fnSigs, 88 | fnFees 89 | ); 90 | 91 | __AccessControl_init(); 92 | __UUPSUpgradeable_init(); 93 | } 94 | 95 | /** 96 | * @notice Increments the `_count` by one. 97 | * Only callable when the contract is not paused. 98 | * 99 | * @dev May revert with `Pausable: paused`. 100 | * May revert with `H1Developed__InsufficientFunds`. 101 | * May emit a `CountIncremented` event. 102 | * May emit a `FeePaid` event. 103 | */ 104 | function incrementCount() 105 | external 106 | payable 107 | whenNotPaused 108 | developerFee(false, true) 109 | { 110 | _count++; 111 | uint256 fee = getFnFeeAdj(msg.sig); 112 | emit Count(msg.sender, Direction.INCR, _count, fee); 113 | } 114 | 115 | /** 116 | * @notice Decrements the `_count` by one. 117 | * Only callable when the contract is not paused. 118 | * 119 | * @dev May revert with `Pausable: paused`. 120 | * May revert with `H1Developed__InsufficientFunds`. 121 | * May emit a `CountDecremented` event. 122 | * May emit a `FeePaid` event. 123 | */ 124 | function decrementCount() 125 | external 126 | payable 127 | whenNotPaused 128 | developerFee(false, true) 129 | { 130 | if (_count > 0) { 131 | _count--; 132 | } 133 | 134 | uint256 fee = getFnFeeAdj(msg.sig); 135 | emit Count(msg.sender, Direction.DECR, _count, fee); 136 | } 137 | 138 | /** 139 | * @notice Allows the developer to reset the `_count` to zero (0). 140 | * 141 | * @dev Only callable by an account with the role: `DEV_ADMIN_ROLE`. 142 | * May revert with: /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ 143 | */ 144 | function resetCount() external whenNotPaused onlyRole(DEV_ADMIN_ROLE) { 145 | _count = 0; 146 | emit Count(msg.sender, Direction.DECR, _count, 0); 147 | } 148 | 149 | /** 150 | * @notice Retruns the current `_count`. 151 | * @return The current `_count`. 152 | */ 153 | function count() external view returns (uint256) { 154 | return _count; 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /contracts/vendor/fee/interfaces/IFeeContract.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | /** 5 | * @title IFeeContract 6 | * @dev The interface for the FeeContract 7 | */ 8 | interface IFeeContract { 9 | /** 10 | * @notice Returns the timestamp for when the oracle can next update. 11 | * @return Timestamp for when the oracle can next update. 12 | */ 13 | function nextResetTime() external view returns (uint256); 14 | 15 | /** 16 | * @notice Returns the fee amount an address should receive. 17 | * @param index The index in the array of channels/weights. 18 | * @return The intended fee. 19 | */ 20 | function amountPaidToUponNextDistribution( 21 | uint8 index 22 | ) external view returns (uint256); 23 | 24 | /** 25 | * @notice Returns the `_fee`. 26 | * @return The current fee. 27 | */ 28 | function getFee() external view returns (uint256); 29 | 30 | /** 31 | * @notice Updates the fee in the fee contract to match the oracle value. 32 | */ 33 | function updateFee() external; 34 | 35 | /** 36 | * @notice Returns all channels. 37 | * @return The channels. 38 | */ 39 | function getChannels() external view returns (address[] memory); 40 | 41 | /** 42 | * @notice Return all the weights. 43 | * @return The weights. 44 | */ 45 | function getWeights() external view returns (uint256[] memory); 46 | 47 | /** 48 | * @notice Returns the fee oracle address. 49 | * @return The fee oracle address. 50 | */ 51 | function getOracleAddress() external view returns (address); 52 | 53 | /** 54 | * @notice Returns a channel's address and its weight. 55 | * @param index The index in the array of channels/weights. 56 | * @return The channel's address and its weight. 57 | */ 58 | function getChannelWeightByIndex( 59 | uint8 index 60 | ) external view returns (address, uint256); 61 | 62 | /** 63 | * @notice Returns the contract shares. 64 | * @return The contract shares. 65 | */ 66 | function getTotalContractShares() external view returns (uint256); 67 | 68 | /** 69 | * @notice Returns the block timestamp that the most recent fee distribution occurred. 70 | * @return The timestamp of the most recent fee distribution. 71 | */ 72 | function getLastDistribution() external view returns (uint256); 73 | 74 | /** 75 | * @notice Returns the min fee, in USD, that a developer may charge. 76 | * @return The min fee, in USD, that a developer may charge. 77 | */ 78 | function getMinDevFee() external view returns (uint256); 79 | 80 | /** 81 | * @notice Returns the max fee, in USD, that a developer may charge. 82 | * @return The max fee, in USD, that a developer may charge. 83 | */ 84 | function getMaxDevFee() external view returns (uint256); 85 | 86 | /** 87 | * @notice Returns the current fee, denominated in USD. 88 | * @return The fee, denominated in USD. 89 | */ 90 | function queryOracle() external view returns (uint256); 91 | 92 | /** 93 | * @notice Distributes fees to channels. 94 | */ 95 | function distributeFees() external; 96 | 97 | /** 98 | * @notice Forces a fee distribution. 99 | */ 100 | function forceDistributeFees() external; 101 | 102 | /** 103 | * @notice Sets the minimum fee for developer applications. __Must__ be to a 104 | * precision of 18 decimals. 105 | * 106 | * @param fee The minimum fee, in USD, that a developer may charge. 107 | */ 108 | function setMinFee(uint256 fee) external; 109 | 110 | /** 111 | * @notice Sets the maximum fee for developer applications. __Must__ be to a 112 | * precision of 18 decimals. 113 | * 114 | * @param fee The highest fee, in USD, that a developer may charge. 115 | */ 116 | function setMaxFee(uint256 fee) external; 117 | 118 | /** 119 | * @notice Updates the `_feeUSD` value. 120 | * @param feeUSD_ The new fee, in USD. __Must__ be to a precision of 18 121 | * decimals. 122 | * @dev Example: 123 | * - 1.75 USD: `1750000000000000000` 124 | * - 1.00 USD: `1000000000000000000` 125 | * - 0.50 USD: `500000000000000000` 126 | */ 127 | function setFeeUSD(uint256 feeUSD_) external; 128 | 129 | /** 130 | * @notice Updates the `_asscShare` value. 131 | * @param asscShare_ The new share of the developer fee that the Association 132 | * will receive. __Must__ be to a precision of 18 decimals. 133 | * @dev Example: 134 | * - 10%: `100000000000000000` 135 | * - 15%: `150000000000000000` 136 | */ 137 | function setAsscShare(uint256 asscShare_) external; 138 | 139 | /** 140 | * @notice Returns the current fee value in USD to a precision of 18 141 | * decimals. 142 | * @return The current fee value in USD to a precision of 18 decimals. 143 | */ 144 | function getFeeUSD() external view returns (uint256); 145 | 146 | /** 147 | * @notice Returns the `_h1USD` value. To be used in H1 Developed 148 | * Applications. 149 | * @return The current `_h1USD` value. 150 | */ 151 | function getDevH1USD() external view returns (uint256); 152 | 153 | /** 154 | * @notice Returns the current share the Association receives of the 155 | * developer fee to a precision of 18 decimals. 156 | * @return The current share the Association receives of the developer fee 157 | * to a precision of 18 decimals. 158 | */ 159 | function getAsscShare() external view returns (uint256); 160 | 161 | /** 162 | * @notice Sets the oracle address. 163 | * @param newOracle The new oracle address. 164 | */ 165 | function setOracle(address newOracle) external; 166 | 167 | /** 168 | * @notice Adds a new channel with a given weight. 169 | * @param _newChannelAddress The new channel to add. 170 | * @param _weight The weight for the new channel. 171 | */ 172 | function addChannel(address _newChannelAddress, uint256 _weight) external; 173 | 174 | /** 175 | * @notice Adjusts a channel and its weight. 176 | * @param _oldChannelAddress The address of the channel to update. 177 | * @param _newChannelAddress The address of the channel that replaces the old one. 178 | * @param _newWeight The amount of total shares the new address will receive. 179 | */ 180 | function adjustChannel( 181 | address _oldChannelAddress, 182 | address _newChannelAddress, 183 | uint256 _newWeight 184 | ) external; 185 | 186 | /** 187 | * @notice Removes a channel and it's weight. 188 | * @param _channel The address being removed. 189 | */ 190 | function removeChannel(address _channel) external; 191 | 192 | /** 193 | * @notice set contracts that accesses grace fee. 194 | */ 195 | function setGraceContract(bool enterGrace) external; 196 | 197 | /** 198 | * @notice enables admins to update the grace period. 199 | * @param gracePeriod the new grace period in seconds. 200 | */ 201 | function setGracePeriod(uint256 gracePeriod) external; 202 | } 203 | -------------------------------------------------------------------------------- /contracts/vendor/fee/interfaces/IFeeOracle.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | /// @title IFeeOracle 5 | /// @dev The interface for the FeeOracle contract 6 | interface IFeeOracle { 7 | /// @notice Returns the value of H1 denominated in USD. 8 | /// @return Value of H1, denominated in USD. 9 | function consult() external view returns (uint256); 10 | 11 | /// @notice Refreshes the oracle 12 | /// @return Whether the refresh was successful 13 | function refreshOracle() external returns (bool); 14 | } 15 | -------------------------------------------------------------------------------- /contracts/vendor/h1-developed-application/components/Errors.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /** 6 | * @notice This error is thrown when a fee transfer failed. 7 | * @param to The recipient address. 8 | * @param amount The amount of the transaction. 9 | */ 10 | error H1Developed__FeeTransferFailed(address to, uint256 amount); 11 | 12 | /** 13 | * @notice This error is thrown when an address validation has failed. 14 | * @param provided The address provided. 15 | * @param source The origination of the error. 16 | */ 17 | error H1Developed__InvalidAddress(address provided, string source); 18 | 19 | /** 20 | * @notice This error is thrown when there are insufficient funds send to 21 | * pay the fee. 22 | * 23 | * @param fundsInContract The current balance of the contract 24 | * @param currentFee The current fee amount 25 | */ 26 | error H1Developed__InsufficientFunds( 27 | uint256 fundsInContract, 28 | uint256 currentFee 29 | ); 30 | 31 | /** 32 | * @dev Error to throw when the length of n arrays are required to be equal 33 | * and are not. 34 | * @param a The length of the first array. 35 | * @param b The length of the second array. 36 | */ 37 | error H1Developed__ArrayLengthMismatch(uint256 a, uint256 b); 38 | 39 | /** 40 | * @dev Error to throw when the length of an array must be greater than zero 41 | * and it is not. 42 | */ 43 | error H1Developed__ArrayLengthZero(); 44 | 45 | /** 46 | * @dev Error to throw when a user tries to access an invalid index of an array. 47 | * param idx The index that was accessed. 48 | * param maxIdx The maximum index that can be accessed on the array. 49 | */ 50 | error H1Developed__IndexOutOfBounds(uint256 idx, uint256 maxIdx); 51 | 52 | /** 53 | * @dev Error to throw when an invalid function signature has been provided. 54 | * @param sig The provided signature. 55 | */ 56 | error H1Developed__InvalidFnSignature(string sig); 57 | 58 | /** 59 | * @dev Error to throw when an attempt add a fee is made and it falls 60 | * outside the constraints set by the `FeeContract`. 61 | * @param fee The invalid fee amount. 62 | */ 63 | error H1Developed__InvalidFeeAmount(uint256 fee); 64 | -------------------------------------------------------------------------------- /contracts/vendor/h1-developed-application/components/H1DevelopedAccessControl.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; 6 | import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; 7 | 8 | import {Validate} from "./H1DevelopedUtils.sol"; 9 | 10 | /** 11 | * @title H1DevelopedAccessControl 12 | * @author Haven1 Development Team 13 | * 14 | * @notice Contains the various roles to be used throughout the 15 | * `H1DevelopedApplication` contracts. 16 | * 17 | * @dev Note that inheriting contracts may also wish to add extra roles. As 18 | * such, the last contract in the inheritance chain should call 19 | * `__AccessControl_init()`. 20 | * 21 | * This contract contains only constants which are inlined on compilation. 22 | * Despite this, a gap has still been provided to cater for future upgrades. 23 | */ 24 | contract H1DevelopedAccessControl is Initializable, AccessControlUpgradeable { 25 | /* STATE VARIABLES 26 | ==================================================*/ 27 | 28 | /** 29 | * @dev The Pauser role. Has the ability to pause the contract. 30 | */ 31 | bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE"); 32 | 33 | /** 34 | * @dev The Unpauser role. Has the ability to unpause the contract. 35 | */ 36 | bytes32 public constant UNPAUSER_ROLE = keccak256("UNPAUSER_ROLE"); 37 | 38 | /** 39 | * @dev The Dev Admin role. For use in the inheriting contract. 40 | */ 41 | bytes32 public constant DEV_ADMIN_ROLE = keccak256("DEV_ADMIN_ROLE"); 42 | 43 | /* FUNCTIONS 44 | ==================================================*/ 45 | 46 | /** 47 | * @notice Initializes the `H1DevelopedAccessControl` contract. 48 | * 49 | * @param association The address of the Haven1 Association. Will be 50 | * assigned roles: `DEFAULT_ADMIN_ROLE`, `PAUSER_ROLE`, and `UNPAUSER_ROLE`. 51 | * 52 | * @param developer The address of the contract's developer. Will be 53 | * assigned roles: `PAUSER_ROLE` and `DEV_ADMIN_ROLE`. 54 | * 55 | * @dev May revert with `H1Developed__InvalidAddress`. 56 | */ 57 | function __H1DevelopedAccessControl_init( 58 | address association, 59 | address developer 60 | ) internal onlyInitializing { 61 | __H1DevelopedAccessControl_init_unchained(association, developer); 62 | } 63 | 64 | /** 65 | * @dev see {H1DevelopedRoles-__H1DevelopedAccessControl__init} 66 | */ 67 | function __H1DevelopedAccessControl_init_unchained( 68 | address association, 69 | address developer 70 | ) internal onlyInitializing { 71 | Validate.addrExn(association, "Init: association"); 72 | Validate.addrExn(developer, "Init: developer"); 73 | 74 | _grantRole(DEFAULT_ADMIN_ROLE, association); 75 | 76 | _grantRole(PAUSER_ROLE, association); 77 | _grantRole(PAUSER_ROLE, developer); 78 | 79 | _grantRole(UNPAUSER_ROLE, association); 80 | 81 | _grantRole(DEV_ADMIN_ROLE, developer); 82 | } 83 | 84 | /* GAP 85 | ==================================================*/ 86 | 87 | /** 88 | * @dev This empty reserved space is put in place to allow future versions 89 | * to add new variables without shifting down storage in the inheritance 90 | * chain. 91 | * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps 92 | * 93 | * As new variables are added, be sure to reduce the gap as required. 94 | * For e.g., if the starting `__gap` is `50` and a new variable is added 95 | * (256 bits in size or part thereof), the gap must now be reduced to `49`. 96 | */ 97 | uint256[50] private __gap; 98 | } 99 | -------------------------------------------------------------------------------- /contracts/vendor/h1-developed-application/components/H1DevelopedPausable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; 6 | import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol"; 7 | 8 | import "./H1DevelopedAccessControl.sol"; 9 | 10 | /** 11 | * @title H1DevelopedPausable 12 | * @author Haven1 Development Team 13 | * 14 | * @notice Wraps Open Zeppelin's `PausableUpgradeable`. 15 | * Contains the protected public `pause` and `unpause` functions. 16 | * 17 | * @dev This contract does not contain any state variables. Even so, a very 18 | * small gap has been provided to accommodate the addition of state variables 19 | * should the need arise. 20 | */ 21 | contract H1DevelopedPausable is 22 | Initializable, 23 | PausableUpgradeable, 24 | H1DevelopedAccessControl 25 | { 26 | /* FUNCTIONS 27 | ==================================================*/ 28 | /* Init 29 | ========================================*/ 30 | 31 | /** 32 | * @notice Initializes the `H1DevelopedPausable` contract. 33 | */ 34 | function __H1DevelopedPausable_init() internal onlyInitializing { 35 | __Pausable_init(); 36 | __H1DevelopedPausable_init_unchained(); 37 | } 38 | 39 | /** 40 | * @dev see {H1DevelopedPausable-__H1DevelopedPausable_init} 41 | */ 42 | function __H1DevelopedPausable_init_unchained() internal onlyInitializing {} 43 | 44 | /* Public 45 | ========================================*/ 46 | 47 | /** 48 | * @notice Pauses the contract. 49 | * @dev Only callable by an account with the role: `PAUSER_ROLE`. 50 | * @dev May revert with: /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ 51 | * May emit a `Paused` event. 52 | */ 53 | function pause() public onlyRole(PAUSER_ROLE) { 54 | _pause(); 55 | } 56 | 57 | /** 58 | * @notice Unpauses the contract. 59 | * @dev Only callable by an account with the role: `UNPAUSER_ROLE`. 60 | * @dev May revert with: /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ 61 | * May emit an `Unpaused` event. 62 | */ 63 | function unpause() public onlyRole(UNPAUSER_ROLE) { 64 | _unpause(); 65 | } 66 | 67 | /* GAP 68 | ==================================================*/ 69 | 70 | /** 71 | * @dev This empty reserved space is put in place to allow future versions 72 | * to add new variables without shifting down storage in the inheritance 73 | * chain. 74 | * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps 75 | * 76 | * As new variables are added, be sure to reduce the gap as required. 77 | * For e.g., if the starting `__gap` is `25` and a new variable is added 78 | * (256 bits in size or part thereof), the gap must now be reduced to `24`. 79 | */ 80 | uint256[25] private __gap; 81 | } 82 | -------------------------------------------------------------------------------- /contracts/vendor/h1-developed-application/components/H1DevelopedUtils.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import {H1Developed__InvalidFeeAmount, H1Developed__InvalidAddress} from "./Errors.sol"; 6 | 7 | struct FeeProposal { 8 | /** 9 | * @dev The fee, in USD, to 18 decimals of precision. 10 | */ 11 | uint256 fee; 12 | /** 13 | * @dev The signature of the function for which the fee should be set. 14 | * E.g., `abi.encode("transfer(address,uint256)");`` 15 | */ 16 | bytes fnSig; 17 | } 18 | 19 | struct FeeProposalFormatted { 20 | /** 21 | * @dev The fee, in USD, to 18 decimals of precision. 22 | */ 23 | uint256 fee; 24 | /** 25 | * @dev The signature of the function for which the fee should be set. 26 | * E.g., `transfer(address,uint256)`. 27 | */ 28 | string fnSig; 29 | } 30 | 31 | /** 32 | * @title Validate 33 | * @author Haven1 Development Team 34 | * @dev Library that consists of validation functions. Functions suffixed 35 | * with `exn` with throw expections if the given condition(s) are not met. 36 | */ 37 | library Validate { 38 | /** 39 | * @notice Helper function to test the validity of a given fee against a 40 | * given min and max constraint. 41 | * 42 | * @param fee The fee to validate. 43 | * @param min The minimum fee. 44 | * @param max The maximum fee. 45 | * 46 | * @dev Will revert with `H1Developed__InvalidFeeAmount` if the fee is 47 | * less than the min or greater than the max. 48 | */ 49 | function feeExn(uint256 fee, uint256 min, uint256 max) internal pure { 50 | if ((fee > 0 && fee < min) || fee > max) { 51 | revert H1Developed__InvalidFeeAmount(fee); 52 | } 53 | } 54 | 55 | /** 56 | * @notice Helper function to test the validity of a given address. 57 | * 58 | * @param addr The address to check 59 | * @param source The source of the address check. 60 | * 61 | * @dev Will revert with `H1Developed__InvalidAddress` if the address is 62 | * invalid. 63 | */ 64 | function addrExn(address addr, string memory source) internal pure { 65 | if (addr == address(0)) { 66 | revert H1Developed__InvalidAddress(addr, source); 67 | } 68 | } 69 | } 70 | 71 | /** 72 | * @title FnSig 73 | * @author Haven1 Development Team 74 | * @dev Library that provides helpers for function signatures stored as bytes. 75 | */ 76 | library FnSig { 77 | /** 78 | * @notice Decodes a given byte array `b` to a string. If the byte array 79 | * has no length, an empty string `""` is returned. 80 | * @param b The byte array to decode. 81 | */ 82 | function toString(bytes memory b) internal pure returns (string memory) { 83 | if (b.length == 0) return ""; 84 | return string(b); 85 | } 86 | 87 | /** 88 | * @notice Converts a given byte array to a function signature. 89 | * If the byte array has no length, an empty bytes4 array is returned. 90 | * @param b The byte array to decode. 91 | * @dev The provided byte array is expected to be a function signature. 92 | * E.g., `abi.encode("transfer(address,uint256)");`` 93 | */ 94 | function toFnSelector(bytes memory b) internal pure returns (bytes4) { 95 | if (b.length == 0) return bytes4(""); 96 | return bytes4(keccak256(b)); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /contracts/vendor/h1-developed-application/interfaces/IH1DevelopedApplication.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | import {FeeProposalFormatted} from "../components/H1DevelopedUtils.sol"; 4 | 5 | /** 6 | * @title IH1DevelopedApplication 7 | * @dev The interface for the H1DevelopedApplication 8 | */ 9 | interface IH1DevelopedApplication { 10 | /** 11 | * @notice Returns the adjusted fee in H1 tokens, if any, associated with 12 | * the given function selector. 13 | * If the fee is less than the minimum possible fee, the minimum fee will be 14 | * returned. 15 | * If the fee is greater than the maximum possible fee, the maximum fee will 16 | * be returned. 17 | * 18 | * @param fnSelector The function selector for which the fee should be 19 | * retrieved. 20 | * 21 | * @return The fee, if any, associated with the given function selector. 22 | */ 23 | function getFnFeeAdj(bytes4 fnSelector) external view returns (uint256); 24 | 25 | /** 26 | * @notice Returns the function selector for a given function signature. 27 | * @param fnSignature The signature of the function. 28 | * @return The function selector for the given function signature. 29 | */ 30 | function getFnSelector( 31 | string memory fnSignature 32 | ) external pure returns (bytes4); 33 | 34 | /** 35 | * @notice Proposes a new fee for a given function. To propose multiple fees 36 | * at once, see {H1DevelopedApplication-proposeFees}. 37 | * 38 | * @param fnSig The signature of the function for which a fee is proposed. 39 | * @param fee The proposed fee. 40 | */ 41 | function proposeFee(string memory fnSig, uint256 fee) external; 42 | 43 | /** 44 | * @notice Proposes fees for a list of functions. 45 | * 46 | * @param fnSigs The list of function signatures for which fees are 47 | * proposed. 48 | * 49 | * @param fnFees The list of proposed fees. 50 | */ 51 | function proposeFees( 52 | string[] memory fnSigs, 53 | uint256[] memory fnFees 54 | ) external; 55 | 56 | /** 57 | * @notice Approves the proposed fee at the given index. 58 | * @param index The index of the fee to approve from the `_feeProposals` list. 59 | */ 60 | function approveFee(uint256 index) external; 61 | 62 | /** 63 | * @notice Approves all currently proposed fees. 64 | */ 65 | function approveAllFees() external; 66 | 67 | /** 68 | * @notice Rejects the proposed fee at the given index. 69 | * @param index The index of the fee to reject from the `_feeProposals` list. 70 | */ 71 | function rejectFee(uint256 index) external; 72 | 73 | /** 74 | * @notice Rejects all currently proposed fees. 75 | */ 76 | function rejectAllFees() external; 77 | 78 | /** 79 | * @notice Allows for the approval / rejection of fees in the 80 | * `_feeProposals` list. 81 | * 82 | * @param approvals A list of booleans that indicate whether a given fee at 83 | * the corresponding index in the `_feeProposals` list should be approved. 84 | */ 85 | function reviewFees(bool[] memory approvals) external; 86 | 87 | /** 88 | * @notice Allows the admin account to remove a fee. 89 | * @param fnSelector The function selector for which the fee is removed. 90 | */ 91 | function removeFeeAdmin(bytes4 fnSelector) external; 92 | 93 | /** 94 | * @notice Updates the `_feeContract` address. 95 | * @param feeContract_ The new FeeContract address. 96 | */ 97 | function setFeeContract(address feeContract_) external; 98 | 99 | /** 100 | * @notice Updates the `_association` address. 101 | * @param association_ The new Association address. 102 | */ 103 | function setAssociation(address association_) external; 104 | 105 | /** 106 | * @notice Updates the `_developer` address. 107 | * @param developer_ The new developer address. 108 | */ 109 | function setDeveloper(address developer_) external; 110 | 111 | /** 112 | * @notice Updates the `_devFeeCollector` address. 113 | * @param devFeeCollector_ The new fee collector address. 114 | */ 115 | function setDevFeeCollector(address devFeeCollector_) external; 116 | 117 | /** 118 | * @notice Updates the `_devFeeCollector` address. 119 | * @param devFeeCollector_ The new fee collector address. 120 | */ 121 | function setDevFeeCollectorAdmin(address devFeeCollector_) external; 122 | 123 | /** 124 | * @notice Returns a list of the currently proposed fees and their function 125 | * signature. 126 | * 127 | * @return A list of the currently proposed fees and their function 128 | * signature. 129 | */ 130 | function proposedFees() 131 | external 132 | view 133 | returns (FeeProposalFormatted[] memory); 134 | 135 | /** 136 | * @notice Returns the address of the `FeeContract`. 137 | * @return The address of the `FeeContract`. 138 | */ 139 | function feeContract() external view returns (address); 140 | 141 | /** 142 | * @notice Returns the address of the `Association`. 143 | * @return The address of the `Association`. 144 | */ 145 | function association() external view returns (address); 146 | 147 | /** 148 | * @notice Returns the address of the `developer`. 149 | * @return The address of the `developer`. 150 | */ 151 | function developer() external view returns (address); 152 | 153 | /** 154 | * @notice Returns the address of the `_devFeeCollector`. 155 | * @return The address of the `_devFeeCollector`. 156 | */ 157 | function devFeeCollector() external view returns (address); 158 | 159 | /** 160 | * @notice Returns the unadjusted USD fee, if any, associated with the given 161 | * function selector. 162 | * 163 | * @param fnSelector The function selector for which the fee should be 164 | * retrieved. 165 | * 166 | * @return The fee, if any, associated with the given function selector. 167 | */ 168 | function getFnFeeUSD(bytes4 fnSelector) external view returns (uint256); 169 | } 170 | -------------------------------------------------------------------------------- /contracts/vendor/h1-developed-application/interfaces/IH1DevelopedPausable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | /** 5 | * @title IH1DevelopedPausable 6 | * @dev The interface for the H1DevelopedPausable 7 | */ 8 | interface IH1DevelopedPausable { 9 | /** 10 | * @notice Pauses the contract. 11 | */ 12 | function pause() external; 13 | 14 | /** 15 | * @notice Unpauses the contract. 16 | */ 17 | function unpause() external; 18 | } 19 | -------------------------------------------------------------------------------- /contracts/vendor/mocks/FixedFeeOracle.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | import "@openzeppelin/contracts/access/AccessControl.sol"; 5 | 6 | contract FixedFeeOracle is AccessControl { 7 | /* STATE VARIABLES 8 | ==================================================*/ 9 | bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE"); 10 | 11 | uint256 private _val; 12 | 13 | /* FUNCTIONS 14 | ==================================================*/ 15 | /* Constructor 16 | ========================================*/ 17 | constructor(address association_, address networkOperator_, uint256 val_) { 18 | _grantRole(DEFAULT_ADMIN_ROLE, association_); 19 | _grantRole(OPERATOR_ROLE, association_); 20 | _grantRole(OPERATOR_ROLE, networkOperator_); 21 | _val = val_; 22 | } 23 | 24 | /* External 25 | ========================================*/ 26 | function refreshOracle() external pure returns (bool) { 27 | return true; 28 | } 29 | 30 | function updateVal(uint256 v) external onlyRole(OPERATOR_ROLE) { 31 | _val = v; 32 | } 33 | 34 | function consult() external view returns (uint256) { 35 | return _val; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /contracts/vendor/mocks/MockAccountManager.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | import "../proof-of-identity/interfaces/vendor/IAccountManager.sol"; 4 | 5 | /** 6 | * @dev account status is denoted by a fixed integer value. The values are 7 | * as below: 8 | * 0 - Not in list 9 | * 1 - Account pending approval 10 | * 2 - Active 11 | * 3 - Inactive 12 | * 4 - Suspended 13 | * 5 - Blacklisted 14 | * 6 - Revoked 15 | * 7 - Recovery Initiated for blacklisted accounts and pending approval 16 | * from network admins 17 | */ 18 | contract MockAccountManager is IAccountManager { 19 | mapping(address => uint256) private _accountAccessList; 20 | 21 | function getAccountStatus(address account) public view returns (uint256) { 22 | return _accountAccessList[account]; 23 | } 24 | 25 | /** 26 | * @notice updates the account status to the passed status value 27 | * @param _account - account id 28 | * @param _action - new status of the account 29 | * @dev the following actions are allowed 30 | * 1 - Suspend the account 31 | * 2 - Reactivate a suspended account 32 | * 3 - Blacklist an account 33 | * 4 - Initiate recovery for black listed account 34 | * 5 - Complete recovery of black listed account and update status to active 35 | */ 36 | function updateAccountStatus( 37 | string calldata, 38 | address _account, 39 | uint _action 40 | ) external { 41 | // this is all the impl we need for testing 42 | if (_action == 1) { 43 | _accountAccessList[_account] = 4; 44 | } 45 | 46 | if (_action == 2) { 47 | _accountAccessList[_account] = 2; 48 | } 49 | } 50 | 51 | function assignAccountRole( 52 | address _account, 53 | string calldata, 54 | string calldata, 55 | bool 56 | ) external { 57 | _accountAccessList[_account] = 2; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /contracts/vendor/mocks/MockNFT.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; 5 | 6 | /** 7 | * @dev Simple mock NFT with no restrictions on minting. Suitable for testing. 8 | */ 9 | contract MockNFT is ERC721 { 10 | error MockNFT__ZeroAddress(); 11 | error MockNFT__MaxSupply(); 12 | 13 | uint256 _tokenCounter; 14 | uint256 _maxSupply; 15 | 16 | constructor(uint256 maxSupply_) ERC721("MockNFT", "MKDNFT") { 17 | _maxSupply = maxSupply_; 18 | } 19 | 20 | function mint(address account) external { 21 | if (account == address(0)) revert MockNFT__ZeroAddress(); 22 | if (_tokenCounter == _maxSupply) revert MockNFT__MaxSupply(); 23 | 24 | // id will start at 1 25 | _tokenCounter++; 26 | 27 | uint256 id = _tokenCounter; 28 | 29 | _mint(account, id); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /contracts/vendor/mocks/MockPermissionsInterface.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | import "../proof-of-identity/interfaces/vendor/IAccountManager.sol"; 5 | import "../proof-of-identity/interfaces/vendor/IPermissionsInterface.sol"; 6 | 7 | contract MockPermissionsInterface is IPermissionsInterface { 8 | IAccountManager private _accountManager; 9 | 10 | constructor(address accManager) { 11 | _accountManager = IAccountManager(accManager); 12 | } 13 | 14 | function assignAccountRole( 15 | address _account, 16 | string calldata _orgId, 17 | string calldata _roleId 18 | ) external { 19 | _accountManager.assignAccountRole(_account, _orgId, _roleId, false); 20 | } 21 | 22 | function updateAccountStatus( 23 | string calldata _orgId, 24 | address _account, 25 | uint256 _action 26 | ) external { 27 | _accountManager.updateAccountStatus(_orgId, _account, _action); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /contracts/vendor/proof-of-identity/interfaces/IProofOfIdentity.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | import "../libraries/AttributeUtils.sol"; 4 | 5 | /** 6 | * @title IProofOfIdentity 7 | * @dev The interface for the ProofOfIdentity contract. 8 | */ 9 | interface IProofOfIdentity { 10 | /** 11 | * @notice Issues a Proof of Identity NFT to the `account`. 12 | * @param account The address of the account to receive the NFT. 13 | * @param primaryID Whether the account has verified a primary ID. 14 | * @param countryCode The ISO 3166-1 alpha-2 country code of the account. 15 | * @param proofOfLiveliness Whether the account has completed a proof of liveliness check. 16 | * @param userType The account type of the user: 1 = retail. 2 = institution. 17 | */ 18 | function issueIdentity( 19 | address account, 20 | bool primaryID, 21 | string calldata countryCode, 22 | bool proofOfLiveliness, 23 | uint256 userType, 24 | uint256[4] memory expiries, 25 | string calldata uri 26 | ) external; 27 | 28 | /** 29 | * @notice Sets an attribute, the value for which is of type `string`. 30 | * @param account The address for which the attribute should be set. 31 | * @param id The ID of the attribute to set. 32 | * @param exp The timestamp of expiry of the attribute. 33 | * @param data The attribute data to set as a `string`. 34 | */ 35 | function setStringAttribute( 36 | address account, 37 | uint256 id, 38 | uint256 exp, 39 | string calldata data 40 | ) external; 41 | 42 | /** 43 | * @notice Sets an attribute, the value for which is of type `uint256`. 44 | * @param account The address for which the attribute should be set. 45 | * @param id The ID of the attribute to set. 46 | * @param exp The timestamp of expiry of the attribute. 47 | * @param data The attribute data to set as `uint256`. 48 | */ 49 | function setU256Attribute( 50 | address account, 51 | uint256 id, 52 | uint256 exp, 53 | uint256 data 54 | ) external; 55 | 56 | /** 57 | * @notice Sets an attribute, the value for which is of type `bool`. 58 | * @param account The address for which the attribute should be set. 59 | * @param id The ID of the attribute to set. 60 | * @param exp The timestamp of expiry of the attribute. 61 | * @param data The attribute data to set as `bool`. 62 | */ 63 | function setBoolAttribute( 64 | address account, 65 | uint256 id, 66 | uint256 exp, 67 | bool data 68 | ) external; 69 | 70 | /** 71 | * @notice Sets an attribute, the value for which is of type `bytes`. 72 | * @param account The address for which the attribute should be set. 73 | * @param id The ID of the attribute to set. 74 | * @param exp The timestamp of expiry of the attribute. 75 | * @param data The attribute data to set as `bytes`. 76 | */ 77 | function setBytesAttribute( 78 | address account, 79 | uint256 id, 80 | uint256 exp, 81 | bytes calldata data 82 | ) external; 83 | 84 | /** 85 | * @notice Sets the attribute count. 86 | * @param count The new count. 87 | */ 88 | function setAttributeCount(uint256 count) external; 89 | 90 | /** 91 | * @notice Adds an attribute to the contract. 92 | * @param name The attribute's name. 93 | * @param attrType The type of the attribute. 94 | */ 95 | function addAttribute( 96 | string calldata name, 97 | SupportedAttributeType attrType 98 | ) external; 99 | 100 | /** 101 | * @notice Updates the URI of a token. 102 | * @param account the target account of the tokenUri to update. 103 | * @param tokenUri the URI data to update for the token Id. 104 | */ 105 | function setTokenURI( 106 | address account, 107 | uint256 tokenId, 108 | string calldata tokenUri 109 | ) external; 110 | 111 | /** 112 | * @notice Suspends an account. 113 | * @param account The account to suspend. 114 | * @param reason The reason for the suspension. 115 | */ 116 | function suspendAccount(address account, string calldata reason) external; 117 | 118 | /** 119 | * @notice Unsuspends an account. 120 | * @param account The account to unsuspend. 121 | */ 122 | function unsuspendAccount(address account) external; 123 | 124 | /** 125 | * @notice Returns a tuple containing whether or not a user has validated 126 | * their primary ID, the expiry of the attribute and the last time it was 127 | * updated. 128 | * 129 | * @param account The address of the account for which the attribute is fetched. 130 | * 131 | * @return A tuple containing whether the account's primary ID has been 132 | * validated, the expiry of the attribute and the last time it was updated. 133 | * Returned in the following form: `(bool, uint256, uint256)` 134 | */ 135 | function getPrimaryID( 136 | address account 137 | ) external view returns (bool, uint256, uint256); 138 | 139 | /** 140 | * @notice Returns a tuple containing a user's country code (lowercase), the 141 | * expiry of the attribute and the last time it was updated. 142 | * 143 | * @param account The address of the account for which the attribute is fetched. 144 | * 145 | * @return A tuple containing a user's country code (lowercase), the expiry 146 | * of the attribute and the last time it was updated. Returned in the 147 | * following form: `(string memory, uint256, uint256)` 148 | */ 149 | function getCountryCode( 150 | address account 151 | ) external view returns (string memory, uint256, uint256); 152 | 153 | /** 154 | * @notice Returns a tuple containing whether a user's proof of liveliness 155 | * check has been completed, the expiry of the attribute and the last time 156 | * it was updated. 157 | * 158 | * @param account The address of the account for which the attribute is fetched. 159 | * 160 | * @return A tuple containing whether a user's proof of liveliness check 161 | * has been completed, the expiry of the attribute and the last time it was 162 | * updated. Returned in the following form: `(bool, uint256, uint256)` 163 | */ 164 | function getProofOfLiveliness( 165 | address account 166 | ) external view returns (bool, uint256, uint256); 167 | 168 | /** 169 | * @notice Returns a tuple containing a user's account type, the expiry of 170 | * the attribute and the last time it was updated. 171 | * 1 = Retail 172 | * 2 = Institution 173 | * 174 | * @param account The address of the account for which the attribute is fetched. 175 | * 176 | * @return A tuple containing a user's account type, the expiry of the 177 | * attribute and the last time it was updated. 178 | * Returned in the following form: `(uint256, uint256, uint256)` 179 | */ 180 | function getUserType( 181 | address account 182 | ) external view returns (uint256, uint256, uint256); 183 | 184 | /** 185 | * @notice Returns a tuple containing a user's competency rating, the expiry 186 | * of the attribute and the last time it was updated. 187 | * 188 | * @param account The address of the account for which the attribute is fetched. 189 | * 190 | * @return A tuple containing a user's competency rating, the expiry of the 191 | * attribute and the last time it was updated. 192 | * Returned in the following form: `(uint256, uint256, uint256)` 193 | */ 194 | function getCompetencyRating( 195 | address account 196 | ) external view returns (uint256, uint256, uint256); 197 | 198 | /** 199 | * @notice Returns a tuple containing the string attribute, the expiry of 200 | * the attribute and the last time it was updated. Note that if an invalid ID 201 | * is passed in, the call with revert. 202 | * If an address for which the attribute has not yet been set is passed in, 203 | * the default `("", 0, 0)` case will be returned. 204 | * 205 | * @param id The attribute ID to fetch. 206 | * @param account The address of the account for which the attribute is fetched. 207 | * 208 | * @return A tuple containing the string attribute, the expiry of the 209 | * attribute and the last time it was updated. Returned in the following 210 | * form: `(string memory, uint256, uint256)` 211 | */ 212 | function getStringAttribute( 213 | uint256 id, 214 | address account 215 | ) external view returns (string memory, uint256, uint256); 216 | 217 | /** 218 | * @notice Returns a tuple containing the uint256 attribute, the expiry of 219 | * the attribute and the last time it was updated. Note that if an invalid ID 220 | * is passed in, the call with revert. 221 | * If an address for which the attribute has not yet been set is passed in, 222 | * the default `(0, 0, 0)` case will be returned. 223 | * 224 | * @param id The attribute ID to fetch. 225 | * @param account The address of the account for which the attribute is fetched. 226 | * 227 | * @return A tuple containing the uint256 attribute, the expiry of the 228 | * attribute and the last time it was updated. Returned in the following 229 | * form: `(uint256, uint256, uint256)` 230 | */ 231 | function getU256Attribute( 232 | uint256 id, 233 | address account 234 | ) external view returns (uint256, uint256, uint256); 235 | 236 | /** 237 | * @notice Returns a tuple containing the bool attribute, the expiry of 238 | * the attribute and the last time it was updated. Note that if an invalid ID 239 | * is passed in, the call with revert. 240 | * If an address for which the attribute has not yet been set is passed in, 241 | * the default `(false, 0, 0)` case will be returned. 242 | * 243 | * @param id The attribute ID to fetch. 244 | * @param account The address of the account for which the attribute is fetched. 245 | * 246 | * @return A tuple containing the uint256 attribute, the expiry of the 247 | * attribute and the last time it was updated. Returned in the following 248 | * form: `(bool, uint256, uint256)` 249 | */ 250 | function getBoolAttribute( 251 | uint256 id, 252 | address account 253 | ) external view returns (bool, uint256, uint256); 254 | 255 | /** 256 | * @notice Returns a tuple containing the bytes attribute, the expiry of 257 | * the attribute and the last time it was updated. Note that if an invalid ID 258 | * is passed in, the call with revert. 259 | * If an address for which the attribute has not yet been set is passed in, 260 | * the default `("0x", 0, 0)` case will be returned. 261 | * 262 | * @param id The attribute ID to fetch. 263 | * @param account The address of the account for which the attribute is fetched. 264 | * 265 | * @return A tuple containing the uint256 attribute, the expiry of the 266 | * attribute and the last time it was updated. Returned in the following 267 | * form: `(bytes, uint256, uint256)` 268 | */ 269 | function getBytesAttribute( 270 | uint256 id, 271 | address account 272 | ) external view returns (bytes memory, uint256, uint256); 273 | 274 | /** 275 | * @notice Helper function that returns an attribute's name. Note that 276 | * it will return an empty string (`""`) if the attribute ID provided is 277 | * invalid. 278 | * @param id The ID of the attribute for which the name is fetched. 279 | * @return The name of the attribute. 280 | */ 281 | function getAttributeName(uint256 id) external view returns (string memory); 282 | 283 | /** 284 | * @notice Returns if a given account is suspended. 285 | * @param account The account the check. 286 | * @return True if suspended, false otherwise. 287 | */ 288 | function isSuspended(address account) external view returns (bool); 289 | 290 | /** 291 | * @notice Returns an account's token ID. 292 | * @param account The address for which the token ID should be retrieved. 293 | * @return The token ID. 294 | */ 295 | function tokenID(address account) external view returns (uint256); 296 | 297 | /** 298 | * @notice Returns the current token ID counter value. 299 | * @return The token ID counter value. 300 | */ 301 | function tokenIDCounter() external view returns (uint256); 302 | 303 | /** 304 | * @notice Returns amount of attributes currently tracked by the contract. 305 | * @return The amount of attributes currently tracked by the contract. 306 | */ 307 | function attributeCount() external view returns (uint256); 308 | 309 | /** 310 | * @notice Helper function that returns an attribute's type. 311 | * E.g., 0 (primaryID) => "bool" 312 | * E.g., 1 (countryCode) => "string" 313 | * 314 | * @param id The ID of the attribute for which the type is fetched. 315 | * 316 | * @return The type of the attribute. 317 | */ 318 | function getAttributeType(uint256 id) external view returns (string memory); 319 | 320 | /** 321 | * Returns the number of tokens in `owner`'s account. 322 | * @param owner The address of the owner whose balance will be checked. 323 | */ 324 | function balanceOf(address owner) external view returns (uint256); 325 | } 326 | -------------------------------------------------------------------------------- /contracts/vendor/proof-of-identity/interfaces/vendor/IAccountManager.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | /** 5 | * @dev account status is denoted by a fixed integer value. The values are 6 | * as below: 7 | * 0 - Not in list 8 | * 1 - Account pending approval 9 | * 2 - Active 10 | * 3 - Inactive 11 | * 4 - Suspended 12 | * 5 - Blacklisted 13 | * 6 - Revoked 14 | * 7 - Recovery Initiated for blacklisted accounts and pending approval 15 | * from network admins 16 | */ 17 | interface IAccountManager { 18 | function getAccountStatus(address _account) external view returns (uint256); 19 | 20 | /** 21 | * @notice updates the account status to the passed status value 22 | * @param _orgId - org id 23 | * @param _account - account id 24 | * @param _action - new status of the account 25 | * @dev the following actions are allowed 26 | * 1 - Suspend the account 27 | * 2 - Reactivate a suspended account 28 | * 3 - Blacklist an account 29 | * 4 - Initiate recovery for black listed account 30 | * 5 - Complete recovery of black listed account and update status to active 31 | */ 32 | function updateAccountStatus( 33 | string calldata _orgId, 34 | address _account, 35 | uint _action 36 | ) external; 37 | 38 | function assignAccountRole( 39 | address _account, 40 | string calldata _orgId, 41 | string calldata _roleId, 42 | bool _adminRole 43 | ) external; 44 | } 45 | -------------------------------------------------------------------------------- /contracts/vendor/proof-of-identity/interfaces/vendor/IPermissionsInterface.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | interface IPermissionsInterface { 5 | function assignAccountRole( 6 | address _account, 7 | string calldata _orgId, 8 | string calldata _roleId 9 | ) external; 10 | 11 | function updateAccountStatus( 12 | string calldata _orgId, 13 | address _account, 14 | uint256 _action 15 | ) external; 16 | } 17 | -------------------------------------------------------------------------------- /contracts/vendor/proof-of-identity/libraries/AttributeUtils.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /** 6 | * @dev Represents one attribute. 7 | * All attributes are stored as bytes via `abi.encode` in order to allow 8 | * for heterogeneous storage. 9 | */ 10 | struct Attribute { 11 | uint256 expiry; 12 | uint256 updatedAt; 13 | bytes data; 14 | } 15 | 16 | /** 17 | * @dev Represents the supported types of an attribute. 18 | */ 19 | enum SupportedAttributeType { 20 | STRING, 21 | BOOL, 22 | U256, 23 | BYTES 24 | } 25 | 26 | /** 27 | * @title AttributeUtils 28 | * @author Haven1 Dev Team 29 | * @dev Library that contains the `Attribute` struct and associated methods. 30 | */ 31 | library AttributeUtils { 32 | /** 33 | * @notice Sets an attribute for the first time. 34 | * @param attr The attribute to set. 35 | * @param expiry The timestamp of expiry of the attribute. 36 | * @param updatedAt The timestamp of the last time the attribute was updated. 37 | * @param data The attribute data to set in bytes. 38 | */ 39 | function setAttribute( 40 | Attribute storage attr, 41 | uint256 expiry, 42 | uint256 updatedAt, 43 | bytes memory data 44 | ) internal { 45 | attr.expiry = expiry; 46 | attr.updatedAt = updatedAt; 47 | attr.data = data; 48 | } 49 | 50 | /** 51 | * @notice Returns the string name of the `SupportedAttributeType`. 52 | * @param attrType The supported attribute type to check. 53 | * @return The string name of the requested attribute type. 54 | */ 55 | function toString( 56 | SupportedAttributeType attrType 57 | ) internal pure returns (string memory) { 58 | if (attrType == SupportedAttributeType.STRING) return "string"; 59 | if (attrType == SupportedAttributeType.BOOL) return "bool"; 60 | if (attrType == SupportedAttributeType.U256) return "uint256"; 61 | return "bytes"; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /contracts/vendor/proof-of-identity/libraries/BytesConversion.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /** 6 | * @title BytesConversion 7 | * @author Haven1 Dev Team 8 | * @dev Library to decode bytes to a given type. 9 | */ 10 | library BytesConversion { 11 | function toString(bytes memory b) internal pure returns (string memory) { 12 | if (b.length == 0) return ""; 13 | return abi.decode(b, (string)); 14 | } 15 | 16 | function toU256(bytes memory b) internal pure returns (uint256) { 17 | if (b.length == 0) return 0; 18 | return abi.decode(b, (uint256)); 19 | } 20 | 21 | function toBool(bytes memory b) internal pure returns (bool) { 22 | if (b.length == 0) return false; 23 | return abi.decode(b, (bool)); 24 | } 25 | 26 | function toBytes(bytes memory b) internal pure returns (bytes memory) { 27 | if (b.length == 0) return ""; 28 | return abi.decode(b, (bytes)); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /environment.d.ts: -------------------------------------------------------------------------------- 1 | declare global { 2 | namespace NodeJS { 3 | interface ProcessEnv { 4 | // Testnet 5 | TESTNET_CHAIN_ID?: string; 6 | HAVEN_TESTNET_RPC?: string; 7 | 8 | TESTNET_EXPLORER?: string; 9 | TESTNET_EXPLORER_API?: string; 10 | TESTNET_EXPLORER_API_KEY?: string; 11 | 12 | TESTNET_DEPLOYER?: string; 13 | TESTNET_ASSOCIATION_ADDRESS?: string; 14 | TESTNET_OPERATOR_ADDRESS?: string; 15 | TESTNET_DEV_ADDRESS?: string; 16 | TESTNET_FEE_CONTRACT?: string; 17 | TESTNET_POI_CONTRACT?: string; 18 | } 19 | } 20 | } 21 | 22 | export {}; 23 | -------------------------------------------------------------------------------- /hardhat.config.ts: -------------------------------------------------------------------------------- 1 | import "hardhat/types/config"; 2 | import { type HardhatUserConfig } from "hardhat/config"; 3 | 4 | import "@nomicfoundation/hardhat-toolbox"; 5 | import "@nomiclabs/hardhat-solhint"; 6 | import "hardhat-contract-sizer"; 7 | import "@openzeppelin/hardhat-upgrades"; 8 | import "solidity-docgen"; 9 | 10 | import "tsconfig-paths/register"; 11 | 12 | import * as dotenv from "dotenv"; 13 | 14 | import "./tasks"; 15 | 16 | dotenv.config(); 17 | 18 | // See, in general, https://hardhat.org/hardhat-runner/docs/config#configuration 19 | const config: HardhatUserConfig = { 20 | networks: { 21 | hardhat: { 22 | mining: { 23 | auto: true, 24 | interval: 5000, 25 | }, 26 | }, 27 | remoteHardhat: { 28 | url: "http://hardhat:8545", 29 | }, 30 | }, 31 | solidity: { 32 | compilers: [ 33 | { 34 | version: "0.8.19", 35 | settings: { 36 | optimizer: { 37 | enabled: true, 38 | runs: 200, 39 | details: { yul: true }, 40 | }, 41 | }, 42 | }, 43 | ], 44 | }, 45 | gasReporter: { 46 | enabled: true, 47 | outputFile: "gas-report.txt", 48 | noColors: true, 49 | }, 50 | mocha: { 51 | timeout: 40_000, 52 | }, 53 | contractSizer: { 54 | // see: https://github.com/ItsNickBarry/hardhat-contract-sizer 55 | alphaSort: false, 56 | disambiguatePaths: false, 57 | runOnCompile: true, 58 | strict: true, 59 | }, 60 | docgen: { 61 | // see: https://github.com/OpenZeppelin/solidity-docgen#readme 62 | outputDir: "./vendor-docs", 63 | pages: "files", 64 | exclude: ["examples"], 65 | }, 66 | }; 67 | 68 | export default config; 69 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "haven1-solidity", 3 | "scripts": { 4 | "clean:coverage": "rm -rf coverage coverage.json", 5 | "clean:docs": "rm -rf docs", 6 | "clean": "npx hardhat clean && npm run clean:coverage && npm run clean:docs && rm -rf cache gas-report.txt", 7 | "test": "npx hardhat clean && npx hardhat test", 8 | "docgen": "npm run clean:docs && npx hardhat docgen", 9 | "coverage": "npm run clean:coverage && npx hardhat clean && npx hardhat coverage", 10 | "prettier:check": "npx prettier . --check", 11 | "prettier:write": "npx prettier . --write", 12 | "eslint": "npx eslint ./", 13 | "lint": "npm run prettier:check && npm run eslint && npx hardhat check", 14 | "deploy:local": "npx hardhat run --network localhost ./scripts/deployLocal.ts", 15 | "deploy:testnet": "npx hardhat run --network haven_testnet ./scripts/deployTestnet.ts", 16 | "verify:testnet": "npx hardhat verifyTestnet --network haven_testnet" 17 | }, 18 | "devDependencies": { 19 | "@nomicfoundation/hardhat-toolbox": "^3.0.0", 20 | "@nomiclabs/hardhat-solhint": "^3.0.1", 21 | "@openzeppelin/hardhat-upgrades": "^2.2.1", 22 | "@typescript-eslint/eslint-plugin": "^6.7.4", 23 | "@typescript-eslint/parser": "^6.7.4", 24 | "eslint": "^8.50.0", 25 | "hardhat": "^2.17.3", 26 | "hardhat-contract-sizer": "^2.10.0", 27 | "prettier": "^2.8.8", 28 | "prettier-plugin-solidity": "^1.1.3", 29 | "solidity-docgen": "^0.6.0-beta.36", 30 | "tsconfig-paths": "^4.2.0" 31 | }, 32 | "dependencies": { 33 | "@openzeppelin/contracts": "^4.9.3", 34 | "@openzeppelin/contracts-upgradeable": "^4.9.3", 35 | "dotenv": "^16.3.1" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /scripts/deployLocal.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @file This script handles deploying all the contracts for this project to 3 | * the local node. 4 | * 5 | * # Deployer Address 6 | * It assumes that the 0th indexed address in the `accounts` array is the 7 | * private key of the account that will be used to deploy the contracts. 8 | * 9 | * # Order of Deployments 10 | * 1. Mock Account Manager 11 | * 12 | * 2. Mock Permissions Interface 13 | * 14 | * 3. Proof of Identity 15 | * 16 | * 4. Fix Fee Oracle 17 | * 18 | * 5. Fee Contract 19 | * 20 | * 6. Simple Storage 21 | * 22 | * 7. MocK NFT - To be used as the prize in the NFT Auction 23 | * 24 | * 8. NFT Auction 25 | */ 26 | 27 | /* IMPORT NODE MODULES 28 | ================================================== */ 29 | import { ethers } from "hardhat"; 30 | 31 | /* IMPORT CONSTANTS UTILS, AND TYPES 32 | ================================================== */ 33 | import { d } from "@utils/deploy/deployWrapper"; 34 | import { parseH1 } from "@utils/token"; 35 | import { WEEK_SEC } from "../test/constants"; 36 | import { writeJSON } from "@utils/json"; 37 | 38 | import { 39 | ProofOfIdentityArgs, 40 | deployProofOfIdentity, 41 | } from "@utils/deploy/proof-of-identity"; 42 | 43 | import { FeeInitalizerArgs, deployFeeContract } from "@utils/deploy/fee"; 44 | 45 | import { 46 | SimpleStorageInitalizerArgs, 47 | deploySimpleStorage, 48 | } from "@utils/deploy/simple-storage"; 49 | 50 | import { 51 | AuctionConfig, 52 | NFTAuctionInitalizerArgs, 53 | deployNFTAuction, 54 | getAuctionKind, 55 | } from "@utils/deploy/nft-auction"; 56 | 57 | /* SCRIPT 58 | ================================================== */ 59 | async function main() { 60 | /* Setup 61 | ======================================== */ 62 | const [assoc, operator, dev] = await ethers.getSigners(); 63 | 64 | const assocAddr = await assoc.getAddress(); 65 | const operatorAddr = await operator.getAddress(); 66 | const devAddr = await dev.getAddress(); 67 | 68 | /* Mock Account Manager 69 | ======================================== */ 70 | const accManager = await d("Account Manager", async function () { 71 | const f = await ethers.getContractFactory("MockAccountManager", assoc); 72 | const c = await f.deploy(); 73 | return await c.waitForDeployment(); 74 | }); 75 | 76 | /* Mock Permissions Interface 77 | ======================================== */ 78 | const permInterface = await d("Permissions Interface", async function () { 79 | const f = await ethers.getContractFactory( 80 | "MockPermissionsInterface", 81 | assoc 82 | ); 83 | 84 | const c = await f.deploy(accManager.address); 85 | return await c.waitForDeployment(); 86 | }); 87 | 88 | /* Proof of Identity 89 | ======================================== */ 90 | const poi = await d("Proof of Identity", async function () { 91 | const args: ProofOfIdentityArgs = { 92 | associationAddress: assocAddr, 93 | networkOperatorAddress: operatorAddr, 94 | deployerAddress: assocAddr, 95 | permissionsInterfaceAddress: permInterface.address, 96 | accountManagerAddress: accManager.address, 97 | }; 98 | 99 | return await deployProofOfIdentity(args, assoc); 100 | }); 101 | 102 | /* Fixed Fee Oracle 103 | ======================================== */ 104 | const oracle = await d("Fixed Fee Oracle", async function () { 105 | const startingVal = parseH1("1.2"); 106 | 107 | const f = await ethers.getContractFactory("FixedFeeOracle", assoc); 108 | const c = await f.deploy(assoc, operator, startingVal); 109 | return await c.waitForDeployment(); 110 | }); 111 | 112 | /* Fee Contract 113 | ======================================== */ 114 | const fee = await d("Fee Contract", async function () { 115 | const args: FeeInitalizerArgs = { 116 | oracleAddress: oracle.address, 117 | channels: [], 118 | weights: [], 119 | haven1Association: assocAddr, 120 | networkOperator: operatorAddr, 121 | deployer: assocAddr, 122 | minDevFee: parseH1("1"), 123 | maxDevFee: parseH1("3"), 124 | asscShare: parseH1("0.2"), 125 | gracePeriod: 600, 126 | }; 127 | 128 | return await deployFeeContract(args, assoc); 129 | }); 130 | 131 | /* Simple Storage 132 | ======================================== */ 133 | const simpleStorage = await d("Simple Storage", async function () { 134 | const fnSigs = ["incrementCount()", "decrementCount()"]; 135 | const fnFees = [parseH1("2"), parseH1("3")]; 136 | 137 | const args: SimpleStorageInitalizerArgs = { 138 | feeContract: fee.address, 139 | association: assocAddr, 140 | developer: devAddr, 141 | feeCollector: devAddr, 142 | fnSigs, 143 | fnFees, 144 | }; 145 | 146 | return await deploySimpleStorage(args, assoc); 147 | }); 148 | 149 | /* Mock NFT 150 | ======================================== */ 151 | const mockNFT = await d("Mock NFT", async function () { 152 | const f = await ethers.getContractFactory("MockNFT", assoc); 153 | const c = await f.deploy(10_000); 154 | return await c.waitForDeployment(); 155 | }); 156 | 157 | /* NFT AUCTION 158 | ======================================== */ 159 | const auction = await d("NFT Auction", async function () { 160 | const nftID = 1n; 161 | const fnSigs = ["bid()"]; 162 | const fnFees = [parseH1("1")]; 163 | 164 | const auctionConfig: AuctionConfig = { 165 | kind: getAuctionKind("ALL"), 166 | length: BigInt(WEEK_SEC), 167 | startingBid: parseH1("10"), 168 | nft: mockNFT.address, 169 | nftID, 170 | beneficiary: devAddr, 171 | }; 172 | 173 | const args: NFTAuctionInitalizerArgs = { 174 | feeContract: fee.address, 175 | proofOfIdentity: poi.address, 176 | association: assocAddr, 177 | developer: devAddr, 178 | feeCollector: devAddr, 179 | fnSigs, 180 | fnFees, 181 | auctionConfig, 182 | }; 183 | 184 | return await deployNFTAuction(args, assoc); 185 | }); 186 | 187 | /* Output 188 | ======================================== */ 189 | const data = { 190 | accountManager: accManager.address, 191 | permissionsInterface: permInterface.address, 192 | proofOfIdentity: poi.address, 193 | oracle: oracle.address, 194 | feeContract: fee.address, 195 | simpleStorage: simpleStorage.address, 196 | mockNFT: mockNFT.address, 197 | auction: auction.address, 198 | } as const satisfies Record; 199 | 200 | const path = "../deployment_data/local/deployments.json"; 201 | const success = writeJSON(path, data); 202 | 203 | if (!success) { 204 | console.error("Write to JSON failed"); 205 | } 206 | } 207 | 208 | main().catch(error => { 209 | console.error(error); 210 | process.exitCode = 1; 211 | }); 212 | -------------------------------------------------------------------------------- /scripts/deployTestnet.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @file This script handles deploying all the contracts in this project to the 3 | * Haven1 Testnet. 4 | * 5 | * # Deployer Address 6 | * It assumes that the 0th indexed address in the `accounts` array is the 7 | * private key of the account that will be used to deploy the contracts. 8 | * E.g. 9 | * 10 | * ```typescript 11 | * const config: HardhatUserConfig = { 12 | * networks: { 13 | * haven_testnet: { 14 | * url: HAVEN_TESTNET_RPC, 15 | * accounts: [TESTNET_DEPLOYER], // this account will deploy 16 | * }, 17 | * }, 18 | * } 19 | * ``` 20 | * 21 | * # Order of Deployments 22 | * 1. Simple Storage 23 | * 24 | * 2. MocK NFT - To be used as the prize in the NFT Auction 25 | * 26 | * 3. NFT Auction 27 | */ 28 | 29 | /* IMPORT NODE MODULES 30 | ================================================== */ 31 | import { ethers } from "hardhat"; 32 | 33 | /* IMPORT CONSTANTS UTILS, AND TYPES 34 | ================================================== */ 35 | import { d } from "@utils/deploy/deployWrapper"; 36 | import { parseH1 } from "@utils/token"; 37 | import { WEEK_SEC } from "../test/constants"; 38 | import { writeJSON } from "@utils/json"; 39 | import { checkENV } from "@utils/checkENV"; 40 | 41 | import { 42 | SimpleStorageInitalizerArgs, 43 | deploySimpleStorage, 44 | } from "@utils/deploy/simple-storage"; 45 | import { 46 | AuctionConfig, 47 | NFTAuctionInitalizerArgs, 48 | deployNFTAuction, 49 | getAuctionKind, 50 | } from "@utils/deploy/nft-auction"; 51 | 52 | /* CONSTANTS, UITLS, AND TYPES 53 | ================================================== */ 54 | const REQUIRED_VARS = [ 55 | "TESTNET_CHAIN_ID", 56 | "HAVEN_TESTNET_RPC", 57 | "TESTNET_DEPLOYER", 58 | "TESTNET_ASSOCIATION_ADDRESS", 59 | "TESTNET_DEV_ADDRESS", 60 | "TESTNET_FEE_CONTRACT", 61 | "TESTNET_POI_CONTRACT", 62 | ]; 63 | 64 | /* SCRIPT 65 | ================================================== */ 66 | async function main() { 67 | // check to make sure that all required env vars are present 68 | // if this test passes, any env vars defined above can now safely be cast 69 | // as string. 70 | const missingVars = checkENV(REQUIRED_VARS); 71 | if (missingVars.length > 0) { 72 | throw new Error(`ErrMissingVars: ${missingVars.join("\n")}`); 73 | } 74 | 75 | /* Setup 76 | ======================================== */ 77 | const [deployer] = await ethers.getSigners(); 78 | 79 | const association = process.env.TESTNET_ASSOCIATION_ADDRESS as string; 80 | const developer = process.env.TESTNET_DEV_ADDRESS as string; 81 | const feeContract = process.env.TESTNET_FEE_CONTRACT as string; 82 | const poiContract = process.env.TESTNET_POI_CONTRACT as string; 83 | 84 | /* Simple Storage 85 | ======================================== */ 86 | const simpleStorage = await d("Simple Storage", async function () { 87 | const fnSigs = ["incrementCount()", "decrementCount()"]; 88 | const fnFees = [parseH1("2"), parseH1("3")]; 89 | 90 | const args: SimpleStorageInitalizerArgs = { 91 | feeContract, 92 | association, 93 | developer, 94 | feeCollector: developer, 95 | fnSigs, 96 | fnFees, 97 | }; 98 | 99 | return await deploySimpleStorage(args, deployer); 100 | }); 101 | 102 | /* Mock NFT 103 | ======================================== */ 104 | const mockNFT = await d("Mock NFT", async function () { 105 | const f = await ethers.getContractFactory("MockNFT", deployer); 106 | const c = await f.deploy(10_000); 107 | return await c.waitForDeployment(); 108 | }); 109 | 110 | /* NFT AUCTION 111 | ======================================== */ 112 | const auction = await d("NFT Auction", async function () { 113 | const nftID = 1n; 114 | const fnSigs = ["bid()"]; 115 | const fnFees = [parseH1("1")]; 116 | 117 | const auctionConfig: AuctionConfig = { 118 | kind: getAuctionKind("ALL"), 119 | length: BigInt(WEEK_SEC), 120 | startingBid: parseH1("10"), 121 | nft: mockNFT.address, 122 | nftID, 123 | beneficiary: developer, 124 | }; 125 | 126 | const args: NFTAuctionInitalizerArgs = { 127 | feeContract: feeContract, 128 | proofOfIdentity: poiContract, 129 | association: association, 130 | developer, 131 | feeCollector: developer, 132 | fnSigs, 133 | fnFees, 134 | auctionConfig, 135 | }; 136 | 137 | return await deployNFTAuction(args, deployer); 138 | }); 139 | 140 | /* Output 141 | ======================================== */ 142 | const data = { 143 | simpleStorage: simpleStorage.address, 144 | mockNFT: mockNFT.address, 145 | auction: auction.address, 146 | } as const satisfies Record; 147 | 148 | const path = "../deployment_data/testnet/deployments.json"; 149 | const success = writeJSON(path, data); 150 | 151 | if (!success) { 152 | console.error("Write to JSON failed"); 153 | } 154 | } 155 | 156 | main().catch(error => { 157 | console.error(error); 158 | process.exitCode = 1; 159 | }); 160 | -------------------------------------------------------------------------------- /tasks/accounts.ts: -------------------------------------------------------------------------------- 1 | import { task } from "hardhat/config"; 2 | 3 | // This is a sample Hardhat task. To learn how to create your own go to: 4 | // https://hardhat.org/guides/create-task.html 5 | task("accounts", "prints all signer accounts").setAction(async function ( 6 | _, 7 | { ethers } 8 | ) { 9 | const accounts = await ethers.getSigners(); 10 | for (let i = 0; i < accounts.length; ++i) { 11 | let out = `Account #${i}:\n${accounts[i].address}\n`; 12 | console.log(out); 13 | } 14 | }); 15 | -------------------------------------------------------------------------------- /tasks/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./accounts"; 2 | -------------------------------------------------------------------------------- /test/constants.ts: -------------------------------------------------------------------------------- 1 | /** One day in seconds. */ 2 | export const DAY_SEC = 86_400; 3 | 4 | /** One week in seconds. */ 5 | export const WEEK_SEC = DAY_SEC * 7; 6 | 7 | /** One year in seconds. */ 8 | export const YEAR_SEC = DAY_SEC * 365; 9 | 10 | /** The zero, or null, address. */ 11 | export const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000"; 12 | 13 | /** Null address used to represent native H1 */ 14 | export const H1_ADDRESS = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"; 15 | 16 | /** The interface ID for ERC721. */ 17 | export const ERC_721_INTERFACE_ID = "0x80ac58cd"; 18 | 19 | /** The interface ID for ERC1155. */ 20 | export const ERC_1155_INTERFACE_ID = "0xd9b67a26"; 21 | 22 | /** Collection of Access Control revert messages. */ 23 | const ACCESS_CONTROL_REVERSIONS = { 24 | MISSING_ROLE: 25 | /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/, 26 | } as const satisfies Record; 27 | 28 | type AccessControlKey = keyof typeof ACCESS_CONTROL_REVERSIONS; 29 | 30 | /** 31 | * Function to return the regex for an OZ `AccessControl` reversion message. 32 | * 33 | * @function accessControlErr 34 | * @param {AccessControlKey} err 35 | * @returns {RegExp} 36 | */ 37 | export function accessControlErr(err: AccessControlKey): RegExp { 38 | return ACCESS_CONTROL_REVERSIONS[err]; 39 | } 40 | 41 | /** Collection of Pausable revert messages. */ 42 | const PAUSABLE_ERRORS = { 43 | WHEN_NOT_PAUSED: "Pausable: paused", 44 | WHEN_PAUSED: "Pausable: not paused", 45 | } as const satisfies Record; 46 | 47 | type PausableErrorsKey = keyof typeof PAUSABLE_ERRORS; 48 | 49 | /** 50 | * Function to return an error message associated with an OZ `Pausable` contract 51 | * error. 52 | * 53 | * @function pausableErr 54 | * @param {PausableErrorsKey} err 55 | * @returns {string} 56 | */ 57 | export function pausableErr(err: PausableErrorsKey): string { 58 | return PAUSABLE_ERRORS[err]; 59 | } 60 | 61 | /** Collection of Safe ERC20 revert messages. */ 62 | const SAFE_ERC20_ERRORS = { 63 | LOW_LEVEL_CALL: "SafeERC20: low-level call failed", 64 | ERC20_OP: "SafeERC20: ERC20 operation did not succeed", 65 | } as const satisfies Record; 66 | 67 | type SafeERC20Error = keyof typeof SAFE_ERC20_ERRORS; 68 | 69 | /** 70 | * Function to return an error message associated with an OZ `SafeERC20` lib. 71 | * 72 | * @function safeERC20Err 73 | * @param {SafeERC20Error} err 74 | * @returns {string} 75 | */ 76 | export function safeERC20Err(err: SafeERC20Error): string { 77 | return SAFE_ERC20_ERRORS[err]; 78 | } 79 | 80 | /** Collection of ERC20 revert messages. */ 81 | const ERC20_ERRORS = { 82 | BURN_FROM_ZERO_ADDRESS: "ERC20: burn from the zero address", 83 | BURN_EXCEEDS_BALANCE: "ERC20: burn amount exceeds balance", 84 | MINT_TO_ZERO_ADDRESS: "ERC20: mint to the zero address", 85 | APPROVE_FROM_ZERO_ADDRESS: "ERC20: approve from the zero address", 86 | APPROVE_TO_ZERO_ADDRESS: "ERC20: approve to the zero address", 87 | INSUFFICIENT_ALLOWANCE: "ERC20: insufficient allowance", 88 | DECREASE_ALLOWANCE: "ERC20: decreased allowance below zero", 89 | TRANSFER_FROM_ZERO_ADDRESS: "ERC20: transfer from the zero address", 90 | TRANSFER_TO_ZERO_ADDRESS: "ERC20: transfer to the zero address", 91 | TRANSFER_EXCEEDS_BALANCE: "ERC20: transfer amount exceeds balance", 92 | } as const satisfies Record; 93 | 94 | type ERC20Error = keyof typeof ERC20_ERRORS; 95 | 96 | /** 97 | * Function to return an error message associated with an OZ `ERC20` contract. 98 | * 99 | * @function erc20Err 100 | * @param {ERC20Error} err 101 | * @returns {string} 102 | */ 103 | export function erc20Err(err: ERC20Error): string { 104 | return ERC20_ERRORS[err]; 105 | } 106 | 107 | /** Collection of Initializable revert messages. */ 108 | const INITIALIZBLE_ERRORS = { 109 | ALREADY_INITIALIZED: "Initializable: contract is already initialized", 110 | IS_INITIALIZING: "Initializable: contract is initializing", 111 | NOT_INITIALIZING: "Initializable: contract is not initializing", 112 | } as const satisfies Record; 113 | 114 | type InitializableError = keyof typeof INITIALIZBLE_ERRORS; 115 | 116 | /** 117 | * Function to return an error message associated with the OZ `Initializalbe` 118 | * contract. 119 | * 120 | * @function initialiazbleErr 121 | * @param {InitializableError} err 122 | * @returns {string} 123 | */ 124 | export function initialiazbleErr(err: InitializableError): string { 125 | return INITIALIZBLE_ERRORS[err]; 126 | } 127 | 128 | type H1DevelopedErrorKey = keyof typeof H1_DEVELOPED_ERRORS; 129 | 130 | /** Collection of H1 Developed revert messages. */ 131 | const H1_DEVELOPED_ERRORS = { 132 | TRANSFER_FAILED: "H1Developed__FeeTransferFailed", 133 | INVALID_ADDRESS: "H1Developed__InvalidAddress", 134 | INSUFFICIENT_FUNDS: "H1Developed__InsufficientFunds", 135 | ARRAY_LENGTH_MISMATCH: "H1Developed__ArrayLengthMismatch", 136 | ARRAY_LENGTH_ZERO: "H1Developed__ArrayLengthZero", 137 | OUT_OF_BOUNDS: "H1Developed__IndexOutOfBounds", 138 | INVALID_FN_SIG: "H1Developed__InvalidFnSignature", 139 | INVALID_FEE_AMT: "H1Developed__InvalidFeeAmount", 140 | } as const satisfies Record; 141 | 142 | /** 143 | * Function to return an error message from the `H1DevelopedApplication` 144 | * contract. 145 | * 146 | * @function h1DevelopedErr 147 | * @param {H1DevelopedErrorKey} err 148 | * @returns {string} 149 | */ 150 | export function h1DevelopedErr(err: H1DevelopedErrorKey): string { 151 | return H1_DEVELOPED_ERRORS[err]; 152 | } 153 | -------------------------------------------------------------------------------- /test/nft-auction/setup.ts: -------------------------------------------------------------------------------- 1 | /* IMPORT NODE MODULES 2 | ================================================== */ 3 | import { ethers } from "hardhat"; 4 | 5 | /* IMPORT TYPES 6 | ================================================== */ 7 | import type { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers"; 8 | import type { 9 | AddressLike, 10 | BigNumberish, 11 | ContractTransactionReceipt, 12 | } from "ethers"; 13 | import type { 14 | FeeContract, 15 | FixedFeeOracle, 16 | MockAccountManager, 17 | MockNFT, 18 | MockPermissionsInterface, 19 | NFTAuction, 20 | ProofOfIdentity, 21 | } from "@typechain"; 22 | import type { ProofOfIdentityArgs } from "@utils/deploy/proof-of-identity"; 23 | import type { NFTAuctionInitalizerArgs } from "@utils/deploy/nft-auction"; 24 | import type { FeeInitalizerArgs } from "@utils/deploy/fee"; 25 | 26 | /* IMPORT CONSTANTS AND UTILS 27 | ================================================== */ 28 | import { deployProofOfIdentity } from "@utils/deploy/proof-of-identity"; 29 | import { deployNFTAuction, getAuctionKind } from "@utils/deploy/nft-auction"; 30 | import { deployFeeContract } from "@utils/deploy/fee"; 31 | import { parseH1 } from "@utils/token"; 32 | import { WEEK_SEC } from "../constants"; 33 | 34 | /* CONSTANTS, TYPES AND UTILS 35 | ================================================== */ 36 | export type IssueIdArgs = { 37 | account: AddressLike; 38 | primaryID: boolean; 39 | countryCode: string; 40 | proofOfLiveliness: boolean; 41 | userType: BigNumberish; 42 | expiries: [BigNumberish, BigNumberish, BigNumberish, BigNumberish]; 43 | tokenURI: string; 44 | }; 45 | 46 | export type UserTypeKey = keyof typeof USER_TYPE; 47 | export type UserTypeVal = (typeof USER_TYPE)[keyof typeof USER_TYPE]; 48 | 49 | export const USER_TYPE = { 50 | RETAIL: 1, 51 | INSTITUTION: 2, 52 | } as const; 53 | 54 | /** 55 | * Returns the numeric value of a given user type. 56 | * 57 | * @function userType 58 | * @param {UserTypeKey} key 59 | * @returns {UserTypeVal} 60 | */ 61 | export function userType(key: UserTypeKey): UserTypeVal { 62 | return USER_TYPE[key]; 63 | } 64 | 65 | type AuctionErrorKey = keyof typeof AUCTION_ERRORS; 66 | 67 | const AUCTION_ERRORS = { 68 | ZERO_ADDRESS: "Auction__ZeroAddress", 69 | INVALID_AUCTION_KIND: "Auction__InvalidAuctionKind", 70 | INVALID_AUCTION_LENGTH: "Auction__InvalidAuctionLength", 71 | NOT_STARTED: "Auction__AuctionNotStarted", 72 | ACTIVE: "Auction__AuctionActive", 73 | FINISHED: "Auction__AuctionFinished", 74 | NO_ID: "Auction__NoIdentityNFT", 75 | SUSPENDED: "Auction__Suspended", 76 | ATTRIBUTE_EXPIRED: "Auction__AttributeExpired", 77 | USER_TYPE: "Auction__UserType", 78 | BID_TOO_LOW: "Auction__BidTooLow", 79 | ALREADY_HIGHEST: "Auction__AlreadyHighestBidder", 80 | TRANSFER_FAILER: "Auction__TransferFailed", 81 | ZERO_VALUE: "Auction__ZeroValue", 82 | } as const satisfies Record; 83 | 84 | /** 85 | * Returns an error message from the `NFTAuction` contract. 86 | * 87 | * @function auctionErr 88 | * @param {AuctionErrorKey} err 89 | * @returns {string} 90 | */ 91 | export function auctionErr(err: AuctionErrorKey): string { 92 | return AUCTION_ERRORS[err]; 93 | } 94 | 95 | /* TEST DEPLOY 96 | ================================================== */ 97 | /** 98 | * Creates a new instances of NFTAuctionTest 99 | * @class NFTAuctionTest 100 | */ 101 | export class NFTAuctionTest { 102 | /* Vars 103 | ======================================== */ 104 | private _isInitialized: boolean; 105 | 106 | private _association!: HardhatEthersSigner; 107 | private _associationAddress!: string; 108 | 109 | private _networkOperator!: HardhatEthersSigner; 110 | private _networkOperatorAddress!: string; 111 | 112 | private _developer!: HardhatEthersSigner; 113 | private _developerAddress!: string; 114 | 115 | private _accounts!: HardhatEthersSigner[]; 116 | private _accountAddresses!: string[]; 117 | 118 | private _proofOfIdContract!: ProofOfIdentity; 119 | private _proofOfIdContractAddress!: string; 120 | private _proofOfIdArgs!: ProofOfIdentityArgs; 121 | 122 | private _mockAccountManager!: MockAccountManager; 123 | private _mockAccountManagerAddress!: string; 124 | 125 | private _mockPermissionsInterface!: MockPermissionsInterface; 126 | private _mockPermissionsInterfaceAddress!: string; 127 | 128 | private _feeContract!: FeeContract; 129 | private _feeInitializerArgs!: FeeInitalizerArgs; 130 | private _feeContractAddress!: string; 131 | 132 | private _feeOracleContract!: FixedFeeOracle; 133 | 134 | private _nftContract!: MockNFT; 135 | private _nftContractAddress!: string; 136 | 137 | private _auctionContract!: NFTAuction; 138 | private _auctionContractAddress!: string; 139 | private _auctionInitializerArgs!: NFTAuctionInitalizerArgs; 140 | 141 | /* Init 142 | ======================================== */ 143 | /** 144 | * Private constructor due to requirement for async init work. 145 | * 146 | * @constructor 147 | * @private 148 | */ 149 | private constructor() { 150 | this._accounts = []; 151 | this._accountAddresses = []; 152 | 153 | this._isInitialized = false; 154 | } 155 | 156 | /** 157 | * Initializes `NFTAuctionTest`. `isInitialized` will return false until 158 | * this is run. 159 | * 160 | * Deploys an auction with a base config of: 161 | * - Association as the deployer; 162 | * - "ALL" (`3`) as the type; 163 | * - `1` as the NFT ID; 164 | * - `10` H1 as the starting bid; and 165 | * - Seven (7) days as the auction length. 166 | * 167 | * # Error 168 | * 169 | * Will throw if any of the deployments are not successful 170 | * 171 | * @private 172 | * @async 173 | * @method init 174 | * @returns {Promise} - Promise that resolves to the `NFTAuctionTest` 175 | * @throws 176 | */ 177 | private async init(): Promise { 178 | // Accounts 179 | const [assc, op, developer, ...rest] = await ethers.getSigners(); 180 | 181 | this._association = assc; 182 | this._associationAddress = await assc.getAddress(); 183 | 184 | this._networkOperator = op; 185 | this._networkOperatorAddress = await op.getAddress(); 186 | 187 | this._developer = developer; 188 | this._developerAddress = await developer.getAddress(); 189 | 190 | for (let i = 0; i < rest.length; ++i) { 191 | this._accounts.push(rest[i]); 192 | this._accountAddresses.push(await rest[i].getAddress()); 193 | } 194 | 195 | // Account Manager 196 | this._mockAccountManager = await this.deployMockAccountManager(); 197 | this._mockAccountManagerAddress = 198 | await this._mockAccountManager.getAddress(); 199 | 200 | // Permissions Interface 201 | this._mockPermissionsInterface = 202 | await this.deployMockPermissionsInterface( 203 | this._mockAccountManagerAddress 204 | ); 205 | this._mockPermissionsInterfaceAddress = 206 | await this._mockPermissionsInterface.getAddress(); 207 | 208 | // Proof of Identity 209 | this._proofOfIdArgs = { 210 | associationAddress: this._associationAddress, 211 | networkOperatorAddress: this._networkOperatorAddress, 212 | deployerAddress: this._associationAddress, 213 | permissionsInterfaceAddress: this._mockPermissionsInterfaceAddress, 214 | accountManagerAddress: this._mockAccountManagerAddress, 215 | }; 216 | 217 | this._proofOfIdContract = await deployProofOfIdentity( 218 | this._proofOfIdArgs, 219 | assc, 220 | 0 221 | ); 222 | 223 | this._proofOfIdContractAddress = 224 | await this._proofOfIdContract.getAddress(); 225 | 226 | // Fee Oracle 227 | this._feeOracleContract = await this.deployFeeOracle( 228 | this._associationAddress, 229 | this._networkOperatorAddress, 230 | parseH1("1") 231 | ); 232 | 233 | // Fee Contract 234 | this._feeInitializerArgs = { 235 | oracleAddress: await this._feeOracleContract.getAddress(), 236 | channels: [], 237 | weights: [], 238 | haven1Association: this._associationAddress, 239 | networkOperator: this._networkOperatorAddress, 240 | deployer: this._associationAddress, 241 | minDevFee: parseH1("1"), 242 | maxDevFee: parseH1("3"), 243 | asscShare: parseH1("0.2"), 244 | gracePeriod: 600, 245 | }; 246 | 247 | this._feeContract = await deployFeeContract( 248 | this._feeInitializerArgs, 249 | this._association, 250 | 0 251 | ); 252 | 253 | this._feeContractAddress = await this._feeContract.getAddress(); 254 | 255 | // NFT 256 | this._nftContract = await this.deployNFT(); 257 | this._nftContractAddress = await this._nftContract.getAddress(); 258 | 259 | await this._nftContract.mint(this._developerAddress); 260 | 261 | // Auction 262 | const nftID = 1n; 263 | const fnSigs = ["bid()"]; 264 | const fnFees = [parseH1("1")]; 265 | 266 | this._auctionInitializerArgs = { 267 | feeContract: this._feeContractAddress, 268 | proofOfIdentity: this._proofOfIdContractAddress, 269 | association: this._associationAddress, 270 | developer: this._developerAddress, 271 | feeCollector: this._developerAddress, 272 | fnSigs, 273 | fnFees, 274 | auctionConfig: { 275 | kind: getAuctionKind("ALL"), 276 | length: BigInt(WEEK_SEC), 277 | startingBid: parseH1("10"), 278 | nft: this._nftContractAddress, 279 | nftID, 280 | beneficiary: this._developerAddress, 281 | }, 282 | }; 283 | 284 | this._auctionContract = await deployNFTAuction( 285 | this._auctionInitializerArgs, 286 | this._association, 287 | 0 288 | ); 289 | 290 | this._auctionContractAddress = await this._auctionContract.getAddress(); 291 | 292 | // Approve the Auction contract to transfer the NFT from the dev 293 | const txRes = await this._nftContract 294 | .connect(this._developer) 295 | .approve(this._auctionContractAddress, nftID); 296 | 297 | await txRes.wait(); 298 | 299 | const bal = await this._nftContract.balanceOf(this._developerAddress); 300 | 301 | if (bal != 1n) { 302 | throw new Error("Auction: NFT transfer unsuccessful"); 303 | } 304 | 305 | this._isInitialized = true; 306 | 307 | return this; 308 | } 309 | 310 | /** 311 | * Static method to create a new instance of `NFTAuctionTest`, runs required 312 | * init and returns the instance. 313 | * 314 | * Deploys an auction with a base config of: 315 | * - Association as the deployer; 316 | * - "ALL" (`3`) as the type; 317 | * - `1` as the NFT ID; 318 | * - `10` H1 as the starting bid; and 319 | * - Seven (7) days as the auction length. 320 | * 321 | * # Error 322 | * 323 | * Will throw if any of the deployments are not successful 324 | * 325 | * @public 326 | * @static 327 | * @async 328 | * @method create 329 | * @returns {Promise} - Promise that resolves to `NFTAuctionTest` 330 | * @throws 331 | */ 332 | public static async create(): Promise { 333 | const instance = new NFTAuctionTest(); 334 | return await instance.init(); 335 | } 336 | 337 | /* Test Contract Deployers 338 | ======================================== */ 339 | /** 340 | * @method deployMockAccountManager 341 | * @async 342 | * @public 343 | * @returns {Promise} 344 | */ 345 | public async deployMockAccountManager(): Promise { 346 | const f = await ethers.getContractFactory("MockAccountManager"); 347 | const c = await f.deploy(); 348 | return await c.waitForDeployment(); 349 | } 350 | /** 351 | * @method deployMockPermissionsInterface 352 | * @async 353 | * @public 354 | * @returns {Promise} 355 | */ 356 | public async deployMockPermissionsInterface( 357 | accountManager: string 358 | ): Promise { 359 | const f = await ethers.getContractFactory("MockPermissionsInterface"); 360 | const c = await f.deploy(accountManager); 361 | return await c.waitForDeployment(); 362 | } 363 | 364 | /** 365 | * @method deployFeeOracle 366 | * @async 367 | * @public 368 | * @param {string} assc 369 | * @param {string} op 370 | * @param {bigint} val 371 | * @returns {Promise} 372 | */ 373 | public async deployFeeOracle( 374 | assc: string, 375 | op: string, 376 | val: bigint 377 | ): Promise { 378 | const f = await ethers.getContractFactory("FixedFeeOracle"); 379 | const c = await f.deploy(assc, op, val); 380 | return await c.waitForDeployment(); 381 | } 382 | 383 | /** 384 | * Deploys the NFT contract with a max supply of `10_000`. 385 | * 386 | * @method deployNFT 387 | * @async 388 | * @public 389 | * @returns {Promise} 390 | * @throws 391 | */ 392 | public async deployNFT(): Promise { 393 | const f = await ethers.getContractFactory("MockNFT"); 394 | const c = await f.deploy(10_000); 395 | return await c.waitForDeployment(); 396 | } 397 | 398 | /* Getters 399 | ======================================== */ 400 | /** 401 | * @method association 402 | * @returns {HardhatEthersSigner} 403 | * @throws 404 | */ 405 | public get association(): HardhatEthersSigner { 406 | this.validateInitialized("association"); 407 | return this._association; 408 | } 409 | 410 | /** 411 | * @method associationAddress 412 | * @returns {string} 413 | * @throws 414 | */ 415 | public get associationAddress(): string { 416 | this.validateInitialized("associationAddress"); 417 | return this._associationAddress; 418 | } 419 | 420 | /** 421 | * @method networkOperator 422 | * @returns {HardhatEthersSigner} 423 | * @throws 424 | */ 425 | public get networkOperator(): HardhatEthersSigner { 426 | this.validateInitialized("networkOperator"); 427 | return this._networkOperator; 428 | } 429 | 430 | /** 431 | * @method networkOperatorAddress 432 | * @returns {string} 433 | * @throws 434 | */ 435 | public get networkOperatorAddress(): string { 436 | this.validateInitialized("networkOperatorAddress"); 437 | return this._networkOperatorAddress; 438 | } 439 | 440 | /** 441 | * @method developer 442 | * @returns {HardhatEthersSigner} 443 | * @throws 444 | */ 445 | public get developer(): HardhatEthersSigner { 446 | this.validateInitialized("developer"); 447 | return this._developer; 448 | } 449 | 450 | /** 451 | * @method developerAddress 452 | * @returns {string} 453 | * @throws 454 | */ 455 | public get developerAddress(): string { 456 | this.validateInitialized("developerAddress"); 457 | return this._developerAddress; 458 | } 459 | 460 | /** 461 | * @method accounts 462 | * @returns {HardhatEthersSigner[]} 463 | * @throws 464 | */ 465 | public get accounts(): HardhatEthersSigner[] { 466 | this.validateInitialized("accounts"); 467 | return this._accounts; 468 | } 469 | 470 | /** 471 | * @method accountAddresses 472 | * @returns {string[]} 473 | * @throws 474 | */ 475 | public get accountAddresses(): string[] { 476 | this.validateInitialized("accountAddresses"); 477 | return this._accountAddresses; 478 | } 479 | 480 | /** 481 | * @method proofOfIdContract 482 | * @returns {ProofOfIdentity} 483 | * @throws 484 | */ 485 | public get proofOfIdContract(): ProofOfIdentity { 486 | this.validateInitialized("proofOfIdContract"); 487 | return this._proofOfIdContract; 488 | } 489 | 490 | /** 491 | * @method proofOfIdContractAddress 492 | * @returns {string} 493 | * @throws 494 | */ 495 | public get proofOfIdContractAddress(): string { 496 | this.validateInitialized("proofOfIdContractAddress"); 497 | return this._proofOfIdContractAddress; 498 | } 499 | 500 | /** 501 | * @method mockAccountManager 502 | * @returns {MockAccountManager} 503 | * @throws 504 | */ 505 | public get mockAccountManager(): MockAccountManager { 506 | this.validateInitialized("mockAccountManager"); 507 | return this._mockAccountManager; 508 | } 509 | 510 | /** 511 | * @method mockAccountManager 512 | * @returns {string} 513 | * @throws 514 | */ 515 | public get mockAccountManagerAddress(): string { 516 | this.validateInitialized("mockAccountManagerAddress"); 517 | return this._mockAccountManagerAddress; 518 | } 519 | 520 | /** 521 | * @method mockPermissionsInterface 522 | * @returns {MockPermissionsInterface} 523 | * @throws 524 | */ 525 | public get mockPermissionsInterface(): MockPermissionsInterface { 526 | this.validateInitialized("mockPermissionsInterface"); 527 | return this._mockPermissionsInterface; 528 | } 529 | 530 | /** 531 | * @method mockPermissionsInterfaceAddress 532 | * @returns {string} 533 | * @throws 534 | */ 535 | public get mockPermissionsInterfaceAddress(): string { 536 | this.validateInitialized("mockPermissionsInterfaceAddress"); 537 | return this._mockPermissionsInterfaceAddress; 538 | } 539 | 540 | /** 541 | * @method proofOfIdArgs 542 | * @returns {ProofOfIdentityArgs} 543 | * @throws 544 | */ 545 | public get proofOfIdArgs(): ProofOfIdentityArgs { 546 | this.validateInitialized("proofOfIdArgs"); 547 | return this._proofOfIdArgs; 548 | } 549 | 550 | /** 551 | * @method feeContract 552 | * @returns {FeeContract} 553 | * @throws 554 | */ 555 | public get feeContract(): FeeContract { 556 | this.validateInitialized("feeContract"); 557 | return this._feeContract; 558 | } 559 | 560 | /** 561 | * @method feeInitalizerArgs 562 | * @returns {FeeInitalizerArgs} 563 | * @throws 564 | */ 565 | public get feeInitalizerArgs(): FeeInitalizerArgs { 566 | this.validateInitialized("feeInitalizeArgs"); 567 | return this._feeInitializerArgs; 568 | } 569 | 570 | /** 571 | * @method feeContractAddress 572 | * @returns {string} 573 | * @throws 574 | */ 575 | public get feeContractAddress(): string { 576 | this.validateInitialized("feeContractAddress"); 577 | return this._feeContractAddress; 578 | } 579 | 580 | /** 581 | * @method feeOracleContract 582 | * @returns {FixedFeeOracle} 583 | * @throws 584 | */ 585 | public get feeOracleContract(): FixedFeeOracle { 586 | this.validateInitialized("feeOracleContract"); 587 | return this._feeOracleContract; 588 | } 589 | 590 | /** 591 | * @method auctionContract 592 | * @returns {MockNFT} 593 | * @throws 594 | */ 595 | public get nftContract(): MockNFT { 596 | this.validateInitialized("nftContract"); 597 | return this._nftContract; 598 | } 599 | 600 | /** 601 | * @method nftContractAddress 602 | * @returns {string} 603 | * @throws 604 | */ 605 | public get nftContractAddress(): string { 606 | this.validateInitialized("nftContractAddress"); 607 | return this._nftContractAddress; 608 | } 609 | 610 | /** 611 | * @method auctionContract 612 | * @returns {NFTAuction} 613 | * @throws 614 | */ 615 | public get auctionContract(): NFTAuction { 616 | this.validateInitialized("auctionContract"); 617 | return this._auctionContract; 618 | } 619 | 620 | /** 621 | * @method auctionContractAddress 622 | * @returns {string} 623 | * @throws 624 | */ 625 | public get auctionContractAddress(): string { 626 | this.validateInitialized("auctionContractAddress"); 627 | return this._auctionContractAddress; 628 | } 629 | 630 | /** 631 | * @method auctionInitializerArgs 632 | * @returns {NFTAuctionInitalizerArgs} 633 | * @throws 634 | */ 635 | public get auctionInitializerArgs(): NFTAuctionInitalizerArgs { 636 | this.validateInitialized("simpleStorageArgs"); 637 | return this._auctionInitializerArgs; 638 | } 639 | 640 | /* Helpers 641 | ======================================== */ 642 | /** 643 | * Issues an ID and returns the transaction reciept. 644 | * 645 | * @async 646 | * @func issueIdentity 647 | * @param {IssueIdArgs} args 648 | * @param {HardhatEthersSigner} [signer] 649 | * @returns {ContractTransactionReceipt | null} 650 | */ 651 | public async issueIdentity( 652 | args: IssueIdArgs, 653 | signer?: HardhatEthersSigner 654 | ): Promise { 655 | this.validateInitialized("issueIdentity"); 656 | 657 | const c = signer 658 | ? this._proofOfIdContract.connect(signer) 659 | : this._proofOfIdContract; 660 | 661 | const txRes = await c.issueIdentity( 662 | args.account, 663 | args.primaryID, 664 | args.countryCode, 665 | args.proofOfLiveliness, 666 | args.userType, 667 | args.expiries, 668 | args.tokenURI 669 | ); 670 | 671 | return await txRes.wait(); 672 | } 673 | 674 | /** 675 | * Validates if the class instance has been initialized. 676 | * 677 | * # Error 678 | * 679 | * Will throw an error if the class instance has not been initialized. 680 | * 681 | * @private 682 | * @method validateInitialized 683 | * @param {string} method 684 | * @throws 685 | */ 686 | private validateInitialized(method: string): void { 687 | if (!this._isInitialized) { 688 | throw new Error( 689 | `Deployment not initialized. Call create() before accessing ${method}.` 690 | ); 691 | } 692 | } 693 | } 694 | -------------------------------------------------------------------------------- /test/simple-storage/setup.ts: -------------------------------------------------------------------------------- 1 | /* IMPORT NODE MODULES 2 | ================================================== */ 3 | import { ethers } from "hardhat"; 4 | 5 | /* IMPORT TYPES 6 | ================================================== */ 7 | import type { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers"; 8 | import type { SimpleStorage, FeeContract, FixedFeeOracle } from "@typechain"; 9 | import type { FeeInitalizerArgs } from "@utils/deploy/fee"; 10 | import type { SimpleStorageInitalizerArgs } from "@utils/deploy/simple-storage"; 11 | 12 | /* IMPORT CONSTANTS AND UTILS 13 | ================================================== */ 14 | import { deployFeeContract } from "@utils/deploy/fee"; 15 | import { deploySimpleStorage } from "@utils/deploy/simple-storage"; 16 | import { parseH1 } from "@utils/token"; 17 | 18 | /* TEST DEPLOY 19 | ================================================== */ 20 | /** 21 | * Creates a new instances of TestDeployment 22 | * @class TestDeployment 23 | */ 24 | export class TestDeployment { 25 | /* Vars 26 | ======================================== */ 27 | private _isInitialized: boolean; 28 | 29 | private _association!: HardhatEthersSigner; 30 | private _associationAddress!: string; 31 | 32 | private _networkOperator!: HardhatEthersSigner; 33 | private _networkOperatorAddress!: string; 34 | 35 | private _deployer!: HardhatEthersSigner; 36 | private _deployerAddress!: string; 37 | 38 | private _developer!: HardhatEthersSigner; 39 | private _developerAddress!: string; 40 | 41 | private _accounts!: HardhatEthersSigner[]; 42 | private _accountAddresses!: string[]; 43 | 44 | private _feeContract!: FeeContract; 45 | private _feeInitializerArgs!: FeeInitalizerArgs; 46 | private _feeContractAddress!: string; 47 | 48 | private _simpleStorageContract!: SimpleStorage; 49 | private _simpleStorageContractAddress!: string; 50 | private _simpleStorageInitializerArgs!: SimpleStorageInitalizerArgs; 51 | 52 | private _feeOracleContract!: FixedFeeOracle; 53 | 54 | /* Init 55 | ======================================== */ 56 | /** 57 | * Private constructor due to requirement for async init work. 58 | * 59 | * @constructor 60 | * @private 61 | */ 62 | private constructor() { 63 | this._accounts = []; 64 | this._accountAddresses = []; 65 | 66 | this._isInitialized = false; 67 | } 68 | 69 | /** 70 | * Initializes `TestDeployment`. `isInitialized` will return false until 71 | * this is run. 72 | * 73 | * # Error 74 | * 75 | * Will throw if any of the deployments are not successful 76 | * 77 | * @private 78 | * @async 79 | * @method init 80 | * @returns {Promise} - Promise that resolves to the `TestDeployment` 81 | * @throws 82 | */ 83 | private async init(): Promise { 84 | // Accounts 85 | const [assc, op, deployer, developer, ...rest] = 86 | await ethers.getSigners(); 87 | 88 | this._association = assc; 89 | this._associationAddress = await assc.getAddress(); 90 | 91 | this._networkOperator = op; 92 | this._networkOperatorAddress = await op.getAddress(); 93 | 94 | this._deployer = deployer; 95 | this._deployerAddress = await deployer.getAddress(); 96 | 97 | this._developer = developer; 98 | this._developerAddress = await developer.getAddress(); 99 | 100 | for (let i = 0; i < rest.length; ++i) { 101 | this._accounts.push(rest[i]); 102 | this._accountAddresses.push(await rest[i].getAddress()); 103 | } 104 | 105 | // Fee Oracle 106 | this._feeOracleContract = await this.deployFeeOracle( 107 | this._associationAddress, 108 | this._networkOperatorAddress, 109 | parseH1("1") 110 | ); 111 | 112 | // Fee Contract 113 | this._feeInitializerArgs = { 114 | oracleAddress: await this._feeOracleContract.getAddress(), 115 | channels: [], 116 | weights: [], 117 | haven1Association: this._associationAddress, 118 | networkOperator: this._networkOperatorAddress, 119 | deployer: this._deployerAddress, 120 | minDevFee: parseH1("1"), 121 | maxDevFee: parseH1("3"), 122 | asscShare: parseH1("0.2"), 123 | gracePeriod: 600, 124 | }; 125 | 126 | this._feeContract = await deployFeeContract( 127 | this._feeInitializerArgs, 128 | this._deployer, 129 | 0 130 | ); 131 | 132 | this._feeContractAddress = await this._feeContract.getAddress(); 133 | 134 | // Simple Storage Contract 135 | 136 | // Note that these functions DO NOT exist on the Simple Storage 137 | // contract. They are added here simply to test functionality on init. 138 | // The correct functions are omitted so that the process of adding 139 | // function fees (while the responsibility of the Developed App 140 | // contract) can be demonstrated and tested in the `./simpleStorage.test.ts` 141 | // file. 142 | const fnSigs = ["increment()", "decrement()"]; 143 | const fnFees = [parseH1("2"), parseH1("1")]; 144 | 145 | this._simpleStorageInitializerArgs = { 146 | feeContract: this._feeContractAddress, 147 | association: this._associationAddress, 148 | developer: this._developerAddress, 149 | feeCollector: this._developerAddress, 150 | fnSigs, 151 | fnFees, 152 | }; 153 | 154 | this._simpleStorageContract = await deploySimpleStorage( 155 | this._simpleStorageInitializerArgs, 156 | assc, 157 | 0 158 | ); 159 | 160 | this._simpleStorageContractAddress = 161 | await this._simpleStorageContract.getAddress(); 162 | 163 | // Init 164 | this._isInitialized = true; 165 | 166 | return this; 167 | } 168 | 169 | /** 170 | * Static method to create a new instance of `TestDeployment`, runs required 171 | * init and returns the instance. 172 | * 173 | * @public 174 | * @static 175 | * @async 176 | * @method create 177 | * @returns {Promise} - Promise that resolves to `TestDeployment` 178 | */ 179 | public static async create(): Promise { 180 | const instance = new TestDeployment(); 181 | return await instance.init(); 182 | } 183 | 184 | /* Test Contract Deployers 185 | ======================================== */ 186 | /** 187 | * @method deployFeeOracle 188 | * @async 189 | * @public 190 | * @param {string} assc 191 | * @param {string} op 192 | * @param {bigint} val 193 | * @returns {Promise} 194 | */ 195 | public async deployFeeOracle( 196 | assc: string, 197 | op: string, 198 | val: bigint 199 | ): Promise { 200 | const f = await ethers.getContractFactory("FixedFeeOracle"); 201 | const c = await f.deploy(assc, op, val); 202 | return await c.waitForDeployment(); 203 | } 204 | 205 | /* Getters 206 | ======================================== */ 207 | /** 208 | * @method association 209 | * @returns {HardhatEthersSigner} 210 | * @throws 211 | */ 212 | public get association(): HardhatEthersSigner { 213 | this.validateInitialized("association"); 214 | return this._association; 215 | } 216 | 217 | /** 218 | * @method associationAddress 219 | * @returns {string} 220 | * @throws 221 | */ 222 | public get associationAddress(): string { 223 | this.validateInitialized("associationAddress"); 224 | return this._associationAddress; 225 | } 226 | 227 | /** 228 | * @method networkOperator 229 | * @returns {HardhatEthersSigner} 230 | * @throws 231 | */ 232 | public get networkOperator(): HardhatEthersSigner { 233 | this.validateInitialized("networkOperator"); 234 | return this._networkOperator; 235 | } 236 | 237 | /** 238 | * @method networkOperatorAddress 239 | * @returns {string} 240 | * @throws 241 | */ 242 | public get networkOperatorAddress(): string { 243 | this.validateInitialized("networkOperatorAddress"); 244 | return this._networkOperatorAddress; 245 | } 246 | 247 | /** 248 | * @method deployer 249 | * @returns {HardhatEthersSigner} 250 | * @throws 251 | */ 252 | public get deployer(): HardhatEthersSigner { 253 | this.validateInitialized("deployer"); 254 | return this._deployer; 255 | } 256 | 257 | /** 258 | * @method deployerAddress 259 | * @returns {string} 260 | * @throws 261 | */ 262 | public get deployerAddress(): string { 263 | this.validateInitialized("deployerAddress"); 264 | return this._deployerAddress; 265 | } 266 | 267 | /** 268 | * @method developer 269 | * @returns {HardhatEthersSigner} 270 | * @throws 271 | */ 272 | public get developer(): HardhatEthersSigner { 273 | this.validateInitialized("developer"); 274 | return this._developer; 275 | } 276 | 277 | /** 278 | * @method developerAddress 279 | * @returns {string} 280 | * @throws 281 | */ 282 | public get developerAddress(): string { 283 | this.validateInitialized("developerAddress"); 284 | return this._developerAddress; 285 | } 286 | 287 | /** 288 | * @method accounts 289 | * @returns {HardhatEthersSigner[]} 290 | * @throws 291 | */ 292 | public get accounts(): HardhatEthersSigner[] { 293 | this.validateInitialized("accounts"); 294 | return this._accounts; 295 | } 296 | 297 | /** 298 | * @method accountAddresses 299 | * @returns {string[]} 300 | * @throws 301 | */ 302 | public get accountAddresses(): string[] { 303 | this.validateInitialized("accountAddresses"); 304 | return this._accountAddresses; 305 | } 306 | 307 | /** 308 | * @method feeContract 309 | * @returns {FeeContract} 310 | * @throws 311 | */ 312 | public get feeContract(): FeeContract { 313 | this.validateInitialized("feeContract"); 314 | return this._feeContract; 315 | } 316 | 317 | /** 318 | * @method feeInitalizerArgs 319 | * @returns {FeeInitalizerArgs} 320 | * @throws 321 | */ 322 | public get feeInitalizerArgs(): FeeInitalizerArgs { 323 | this.validateInitialized("feeInitalizeArgs"); 324 | return this._feeInitializerArgs; 325 | } 326 | 327 | /** 328 | * @method feeContractAddress 329 | * @returns {string} 330 | * @throws 331 | */ 332 | public get feeContractAddress(): string { 333 | this.validateInitialized("feeContractAddress"); 334 | return this._feeContractAddress; 335 | } 336 | 337 | /** 338 | * @method feeOracleContract 339 | * @returns {FixedFeeOracle} 340 | * @throws 341 | */ 342 | public get feeOracleContract(): FixedFeeOracle { 343 | this.validateInitialized("feeOracleContract"); 344 | return this._feeOracleContract; 345 | } 346 | 347 | /** 348 | * @method simpleStorageContract 349 | * @returns {SimpleStorage} 350 | * @throws 351 | */ 352 | public get simpleStorageContract(): SimpleStorage { 353 | this.validateInitialized("simpleStorageContract"); 354 | return this._simpleStorageContract; 355 | } 356 | 357 | /** 358 | * @method simpleStorageContractAddress 359 | * @returns {string} 360 | * @throws 361 | */ 362 | public get simpleStorageContractAddress(): string { 363 | this.validateInitialized("simpleStorageContractAddress"); 364 | return this._simpleStorageContractAddress; 365 | } 366 | 367 | /** 368 | * @method simpleStorageInitializerArgs 369 | * @returns {SimpleStorageInitalizerArgs} 370 | * @throws 371 | */ 372 | public get simpleStorageInitializerArgs(): SimpleStorageInitalizerArgs { 373 | this.validateInitialized("simpleStorageArgs"); 374 | return this._simpleStorageInitializerArgs; 375 | } 376 | 377 | /* Helpers 378 | ======================================== */ 379 | /** 380 | * Validates if the class instance has been initialized. 381 | * 382 | * # Error 383 | * 384 | * Will throw an error if the class instance has not been initialized. 385 | * 386 | * @private 387 | * @method validateInitialized 388 | * @param {string} method 389 | * @throws 390 | */ 391 | private validateInitialized(method: string): void { 392 | if (!this._isInitialized) { 393 | throw new Error( 394 | `Deployment not initialized. Call create() before accessing ${method}.` 395 | ); 396 | } 397 | } 398 | } 399 | -------------------------------------------------------------------------------- /test/simple-storage/simpleStorage.test.ts: -------------------------------------------------------------------------------- 1 | /* IMPORT NODE MODULES 2 | ================================================== */ 3 | import { loadFixture } from "@nomicfoundation/hardhat-toolbox/network-helpers"; 4 | import { expect } from "chai"; 5 | 6 | /* IMPORT CONSTANTS AND UTILS 7 | ================================================== */ 8 | import { TestDeployment } from "./setup"; 9 | import { 10 | ZERO_ADDRESS, 11 | accessControlErr, 12 | h1DevelopedErr, 13 | initialiazbleErr, 14 | pausableErr, 15 | } from "../constants"; 16 | import { parseH1 } from "@utils/token"; 17 | import { fnSelector } from "@utils/fnSelector"; 18 | 19 | /* CONSTANTS 20 | ================================================== */ 21 | const dir = { 22 | DECR: 0, 23 | INCR: 1, 24 | } as const; 25 | 26 | const evt = "Count"; 27 | 28 | /* TESTS 29 | ================================================== */ 30 | describe("H1 Developed Application - Simple Storage Example", function () { 31 | async function setup() { 32 | return await TestDeployment.create(); 33 | } 34 | 35 | /* Deployment and Init 36 | ========================================*/ 37 | describe("Deployment and Initialization", function () { 38 | it("Should have a deployment address", async function () { 39 | const t = await loadFixture(setup); 40 | const addr = t.simpleStorageContractAddress; 41 | expect(addr).to.have.length(42); 42 | expect(addr).to.not.equal(ZERO_ADDRESS); 43 | }); 44 | 45 | it("Should not allow initialize to be called a second time", async function () { 46 | const t = await loadFixture(setup); 47 | 48 | const c = t.simpleStorageContract; 49 | const a = t.simpleStorageInitializerArgs; 50 | const err = initialiazbleErr("ALREADY_INITIALIZED"); 51 | 52 | await expect( 53 | c.initialize( 54 | a.feeContract, 55 | a.association, 56 | a.developer, 57 | a.feeCollector, 58 | a.fnSigs, 59 | a.fnFees 60 | ) 61 | ).to.be.revertedWith(err); 62 | }); 63 | }); 64 | 65 | /* Increment Count 66 | ========================================*/ 67 | describe("Increment Count", function () { 68 | it("Should correctly increment the count", async function () { 69 | // vars 70 | const t = await loadFixture(setup); 71 | const c = t.simpleStorageContract; 72 | const d = c.connect(t.developer); 73 | 74 | const fnSig = "incrementCount()"; 75 | const fee = parseH1("2.25"); 76 | const fnSel = fnSelector(fnSig); 77 | 78 | const err = h1DevelopedErr("INSUFFICIENT_FUNDS"); 79 | 80 | // incr count before fee is added 81 | let count = await c.count(); 82 | expect(count).to.equal(0n); 83 | 84 | let txRes = await c.incrementCount(); 85 | await txRes.wait(); 86 | 87 | count = await c.count(); 88 | expect(count).to.equal(1n); 89 | 90 | // set a fee on incr count 91 | txRes = await d.proposeFee(fnSig, fee); 92 | await txRes.wait(); 93 | 94 | // approve fee 95 | txRes = await c.approveAllFees(); 96 | await txRes.wait(); 97 | 98 | // incr the count 99 | const feeAdj = await c.getFnFeeAdj(fnSel); 100 | 101 | txRes = await c.incrementCount({ value: feeAdj }); 102 | await txRes.wait(); 103 | 104 | count = await c.count(); 105 | expect(count).to.equal(2n); 106 | 107 | // sanity check on incr with no fee 108 | await expect(c.incrementCount()) 109 | .to.be.revertedWithCustomError(c, err) 110 | .withArgs(0n, feeAdj); 111 | }); 112 | 113 | it("Should emit a Count event", async function () { 114 | // vars 115 | const t = await loadFixture(setup); 116 | const c = t.simpleStorageContract; 117 | const d = c.connect(t.developer); 118 | const addr = t.associationAddress; 119 | 120 | const fnSig = "incrementCount()"; 121 | const fee = parseH1("2.25"); 122 | 123 | // set a fee on incr count 124 | let txRes = await d.proposeFee(fnSig, fee); 125 | await txRes.wait(); 126 | 127 | // approve fee 128 | txRes = await c.approveAllFees(); 129 | await txRes.wait(); 130 | 131 | // test event emit 132 | await expect(c.incrementCount({ value: fee })) 133 | .to.emit(c, evt) 134 | .withArgs(addr, dir.INCR, 1n, fee); 135 | }); 136 | 137 | it("Should revert if the contract is paused", async function () { 138 | // vars 139 | const t = await loadFixture(setup); 140 | const c = t.simpleStorageContract; 141 | const err = pausableErr("WHEN_NOT_PAUSED"); 142 | 143 | // state check 144 | let isPaused = await c.paused(); 145 | expect(isPaused).to.be.false; 146 | 147 | // pause the contract 148 | let txRes = await c.pause(); 149 | await txRes.wait(); 150 | 151 | // case - contract is paused 152 | await expect(c.incrementCount()).to.be.revertedWith(err); 153 | 154 | // case - contract is not paused 155 | txRes = await c.unpause(); 156 | await txRes.wait(); 157 | await expect(c.incrementCount()).to.not.be.reverted; 158 | }); 159 | }); 160 | 161 | /* Decrement Count 162 | ========================================*/ 163 | describe("Decrement Count", function () { 164 | it("Should correctly decrement the count", async function () { 165 | // vars 166 | const t = await loadFixture(setup); 167 | const c = t.simpleStorageContract; 168 | const d = c.connect(t.developer); 169 | 170 | const fnSig = "decrementCount()"; 171 | const fee = parseH1("1.75"); 172 | const fnSel = fnSelector(fnSig); 173 | 174 | const err = h1DevelopedErr("INSUFFICIENT_FUNDS"); 175 | 176 | // decr count before fee is added 177 | let count = await c.count(); 178 | expect(count).to.equal(0n); 179 | 180 | let txRes = await c.incrementCount(); 181 | await txRes.wait(); 182 | 183 | count = await c.count(); 184 | expect(count).to.equal(1n); 185 | 186 | txRes = await c.decrementCount(); 187 | await txRes.wait(); 188 | 189 | count = await c.count(); 190 | expect(count).to.equal(0n); 191 | 192 | // set a fee on decr count 193 | txRes = await d.proposeFee(fnSig, fee); 194 | await txRes.wait(); 195 | 196 | // approve fee 197 | txRes = await c.approveAllFees(); 198 | await txRes.wait(); 199 | 200 | // decr the count 201 | const feeAdj = await c.getFnFeeAdj(fnSel); 202 | 203 | txRes = await c.incrementCount({ value: feeAdj }); 204 | await txRes.wait(); 205 | 206 | count = await c.count(); 207 | expect(count).to.equal(1n); 208 | 209 | txRes = await c.decrementCount({ value: feeAdj }); 210 | await txRes.wait(); 211 | 212 | count = await c.count(); 213 | expect(count).to.equal(0n); 214 | 215 | // sanity check on decr with no fee 216 | await expect(c.decrementCount()) 217 | .to.be.revertedWithCustomError(c, err) 218 | .withArgs(0n, feeAdj); 219 | }); 220 | 221 | it("Should return early if the count is already zero", async function () { 222 | // vars 223 | const t = await loadFixture(setup); 224 | const c = t.simpleStorageContract; 225 | 226 | // state checks 227 | let count = await c.count(); 228 | expect(count).to.equal(0n); 229 | 230 | // should not revert and count remains zero 231 | const txRes = await c.decrementCount(); 232 | await txRes.wait(); 233 | 234 | count = await c.count(); 235 | expect(count).to.equal(0n); 236 | }); 237 | 238 | it("Should emit a Count event", async function () { 239 | // vars 240 | const t = await loadFixture(setup); 241 | const c = t.simpleStorageContract; 242 | const d = c.connect(t.developer); 243 | const addr = t.associationAddress; 244 | 245 | const fnSig = "decrementCount()"; 246 | const fee = parseH1("1.5"); 247 | 248 | // increment first 249 | let txRes = await c.incrementCount(); 250 | await txRes.wait(); 251 | 252 | const count = await c.count(); 253 | expect(count).to.equal(1n); 254 | 255 | // set a fee on decr count 256 | txRes = await d.proposeFee(fnSig, fee); 257 | await txRes.wait(); 258 | 259 | // approve fee 260 | txRes = await c.approveAllFees(); 261 | 262 | // test event emit 263 | await expect(c.decrementCount({ value: fee })) 264 | .to.emit(c, evt) 265 | .withArgs(addr, dir.DECR, 0n, fee); 266 | 267 | await txRes.wait(); 268 | }); 269 | 270 | it("Should revert if the contract is paused", async function () { 271 | // vars 272 | const t = await loadFixture(setup); 273 | const c = t.simpleStorageContract; 274 | const err = pausableErr("WHEN_NOT_PAUSED"); 275 | 276 | // state check 277 | let isPaused = await c.paused(); 278 | expect(isPaused).to.be.false; 279 | 280 | // pause the contract 281 | let txRes = await c.pause(); 282 | await txRes.wait(); 283 | 284 | // case - contract is paused 285 | await expect(c.decrementCount()).to.be.revertedWith(err); 286 | 287 | // case - contract is not paused 288 | txRes = await c.unpause(); 289 | await txRes.wait(); 290 | await expect(c.decrementCount()).to.not.be.reverted; 291 | }); 292 | }); 293 | 294 | /* Reset Count 295 | ========================================*/ 296 | describe("Reset Count", function () { 297 | it("Should correctly reset the count", async function () { 298 | // vars 299 | const t = await loadFixture(setup); 300 | const d = t.simpleStorageContract.connect(t.developer); 301 | const rounds = 5; 302 | 303 | // set initial count 304 | let count = await d.count(); 305 | expect(count).to.equal(0n); 306 | 307 | for (let i = 0; i < rounds; ++i) { 308 | const txRes = await d.incrementCount(); 309 | await txRes.wait(); 310 | } 311 | 312 | count = await d.count(); 313 | expect(count).to.equal(rounds); 314 | 315 | // test reset 316 | const txRes = await d.resetCount(); 317 | await txRes.wait(); 318 | 319 | count = await d.count(); 320 | expect(count).to.equal(0n); 321 | }); 322 | 323 | it("Should only allow an account with the role DEFAULT_DEV_ROLE to reset the count ", async function () { 324 | // vars 325 | const t = await loadFixture(setup); 326 | const d = t.simpleStorageContract.connect(t.developer); 327 | const u = d.connect(t.accounts[0]); 328 | const err = accessControlErr("MISSING_ROLE"); 329 | 330 | // case - no role 331 | await expect(u.resetCount()).to.be.revertedWith(err); 332 | 333 | // case - has role 334 | await expect(d.resetCount()).to.not.be.reverted; 335 | }); 336 | 337 | it("Should fail to reset the count if the contract is paused", async function () { 338 | // vars 339 | const t = await loadFixture(setup); 340 | const c = t.simpleStorageContract; 341 | const cDev = t.simpleStorageContract.connect(t.developer); 342 | const err = pausableErr("WHEN_NOT_PAUSED"); 343 | 344 | // state check 345 | let isPaused = await c.paused(); 346 | expect(isPaused).to.be.false; 347 | 348 | // pause the contract 349 | let txRes = await c.pause(); 350 | await txRes.wait(); 351 | 352 | // case - contract is paused 353 | await expect(cDev.resetCount()).to.be.revertedWith(err); 354 | 355 | // case - contract is not paused 356 | txRes = await c.unpause(); 357 | await txRes.wait(); 358 | await expect(cDev.incrementCount()).to.not.be.reverted; 359 | }); 360 | }); 361 | 362 | /* Get Count 363 | ========================================*/ 364 | describe("Get Count", function () { 365 | it("Should correctly get the count", async function () { 366 | // vars 367 | const t = await loadFixture(setup); 368 | const c = t.simpleStorageContract; 369 | const rounds = 10; 370 | 371 | // set initial count 372 | let count = await c.count(); 373 | expect(count).to.equal(0n); 374 | 375 | for (let i = 0; i < rounds; ++i) { 376 | const txRes = await c.incrementCount(); 377 | await txRes.wait(); 378 | count = await c.count(); 379 | expect(count).to.equal(i + 1); 380 | } 381 | 382 | count = await c.count(); 383 | expect(count).to.equal(rounds); 384 | }); 385 | }); 386 | }); 387 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2020", 4 | "module": "commonjs", 5 | "esModuleInterop": true, 6 | "forceConsistentCasingInFileNames": true, 7 | "strict": true, 8 | "skipLibCheck": true, 9 | "resolveJsonModule": true, 10 | "paths": { 11 | "@utils": ["./utils/"], 12 | "@utils/*": ["./utils/*"], 13 | "@typechain": ["./typechain-types/"], 14 | "@typechain/*": ["./typechain-types/*"] 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /utils/checkENV.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Function to check all required ENV vars are present and return the missing 3 | * ones. If the length of the returned array is not zero (0), there are missing 4 | * ENV vars and it can be handled by the calling code as desired. 5 | * 6 | * @function checkENV 7 | * @param {string[]} vars List of vars to check 8 | * @returns {string[]} Array of missing environment variables 9 | */ 10 | export function checkENV(vars: string[]): string[] { 11 | const missingEnvVars: string[] = []; 12 | 13 | for (const v of vars) { 14 | if (!process.env[v]) { 15 | missingEnvVars.push(v); 16 | } 17 | } 18 | 19 | return missingEnvVars; 20 | } 21 | -------------------------------------------------------------------------------- /utils/deploy/README.md: -------------------------------------------------------------------------------- 1 | # Deploy Functions 2 | 3 | A selection of functions that handle deploying contracts. 4 | 5 | ## Note 6 | 7 | Only contracts that should be deployed to staging / production will have a 8 | deployment function in this directory. Functions related to the deployment of 9 | dummy / testing contracts should be colocated in their appropriate directories 10 | under `tests/*`. 11 | -------------------------------------------------------------------------------- /utils/deploy/deployWrapper.ts: -------------------------------------------------------------------------------- 1 | /* IMPORT NODE MODULES 2 | ================================================== */ 3 | import type { BaseContract } from "ethers"; 4 | 5 | /* TYPES 6 | ================================================== */ 7 | export type DeploymentData = { 8 | readonly contractName: string; 9 | readonly address: string; 10 | readonly hash: string; 11 | readonly nonce: number; 12 | }; 13 | 14 | type Fn = () => Promise; 15 | 16 | /* WRAPPER 17 | ================================================== */ 18 | /** 19 | * Function that is a wrapper around any function that returns a type that 20 | * extends `BaseContract`. 21 | * Useful for logging the results of a deployment to the console. 22 | * Returns a selection of data about the deployment. 23 | * 24 | * @function d 25 | * @param {string} contractName 26 | * @param {Fn} f 27 | * @returns {DeploymentData} 28 | */ 29 | export async function d( 30 | contractName: string, 31 | f: Fn 32 | ): Promise { 33 | console.log(`Deploying: ${contractName}\n`); 34 | 35 | const c = await f(); 36 | 37 | const address = await c.getAddress(); 38 | let hash = ""; 39 | let nonce = 0; 40 | 41 | const t = c.deploymentTransaction(); 42 | if (t) { 43 | hash = t.hash; 44 | nonce = t.nonce; 45 | } 46 | 47 | console.table([ 48 | { attr: "Hash", val: hash }, 49 | { attr: "Address", val: address }, 50 | { attr: "Nonce", val: nonce }, 51 | ]); 52 | 53 | console.log("\nDeployment Completed\n"); 54 | console.log("========================================\n"); 55 | 56 | return { contractName, address, hash, nonce }; 57 | } 58 | -------------------------------------------------------------------------------- /utils/deploy/fee/deployFeeContract.ts: -------------------------------------------------------------------------------- 1 | /* IMPORT NODE MODULES 2 | ================================================== */ 3 | import { ethers, upgrades } from "hardhat"; 4 | 5 | /* IMPORT TYPES 6 | ================================================== */ 7 | import type { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers"; 8 | import type { BigNumberish } from "ethers"; 9 | import type { FeeContract } from "@typechain"; 10 | 11 | /* TYPES 12 | ================================================== */ 13 | export type FeeInitalizerArgs = { 14 | readonly oracleAddress: string; 15 | readonly channels: string[]; 16 | readonly weights: BigNumberish[]; 17 | readonly haven1Association: string; 18 | readonly networkOperator: string; 19 | readonly deployer: string; 20 | readonly minDevFee: BigNumberish; 21 | readonly maxDevFee: BigNumberish; 22 | readonly asscShare: BigNumberish; 23 | readonly gracePeriod: BigNumberish; 24 | }; 25 | 26 | /* DEPLOY 27 | ================================================== */ 28 | /** 29 | * Deploys the `FeeContract`. 30 | * 31 | * # Error 32 | * Will throw an error if the deployment is not successful. The calling code 33 | * must handle as desired. 34 | * 35 | * @async 36 | * @function deployFeeContract 37 | * @param {FeeInitalizerArgs} args 38 | * @param {HardhatEthersSigner} signer 39 | * @param {number} [confs = 2] - Number of confirmations 40 | * @returns {Promise} Promise that resolves to the Fee Contract 41 | * @throws 42 | */ 43 | export async function deployFeeContract( 44 | args: FeeInitalizerArgs, 45 | signer: HardhatEthersSigner, 46 | confs: number = 2 47 | ): Promise { 48 | const f = await ethers.getContractFactory("FeeContract", signer); 49 | 50 | const c = await upgrades.deployProxy( 51 | f, 52 | [ 53 | args.oracleAddress, 54 | args.channels, 55 | args.weights, 56 | args.haven1Association, 57 | args.networkOperator, 58 | args.deployer, 59 | args.minDevFee, 60 | args.maxDevFee, 61 | args.asscShare, 62 | args.gracePeriod, 63 | ], 64 | { kind: "uups", initializer: "initialize" } 65 | ); 66 | 67 | await c.waitForDeployment(); 68 | 69 | if (confs > 0) { 70 | await c.deploymentTransaction()?.wait(confs); 71 | } 72 | 73 | return c as unknown as FeeContract; 74 | } 75 | -------------------------------------------------------------------------------- /utils/deploy/fee/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./deployFeeContract"; 2 | -------------------------------------------------------------------------------- /utils/deploy/nft-auction/deployNFTAuction.ts: -------------------------------------------------------------------------------- 1 | // @TODO UPDATE THIS WHEN CONTRACT IS DONE 2 | /* IMPORT NODE MODULES 3 | ================================================== */ 4 | import { ethers, upgrades } from "hardhat"; 5 | 6 | /* IMPORT TYPES 7 | ================================================== */ 8 | import type { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers"; 9 | import type { BigNumberish } from "ethers"; 10 | import type { NFTAuction } from "@typechain"; 11 | 12 | /* TYPES 13 | ================================================== */ 14 | type AuctionKindKey = keyof typeof AUCTION_KIND; 15 | type AuctionKindVal = (typeof AUCTION_KIND)[keyof typeof AUCTION_KIND]; 16 | 17 | export type AuctionConfig = { 18 | readonly kind: AuctionKindVal; 19 | readonly length: bigint; 20 | readonly startingBid: bigint; 21 | readonly nft: string; 22 | readonly nftID: bigint; 23 | readonly beneficiary: string; 24 | }; 25 | 26 | export type NFTAuctionInitalizerArgs = { 27 | readonly feeContract: string; 28 | readonly proofOfIdentity: string; 29 | readonly association: string; 30 | readonly developer: string; 31 | readonly feeCollector: string; 32 | readonly fnSigs: string[]; 33 | readonly fnFees: BigNumberish[]; 34 | readonly auctionConfig: AuctionConfig; 35 | }; 36 | 37 | /* CONSTANTS AND UTILS 38 | ================================================== */ 39 | export const AUCTION_KIND = { 40 | RETAIL: 1, 41 | INSTITUTION: 2, 42 | ALL: 3, 43 | } as const; 44 | 45 | /** 46 | * Returns the numeric value associated with an auction kind. 47 | * 48 | * @function getAuctionKind 49 | * @param {AuctionKindKey} auction 50 | * @returns {AuctionKindVal} 51 | */ 52 | export function getAuctionKind(auction: AuctionKindKey): AuctionKindVal { 53 | return AUCTION_KIND[auction]; 54 | } 55 | 56 | /* DEPLOY 57 | ================================================== */ 58 | /** 59 | * Deploys the `NFTAuction` contract. 60 | * 61 | * # Error 62 | * Will throw an error if the deployment is not successful. The calling code 63 | * must handle as desired. 64 | * 65 | * @async 66 | * @function deployNFTAuction 67 | * @param {NFTAuctionInitalizerArgs} args 68 | * @param {HardhatEthersSigner} signer 69 | * @param {number} [confs = 2] - Number of confirmations 70 | * @returns {Promise} Promise that resolves to the `NFTAuction` contract 71 | * @throws 72 | */ 73 | export async function deployNFTAuction( 74 | args: NFTAuctionInitalizerArgs, 75 | signer: HardhatEthersSigner, 76 | confs: number = 2 77 | ): Promise { 78 | const f = await ethers.getContractFactory("NFTAuction", signer); 79 | 80 | const c = await upgrades.deployProxy( 81 | f, 82 | [ 83 | args.feeContract, 84 | args.proofOfIdentity, 85 | args.association, 86 | args.developer, 87 | args.feeCollector, 88 | args.fnSigs, 89 | args.fnFees, 90 | args.auctionConfig, 91 | ], 92 | { kind: "uups", initializer: "initialize" } 93 | ); 94 | 95 | await c.waitForDeployment(); 96 | 97 | if (confs > 0) { 98 | await c.deploymentTransaction()?.wait(confs); 99 | } 100 | 101 | return c as unknown as NFTAuction; 102 | } 103 | -------------------------------------------------------------------------------- /utils/deploy/nft-auction/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./deployNFTAuction"; 2 | -------------------------------------------------------------------------------- /utils/deploy/proof-of-identity/deployProofOfIdentity.ts: -------------------------------------------------------------------------------- 1 | /* IMPORT NODE MODULES 2 | ================================================== */ 3 | import { ethers, upgrades } from "hardhat"; 4 | 5 | /* IMPORT TYPES 6 | ================================================== */ 7 | import type { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers"; 8 | import type { ProofOfIdentity } from "@typechain"; 9 | 10 | /* TYPES 11 | ================================================== */ 12 | /** 13 | * The args required to initialise the Proof of Identity contract. 14 | */ 15 | export type ProofOfIdentityArgs = { 16 | readonly associationAddress: string; 17 | readonly networkOperatorAddress: string; 18 | readonly deployerAddress: string; 19 | readonly permissionsInterfaceAddress: string; 20 | readonly accountManagerAddress: string; 21 | }; 22 | 23 | /** 24 | * The string representation of the supported attribute types. 25 | */ 26 | type AttributeTypeStr = "bool" | "string" | "uint256" | "bytes"; 27 | 28 | /** 29 | * The supported attribute type's id (matches the `SupportedAttributeType`) 30 | * enum from `AttributeUtils.sol` and its string representation. 31 | */ 32 | type SupportedAttributeType = { 33 | id: number; 34 | str: AttributeTypeStr; 35 | }; 36 | 37 | type Attribute = { 38 | readonly id: number; 39 | readonly name: string; 40 | readonly attrType: SupportedAttributeType; 41 | }; 42 | 43 | type AttributeMapping = { [key: string]: Attribute }; 44 | 45 | /* CONSTANTS 46 | ================================================== */ 47 | /** 48 | * Mapping of supported attribute types to the enum value 49 | */ 50 | export const SUPPORTED_ID_ATTRIBUTE_TYPES = { 51 | STRING: { 52 | id: 0, 53 | str: "string", 54 | }, 55 | BOOL: { 56 | id: 1, 57 | str: "bool", 58 | }, 59 | U256: { 60 | id: 2, 61 | str: "uint256", 62 | }, 63 | BYTES: { 64 | id: 3, 65 | str: "bytes", 66 | }, 67 | } as const satisfies Record; 68 | 69 | /** 70 | * Mapping of attributes to their ID and names. 71 | * 72 | * # Important: 73 | * 74 | * Update this mapping as more attributes are released so that the deployment 75 | * scripts stay up to date 76 | */ 77 | export const PROOF_OF_ID_ATTRIBUTES = { 78 | PRIMARY_ID: { 79 | id: 0, 80 | name: "primaryID", 81 | attrType: SUPPORTED_ID_ATTRIBUTE_TYPES.BOOL, 82 | }, 83 | COUNTRY_CODE: { 84 | id: 1, 85 | name: "countryCode", 86 | attrType: SUPPORTED_ID_ATTRIBUTE_TYPES.STRING, 87 | }, 88 | PROOF_OF_LIVELINESS: { 89 | id: 2, 90 | name: "proofOfLiveliness", 91 | attrType: SUPPORTED_ID_ATTRIBUTE_TYPES.BOOL, 92 | }, 93 | USER_TYPE: { 94 | id: 3, 95 | name: "userType", 96 | attrType: SUPPORTED_ID_ATTRIBUTE_TYPES.U256, 97 | }, 98 | COMPETENCY_RATING: { 99 | id: 4, 100 | name: "competencyRating", 101 | attrType: SUPPORTED_ID_ATTRIBUTE_TYPES.U256, 102 | }, 103 | } as const satisfies AttributeMapping; 104 | 105 | /* DEPLOY 106 | ================================================== */ 107 | /** 108 | * Deploys the `Proof of Identity contract`. 109 | * 110 | * # Error 111 | * Will throw an error if the deployment is not successful. The calling code 112 | * must handle as desired. 113 | * 114 | * @async 115 | * @function deployProofOfIdentity 116 | * @param {ProofOfIdentityArgs} args 117 | * @param {HardhatEthersSigner} signer 118 | * @param {number} [confs = 2] - Number of confirmations 119 | * @returns {Promise} Promise that resolves to the `ProofOfIdentity` contract. 120 | * @throws 121 | */ 122 | export async function deployProofOfIdentity( 123 | args: ProofOfIdentityArgs, 124 | signer: HardhatEthersSigner, 125 | confs: number = 2 126 | ): Promise { 127 | // deploy 128 | const f = await ethers.getContractFactory("ProofOfIdentity", signer); 129 | 130 | const c = (await upgrades.deployProxy( 131 | f, 132 | [ 133 | args.associationAddress, 134 | args.networkOperatorAddress, 135 | args.deployerAddress, 136 | args.permissionsInterfaceAddress, 137 | args.accountManagerAddress, 138 | ], 139 | { kind: "uups", initializer: "initialize" } 140 | )) as unknown as ProofOfIdentity; 141 | 142 | await c.waitForDeployment(); 143 | 144 | if (confs > 0) { 145 | await c.deploymentTransaction()?.wait(confs); 146 | } 147 | 148 | // set all attribute names and types 149 | for (const { id, name, attrType } of Object.values( 150 | PROOF_OF_ID_ATTRIBUTES 151 | )) { 152 | let txRes = await c.setAttributeName(id, name); 153 | await txRes.wait(); 154 | 155 | txRes = await c.setAttributeType(id, attrType.id); 156 | await txRes.wait(); 157 | } 158 | 159 | return c; 160 | } 161 | -------------------------------------------------------------------------------- /utils/deploy/proof-of-identity/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./deployProofOfIdentity"; 2 | -------------------------------------------------------------------------------- /utils/deploy/simple-storage/deploySimpleStorage.ts: -------------------------------------------------------------------------------- 1 | /* IMPORT NODE MODULES 2 | ================================================== */ 3 | import { ethers, upgrades } from "hardhat"; 4 | 5 | /* IMPORT TYPES 6 | ================================================== */ 7 | import type { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers"; 8 | import type { BigNumberish } from "ethers"; 9 | import type { SimpleStorage } from "@typechain"; 10 | 11 | /* TYPES 12 | ================================================== */ 13 | export type SimpleStorageInitalizerArgs = { 14 | readonly feeContract: string; 15 | readonly association: string; 16 | readonly developer: string; 17 | readonly feeCollector: string; 18 | readonly fnSigs: string[]; 19 | readonly fnFees: BigNumberish[]; 20 | }; 21 | 22 | /* DEPLOY 23 | ================================================== */ 24 | /** 25 | * Deploys the `SimpleStorage` contract. 26 | * 27 | * # Error 28 | * Will throw an error if the deployment is not successful. The calling code 29 | * must handle as desired. 30 | * 31 | * @async 32 | * @function deploySimpleStorage 33 | * @param {SimpleStorageInitalizerArgs} args 34 | * @param {HardhatEthersSigner} signer 35 | * @param {number} [confs = 2] - Number of confirmations 36 | * @returns {Promise} Promise that resolves to the `SimpleStorage` contract 37 | * @throws 38 | */ 39 | export async function deploySimpleStorage( 40 | args: SimpleStorageInitalizerArgs, 41 | signer: HardhatEthersSigner, 42 | confs: number = 2 43 | ): Promise { 44 | const f = await ethers.getContractFactory("SimpleStorage", signer); 45 | 46 | const c = await upgrades.deployProxy( 47 | f, 48 | [ 49 | args.feeContract, 50 | args.association, 51 | args.developer, 52 | args.feeCollector, 53 | args.fnSigs, 54 | args.fnFees, 55 | ], 56 | { kind: "uups", initializer: "initialize" } 57 | ); 58 | 59 | await c.waitForDeployment(); 60 | 61 | if (confs > 0) { 62 | await c.deploymentTransaction()?.wait(confs); 63 | } 64 | 65 | return c as unknown as SimpleStorage; 66 | } 67 | -------------------------------------------------------------------------------- /utils/deploy/simple-storage/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./deploySimpleStorage"; 2 | -------------------------------------------------------------------------------- /utils/deploy/upgrade.ts: -------------------------------------------------------------------------------- 1 | /* IMPORT NODE MODULES 2 | ================================================== */ 3 | import { upgrades } from "hardhat"; 4 | 5 | /* IMPORT TYPES 6 | ================================================== */ 7 | import type { BaseContract, ContractFactory } from "ethers"; 8 | import type { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers"; 9 | 10 | /* UPGRADE PROXY 11 | ================================================== */ 12 | /** 13 | * Upgrades the deployed instance of a contract to a new version. 14 | * 15 | * # Error 16 | * Will throw an error if the upgrade is not successful. The calling code 17 | * must handle as desired. 18 | * 19 | * @async 20 | * @function upgrade 21 | * @param {string} deployedContractAddress 22 | * @param {ContractFactory} newImpl 23 | * @returns {Promise} Promise that resolves to `T` 24 | * @throws 25 | */ 26 | export async function upgrade( 27 | deployedContractAddress: string, 28 | newImpl: ContractFactory, 29 | signer?: HardhatEthersSigner 30 | ): Promise { 31 | let f = newImpl; 32 | 33 | if (signer) { 34 | f = newImpl.connect(signer); 35 | } 36 | 37 | const c = await upgrades.upgradeProxy(deployedContractAddress, f, { 38 | kind: "uups", 39 | }); 40 | 41 | return (await c.waitForDeployment()) as unknown as T; 42 | } 43 | -------------------------------------------------------------------------------- /utils/deploy/verifyWrapper.ts: -------------------------------------------------------------------------------- 1 | /* IMPORT NODE MODULES 2 | ================================================== */ 3 | import { type RunTaskFunction } from "hardhat/types"; 4 | 5 | /* UTILS 6 | ================================================== */ 7 | export function createVerifyWrapper(run: RunTaskFunction) { 8 | return async function ( 9 | name: string, 10 | address: string, 11 | constructorArguments: unknown[] 12 | ) { 13 | try { 14 | console.log(`Verifying: ${name}\n`); 15 | 16 | await run("verify:verify", { address, constructorArguments }); 17 | 18 | console.log("Verification Completed"); 19 | console.log("Status: Success\n"); 20 | console.log("========================================\n"); 21 | } catch (e) { 22 | console.log("Verification Failed\n"); 23 | console.log("========================================\n"); 24 | } 25 | }; 26 | } 27 | -------------------------------------------------------------------------------- /utils/dummyAddresses.ts: -------------------------------------------------------------------------------- 1 | /* CONSTANTS 2 | ================================================== */ 3 | const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000"; 4 | 5 | /* UTILS 6 | ================================================== */ 7 | /** 8 | * Generates a dummy address, padded with zeros until the suffix. 9 | * 10 | * @function generateDummyAddress 11 | * @param {string} suffix 12 | * @returns {string} 13 | */ 14 | export function generateDummyAddress(suffix: string): string { 15 | return ZERO_ADDRESS.slice(0, -suffix.length) + suffix; 16 | } 17 | 18 | /** 19 | * Generates an array of dummy address. 20 | * 21 | * @function generateDummyAddresses 22 | * @param {number} n 23 | * @returns {string[]} 24 | */ 25 | export function generateDummyAddresses(n: number): string[] { 26 | const out: string[] = []; 27 | 28 | for (let i = 0; i < n; ++i) { 29 | out.push(generateDummyAddress(`${i + 1}`)); 30 | } 31 | 32 | return out; 33 | } 34 | -------------------------------------------------------------------------------- /utils/fnSelector.ts: -------------------------------------------------------------------------------- 1 | import { ethers } from "ethers"; 2 | 3 | /** 4 | * Returns the function selector for a given function signature. 5 | * 6 | * @function fnSelector 7 | * @param {string} sig 8 | * @returns {string} The function selector 9 | * 10 | */ 11 | export function fnSelector(sig: string): string { 12 | // Function selector is the first four (4) bytes the keccak256 hash of the 13 | // bytes representation of the function signature. 14 | // E.g., bytes4(keccak256(bytes("transfer(address,uint256)"))) 15 | // 16 | // Each hex digit is four bits. So 0x + first four bytes is the first ten 17 | // chars. 0x|d0|9d|e0|8a 18 | return ethers.id(sig).substring(0, 10); 19 | } 20 | -------------------------------------------------------------------------------- /utils/json.ts: -------------------------------------------------------------------------------- 1 | /* IMPORT NODE MODULES 2 | ================================================== */ 3 | import * as fs from "fs"; 4 | import * as path from "path"; 5 | 6 | /* TYPES 7 | ================================================== */ 8 | type Obj = Record; 9 | 10 | /* WRITE 11 | ================================================== */ 12 | /** 13 | * Basic implementation of a JSON file writer. It will prepend the filename 14 | * with the current timestamp to avoid clashes. Cannot be used to append data. 15 | * 16 | * If the dirname does not exist, this function will create it. 17 | * 18 | * @function writeJSON 19 | * @param {string} filePath - The relative file path. Must end in ".json" 20 | * @param {Obj} content - The content to write. 21 | * @returns {boolean} True is success, false otherwise. 22 | */ 23 | export function writeJSON(filePath: string, content: Obj): boolean { 24 | if (!filePath.endsWith(".json")) { 25 | return false; 26 | } 27 | 28 | const t = Date.now(); 29 | 30 | let p = path.join(__dirname, filePath); 31 | let f = `${t}_${path.basename(p)}`; 32 | const dir = path.dirname(p); 33 | 34 | p = path.join(dir, f); 35 | 36 | const exists = fs.existsSync(dir); 37 | if (!exists) { 38 | fs.mkdirSync(dir, { recursive: true }); 39 | } 40 | 41 | let d = ""; 42 | 43 | try { 44 | d = JSON.stringify(content, null, 4); 45 | } catch (_) { 46 | return false; 47 | } 48 | 49 | try { 50 | fs.writeFileSync(p, d, "utf8"); 51 | } catch (_) { 52 | return false; 53 | } 54 | 55 | return true; 56 | } 57 | -------------------------------------------------------------------------------- /utils/random.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Generates a random number starting at `from` until, but __not including__ 3 | * `to`. 4 | * 5 | * # Error 6 | * Will throw an error if `to` is less than or equal to `from`. 7 | * 8 | * @function randomNumber 9 | * @param {number} from 10 | * @param {number} to 11 | * @returns {number} 12 | * @throws 13 | */ 14 | export function randomNumber(from: number, to: number): number { 15 | if (to <= from) { 16 | throw new Error( 17 | `Invalid arguments. Expected "to" to be greater than "from", got from = ${from}. to = ${to}.` 18 | ); 19 | } 20 | const randomNumber = Math.random() * (to - from) + from; 21 | return Math.floor(randomNumber); 22 | } 23 | -------------------------------------------------------------------------------- /utils/time.ts: -------------------------------------------------------------------------------- 1 | /* TYPES 2 | ================================================== */ 3 | type TimeFrame = "years" | "months" | "days" | "hours" | "minutes" | "seconds"; 4 | type Output = "ms" | "sec"; 5 | 6 | /* FUNCTIONS 7 | ================================================== */ 8 | /** 9 | * Takes in a timestamp in ms and adds an amount of time to it. Returns a new 10 | * timestamp in ms by default, however can also return seconds. 11 | * 12 | * # Errors 13 | * Will throw an error `Invalid time frame` if an invalid time frame is provided. 14 | * 15 | * @function addTime 16 | * 17 | * @param {number} ts The starting timestamp in ms 18 | * @param {number} amount The amount of time to add 19 | * @param {TimeFrame} timeFrame The time frame of the amount to add. 20 | * @param {Output} [output] The output type: ms or sec. Defaults ms. 21 | * @returns {number} The new timestamp. 22 | * 23 | * @example 24 | * const newTime = addTime(Date.now(), 1, "years") 25 | * const newTime = addTime(Date.now(), 1, "years", "ms") 26 | * const newTime = addTime(Date.now(), 1, "years", "sec") 27 | */ 28 | export function addTime( 29 | ts: number, 30 | amount: number, 31 | timeFrame: TimeFrame, 32 | output: Output = "ms" 33 | ): number { 34 | const date = new Date(ts); 35 | 36 | switch (timeFrame) { 37 | case "years": 38 | date.setFullYear(date.getFullYear() + amount); 39 | break; 40 | case "months": 41 | date.setMonth(date.getMonth() + amount); 42 | break; 43 | case "days": 44 | date.setDate(date.getDate() + amount); 45 | break; 46 | case "hours": 47 | date.setHours(date.getHours() + amount); 48 | break; 49 | case "minutes": 50 | date.setMinutes(date.getMinutes() + amount); 51 | break; 52 | case "seconds": 53 | date.setSeconds(date.getSeconds() + amount); 54 | break; 55 | default: 56 | throw new Error("Invalid time frame"); 57 | } 58 | 59 | if (output === "sec") return msToSec(date.getTime()); 60 | return date.getTime(); 61 | } 62 | 63 | /** 64 | * Takes in a ms timestamp and converts it to seconds. If the conversion results 65 | * in a fractional number, the time will be __rounded up__ to the nearest second. 66 | * 67 | * @function msToSec 68 | * 69 | * @param {number} ts The ms timestamp to convert 70 | * @returns {number} The timestamp, rounded up to the nearest second. 71 | */ 72 | export function msToSec(ts: number): number { 73 | // convert to seconds, remove decimals and add one second to round up 74 | return Number((ts / 1000).toString().split(".")[0]) + 1; 75 | } 76 | 77 | /** 78 | * Takes in a timestamp in seconds and returns the start of the next day in UTC. 79 | * 80 | * @function getStartOfNextDayUTC 81 | * 82 | * @param {number} ts The timestamp in seconds 83 | * @returns {number} The start of the next day in seconds 84 | */ 85 | export function getStartOfNextDayUTC(ts: number): number { 86 | // Create a Date object from the timestamp (assumed to be in seconds) 87 | const date = new Date(ts * 1000); 88 | 89 | // Convert to UTC and set to start of the current day 90 | date.setUTCHours(0, 0, 0, 0); 91 | 92 | // Add one day to get the start of the next day 93 | date.setUTCDate(date.getUTCDate() + 1); 94 | 95 | // Convert back to a timestamp in seconds and return 96 | return Math.floor(date.getTime() / 1000); 97 | } 98 | -------------------------------------------------------------------------------- /utils/token.ts: -------------------------------------------------------------------------------- 1 | /* IMPORT NODE MODULES 2 | ================================================== */ 3 | import { 4 | type BigNumberish, 5 | type AddressLike, 6 | type TransactionReceipt, 7 | } from "ethers"; 8 | import { ethers } from "hardhat"; 9 | import type { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers"; 10 | 11 | /* UTILS 12 | ================================================== */ 13 | /** 14 | * Wrapper around `ethers.formatUnits` that converts a value into decimal 15 | * string, with the correct H1 decimal places. 16 | * 17 | * @function formatH1 18 | * @param {BigNumberish} amount 19 | * @returns {string} 20 | */ 21 | export function formatH1(amount: BigNumberish): string { 22 | return ethers.formatUnits(amount, 18); 23 | } 24 | 25 | /** 26 | * Wrapper around `ethers.parseUnits` that converts a decimal string to a 27 | * `bigint`, parsed to 18 decimals. 28 | * 29 | * @function parseH1 30 | * @param {string} amount 31 | * @returns {bigint} 32 | */ 33 | export function parseH1(amount: string): bigint { 34 | return ethers.parseUnits(amount, 18); 35 | } 36 | 37 | /** 38 | * Sends H1 from the `from` signer to the `to` address. 39 | * The `amount` should be a plain dec string, e.g., "100". 40 | * 41 | * # Errors 42 | * This function may error. It is up to the calling code to handle as desired. 43 | * 44 | * @async 45 | * @function sendH1 46 | * @param {HardhatEthersSigner} from 47 | * @param {AddressLike} to 48 | * @param {string} amount 49 | * @param {string} [unencodedData] 50 | * @returns {Promise} 51 | * @throws 52 | */ 53 | export async function sendH1( 54 | from: HardhatEthersSigner, 55 | to: AddressLike, 56 | amount: string, 57 | unencodedData?: string 58 | ): Promise { 59 | const value = parseH1(amount); 60 | 61 | const data = unencodedData && ethers.encodeBytes32String(unencodedData); 62 | 63 | const tx = await from.sendTransaction({ to, value, data }); 64 | return await tx.wait(); 65 | } 66 | 67 | /** 68 | * Sends H1 from the `from` signer to the `to` address. 69 | * The `amount` should be a bigint, pre-parsed. 70 | * 71 | * # Errors 72 | * This function may error. It is up to the calling code to handle as desired. 73 | * 74 | * @async 75 | * @function sendH1Bigint 76 | * @param {HardhatEthersSigner} from 77 | * @param {AddressLike} to 78 | * @param {bigint} amount 79 | * @param {string} [unencodedData] 80 | * @returns {Promise} 81 | * @throws 82 | */ 83 | export async function sendH1Bigint( 84 | from: HardhatEthersSigner, 85 | to: AddressLike, 86 | amount: bigint, 87 | unencodedData?: string 88 | ): Promise { 89 | const data = unencodedData && ethers.encodeBytes32String(unencodedData); 90 | 91 | const tx = await from.sendTransaction({ to, value: amount, data }); 92 | return await tx.wait(); 93 | } 94 | 95 | /** 96 | * Wrapper around `signer.provider.getBalance`. 97 | * 98 | * # Errors 99 | * This function may error. It is up to the calling code to handle as desired. 100 | * 101 | * @async 102 | * @function getH1Balance 103 | * @param {AddressLike} address 104 | * @param {HardhatEthersSigner} signer 105 | * @returns {Promise} 106 | * @throws 107 | */ 108 | export async function getH1Balance( 109 | address: AddressLike, 110 | signer?: HardhatEthersSigner 111 | ): Promise { 112 | signer ||= (await ethers.getSigners())[0]; 113 | return await signer.provider.getBalance(address); 114 | } 115 | -------------------------------------------------------------------------------- /utils/transaction.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ContractTransactionResponse, 3 | type ContractTransactionReceipt, 4 | } from "ethers"; 5 | 6 | /** 7 | * Return the total gas cost of a transaction. Will return zero if the `txRec` 8 | * is null. 9 | * 10 | * @funtion totalGas 11 | * @param {ContractTransactionReceipt | null} txRec 12 | * @returns {bigint} 13 | */ 14 | export function totalGas(txRec: ContractTransactionReceipt | null): bigint { 15 | return (txRec?.gasUsed ?? 0n) * (txRec?.gasPrice ?? 0n); 16 | } 17 | 18 | /** 19 | * Get the timestamp of a transaction. Will return 0 if the timestamp was not 20 | * found. 21 | * 22 | * @async 23 | * @function tsFromTxRec 24 | * @param {ContractTransactionReceipt | null} txRec 25 | * @returns {Promise} 26 | */ 27 | export async function tsFromTxRec( 28 | txRec: ContractTransactionReceipt | null 29 | ): Promise { 30 | return (await txRec?.getBlock())?.timestamp ?? 0; 31 | } 32 | 33 | /** 34 | * Get the timestamp of a transaction. Will return 0 if the timestamp was not 35 | * found. 36 | * 37 | * @async 38 | * @function tsFromTxRes 39 | * @param {ContractTransactionResponse | null} txRes 40 | * @returns {Promise} 41 | */ 42 | export async function tsFromTxRes( 43 | txRes: ContractTransactionResponse | null 44 | ): Promise { 45 | return (await txRes?.getBlock())?.timestamp ?? 0; 46 | } 47 | -------------------------------------------------------------------------------- /vendor-docs/h1-developed-application/components/Errors.md: -------------------------------------------------------------------------------- 1 | # Solidity API 2 | 3 | ## H1Developed\_\_FeeTransferFailed 4 | 5 | ```solidity 6 | error H1Developed__FeeTransferFailed(address to, uint256 amount) 7 | ``` 8 | 9 | This error is thrown when a fee transfer failed. 10 | 11 | ### Parameters 12 | 13 | | Name | Type | Description | 14 | | ------ | ------- | ------------------------------ | 15 | | to | address | The recipient address. | 16 | | amount | uint256 | The amount of the transaction. | 17 | 18 | ## H1Developed\_\_InvalidAddress 19 | 20 | ```solidity 21 | error H1Developed__InvalidAddress(address provided, string source) 22 | ``` 23 | 24 | This error is thrown when an address validation has failed. 25 | 26 | ### Parameters 27 | 28 | | Name | Type | Description | 29 | | -------- | ------- | ----------------------------- | 30 | | provided | address | The address provided. | 31 | | source | string | The origination of the error. | 32 | 33 | ## H1Developed\_\_InsufficientFunds 34 | 35 | ```solidity 36 | error H1Developed__InsufficientFunds(uint256 fundsInContract, uint256 currentFee) 37 | ``` 38 | 39 | This error is thrown when there are insufficient funds send to 40 | pay the fee. 41 | 42 | ### Parameters 43 | 44 | | Name | Type | Description | 45 | | --------------- | ------- | ----------------------------------- | 46 | | fundsInContract | uint256 | The current balance of the contract | 47 | | currentFee | uint256 | The current fee amount | 48 | 49 | ## H1Developed\_\_ArrayLengthMismatch 50 | 51 | ```solidity 52 | error H1Developed__ArrayLengthMismatch(uint256 a, uint256 b) 53 | ``` 54 | 55 | _Error to throw when the length of n arrays are required to be equal 56 | and are not._ 57 | 58 | ### Parameters 59 | 60 | | Name | Type | Description | 61 | | ---- | ------- | ------------------------------- | 62 | | a | uint256 | The length of the first array. | 63 | | b | uint256 | The length of the second array. | 64 | 65 | ## H1Developed\_\_ArrayLengthZero 66 | 67 | ```solidity 68 | error H1Developed__ArrayLengthZero() 69 | ``` 70 | 71 | _Error to throw when the length of an array must be greater than zero 72 | and it is not._ 73 | 74 | ## H1Developed\_\_IndexOutOfBounds 75 | 76 | ```solidity 77 | error H1Developed__IndexOutOfBounds(uint256 idx, uint256 maxIdx) 78 | ``` 79 | 80 | _Error to throw when a user tries to access an invalid index of an array. 81 | param idx The index that was accessed. 82 | param maxIdx The maximum index that can be accessed on the array._ 83 | 84 | ## H1Developed\_\_InvalidFnSignature 85 | 86 | ```solidity 87 | error H1Developed__InvalidFnSignature(string sig) 88 | ``` 89 | 90 | _Error to throw when an invalid function signature has been provided._ 91 | 92 | ### Parameters 93 | 94 | | Name | Type | Description | 95 | | ---- | ------ | ----------------------- | 96 | | sig | string | The provided signature. | 97 | 98 | ## H1Developed\_\_InvalidFeeAmount 99 | 100 | ```solidity 101 | error H1Developed__InvalidFeeAmount(uint256 fee) 102 | ``` 103 | 104 | _Error to throw when an attempt add a fee is made and it falls 105 | outside the constraints set by the `FeeContract`._ 106 | 107 | ### Parameters 108 | 109 | | Name | Type | Description | 110 | | ---- | ------- | ----------------------- | 111 | | fee | uint256 | The invalid fee amount. | 112 | -------------------------------------------------------------------------------- /vendor-docs/h1-developed-application/components/H1DevelopedAccessControl.md: -------------------------------------------------------------------------------- 1 | # Solidity API 2 | 3 | ## H1DevelopedAccessControl 4 | 5 | _Contains the various roles to be used throughout the 6 | `H1DevelopedApplication` contracts._ 7 | 8 | _Note that inheriting contracts may also wish to add extra roles. As 9 | such, the last contract in the inheritance chain should call 10 | `__AccessControl_init()`._ 11 | 12 | _This contract contains only constants which are inlined on compilation. 13 | Despite this, a gap has still been provided to cater for future upgrades._ 14 | 15 | ### PAUSER_ROLE 16 | 17 | ```solidity 18 | bytes32 PAUSER_ROLE 19 | ``` 20 | 21 | _The Pauser role. Has the ability to pause the contract._ 22 | 23 | ### UNPAUSER_ROLE 24 | 25 | ```solidity 26 | bytes32 UNPAUSER_ROLE 27 | ``` 28 | 29 | _The Unpauser role. Has the ability to unpause the contract._ 30 | 31 | ### DEV_ADMIN_ROLE 32 | 33 | ```solidity 34 | bytes32 DEV_ADMIN_ROLE 35 | ``` 36 | 37 | _The Dev Admin role. For use in the inheriting contract._ 38 | 39 | ### \_\_H1DevelopedAccessControl_init 40 | 41 | ```solidity 42 | function __H1DevelopedAccessControl_init(address association, address developer) internal 43 | ``` 44 | 45 | Initializes the `H1DevelopedAccessControl` contract. 46 | 47 | _May revert with `H1Developed__InvalidAddress`._ 48 | 49 | #### Parameters 50 | 51 | | Name | Type | Description | 52 | | ----------- | ------- | ------------------------------------------------------------------------------------------------------------------------ | 53 | | association | address | The address of the Haven1 Association. Will be assigned roles: `DEFAULT_ADMIN_ROLE`, `PAUSER_ROLE`, and `UNPAUSER_ROLE`. | 54 | | developer | address | The address of the contract's developer. Will be assigned roles: `PAUSER_ROLE` and `DEV_ADMIN_ROLE`. | 55 | 56 | ### \_\_H1DevelopedAccessControl_init_unchained 57 | 58 | ```solidity 59 | function __H1DevelopedAccessControl_init_unchained(address association, address developer) internal 60 | ``` 61 | 62 | _see {H1DevelopedRoles-**H1DevelopedAccessControl**init}_ 63 | -------------------------------------------------------------------------------- /vendor-docs/h1-developed-application/components/H1DevelopedPausable.md: -------------------------------------------------------------------------------- 1 | # Solidity API 2 | 3 | ## H1DevelopedPausable 4 | 5 | Wraps Open Zeppelin's `PausableUpgradeable`. 6 | Contains the protected public `pause` and `unpause` functions. 7 | 8 | _This contract does not contain any state variables. Even so, a very 9 | small gap has been provided to accommodate the addition of state variables 10 | should the need arise._ 11 | 12 | ### \_\_H1DevelopedPausable_init 13 | 14 | ```solidity 15 | function __H1DevelopedPausable_init() internal 16 | ``` 17 | 18 | Initializes the `H1DevelopedPausable` contract. 19 | 20 | ### \_\_H1DevelopedPausable_init_unchained 21 | 22 | ```solidity 23 | function __H1DevelopedPausable_init_unchained() internal 24 | ``` 25 | 26 | _see {H1DevelopedPausable-\_\_H1DevelopedPausable_init}_ 27 | 28 | ### pause 29 | 30 | ```solidity 31 | function pause() public 32 | ``` 33 | 34 | Pauses the contract. 35 | 36 | _Only callable by an account with the role: `PAUSER_ROLE`._ 37 | 38 | - May revert with: /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ 39 | - May emit a `Paused` event. 40 | 41 | ### unpause 42 | 43 | ```solidity 44 | function unpause() public 45 | ``` 46 | 47 | Unpauses the contract. 48 | 49 | _Only callable by an account with the role: `UNPAUSER_ROLE`._ 50 | 51 | - May revert with: /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ 52 | - May emit an `Unpaused` event. 53 | -------------------------------------------------------------------------------- /vendor-docs/h1-developed-application/components/H1DevelopedUtils.md: -------------------------------------------------------------------------------- 1 | # Solidity API 2 | 3 | ## FeeProposal 4 | 5 | ```solidity 6 | struct FeeProposal { 7 | uint256 fee; 8 | bytes fnSig; 9 | } 10 | ``` 11 | 12 | ## FeeProposalFormatted 13 | 14 | ```solidity 15 | struct FeeProposalFormatted { 16 | uint256 fee; 17 | string fnSig; 18 | } 19 | ``` 20 | 21 | ## Validate 22 | 23 | _Library that consists of validation functions. Functions suffixed 24 | with `exn` with throw expections if the given condition(s) are not met._ 25 | 26 | ### feeExn 27 | 28 | ```solidity 29 | function feeExn(uint256 fee, uint256 min, uint256 max) internal pure 30 | ``` 31 | 32 | Helper function to test the validity of a given fee against a 33 | given min and max constraint. 34 | 35 | _Will revert with `H1Developed__InvalidFeeAmount` if the fee is 36 | less than the min or greater than the max._ 37 | 38 | #### Parameters 39 | 40 | | Name | Type | Description | 41 | | ---- | ------- | -------------------- | 42 | | fee | uint256 | The fee to validate. | 43 | | min | uint256 | The minimum fee. | 44 | | max | uint256 | The maximum fee. | 45 | 46 | ### addrExn 47 | 48 | ```solidity 49 | function addrExn(address addr, string source) internal pure 50 | ``` 51 | 52 | Helper function to test the validity of a given address. 53 | 54 | _Will revert with `H1Developed__InvalidAddress` if the address is 55 | invalid._ 56 | 57 | #### Parameters 58 | 59 | | Name | Type | Description | 60 | | ------ | ------- | -------------------------------- | 61 | | addr | address | The address to check | 62 | | source | string | The source of the address check. | 63 | 64 | ## FnSig 65 | 66 | _Library that provides helpers for function signatures stored as bytes._ 67 | 68 | ### toString 69 | 70 | ```solidity 71 | function toString(bytes b) internal pure returns (string) 72 | ``` 73 | 74 | Decodes a given byte array `b` to a string. If the byte array 75 | has no length, an empty string `""` is returned. 76 | 77 | #### Parameters 78 | 79 | | Name | Type | Description | 80 | | ---- | ----- | ------------------------- | 81 | | b | bytes | The byte array to decode. | 82 | 83 | ### toFnSelector 84 | 85 | ```solidity 86 | function toFnSelector(bytes b) internal pure returns (bytes4) 87 | ``` 88 | 89 | Converts a given byte array to a function signature. 90 | If the byte array has no length, an empty bytes4 array is returned. 91 | 92 | _The provided byte array is expected to be a function signature. 93 | E.g., `abi.encode("transfer(address,uint256)");``_ 94 | 95 | #### Parameters 96 | 97 | | Name | Type | Description | 98 | | ---- | ----- | ------------------------- | 99 | | b | bytes | The byte array to decode. | 100 | -------------------------------------------------------------------------------- /vendor-docs/proof-of-identity/libraries/AttributeUtils.md: -------------------------------------------------------------------------------- 1 | # Solidity API 2 | 3 | ## Attribute 4 | 5 | ```solidity 6 | struct Attribute { 7 | uint256 expiry; 8 | uint256 updatedAt; 9 | bytes data; 10 | } 11 | ``` 12 | 13 | ## SupportedAttributeType 14 | 15 | ```solidity 16 | enum SupportedAttributeType { 17 | STRING, 18 | BOOL, 19 | U256, 20 | BYTES 21 | } 22 | ``` 23 | 24 | ## AttributeUtils 25 | 26 | _Library that contains the `Attribute` struct and associated methods._ 27 | 28 | ### setAttribute 29 | 30 | ```solidity 31 | function setAttribute(struct Attribute attr, uint256 expiry, uint256 updatedAt, bytes data) internal 32 | ``` 33 | 34 | Sets an attribute for the first time. 35 | 36 | #### Parameters 37 | 38 | | Name | Type | Description | 39 | | --------- | ---------------- | --------------------------------------------------------- | 40 | | attr | struct Attribute | The attribute to set. | 41 | | expiry | uint256 | The timestamp of expiry of the attribute. | 42 | | updatedAt | uint256 | The timestamp of the last time the attribute was updated. | 43 | | data | bytes | The attribute data to set in bytes. | 44 | 45 | ### toString 46 | 47 | ```solidity 48 | function toString(enum SupportedAttributeType attrType) internal pure returns (string) 49 | ``` 50 | 51 | Returns the string name of the `SupportedAttributeType`. 52 | 53 | #### Parameters 54 | 55 | | Name | Type | Description | 56 | | -------- | --------------------------- | -------------------------------------- | 57 | | attrType | enum SupportedAttributeType | The supported attribute type to check. | 58 | 59 | #### Return Values 60 | 61 | | Name | Type | Description | 62 | | ---- | ------ | ------------------------------------------------ | 63 | | [0] | string | The string name of the requested attribute type. | 64 | -------------------------------------------------------------------------------- /vendor-docs/proof-of-identity/libraries/BytesConversion.md: -------------------------------------------------------------------------------- 1 | # Solidity API 2 | 3 | ## BytesConversion 4 | 5 | _Library to decode bytes to a given type._ 6 | 7 | ### toString 8 | 9 | ```solidity 10 | function toString(bytes b) internal pure returns (string) 11 | ``` 12 | 13 | ### toU256 14 | 15 | ```solidity 16 | function toU256(bytes b) internal pure returns (uint256) 17 | ``` 18 | 19 | ### toBool 20 | 21 | ```solidity 22 | function toBool(bytes b) internal pure returns (bool) 23 | ``` 24 | 25 | ### toBytes 26 | 27 | ```solidity 28 | function toBytes(bytes b) internal pure returns (bytes) 29 | ``` 30 | --------------------------------------------------------------------------------