├── .editorconfig ├── .env.example ├── .gitignore ├── .gitmodules ├── .prettierignore ├── .prettierrc.yml ├── .python-version ├── .solhint.json ├── .vscode └── settings.json ├── 4naly3er-report.md ├── LICENSE.md ├── README-sponsor.md ├── README.md ├── bun.lockb ├── discord-export ├── Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html ├── Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files │ ├── 0-EB806.png │ ├── 01865d1bed0a1da3a253f05b3e79cb07-CB286.png │ ├── 0473983eb60155e816c3a2e070dcd889-A67D3.png │ ├── 060ef80acbf6b2cb6cc9d7ad25665daf-4D4FE.png │ ├── 0c3222f16c36405c5c7024593d1b7225-7BCE7.png │ ├── 0c70f1a9b0c75c0499ba9960169dc468-F07E0.png │ ├── 0cf7719f20f0aaed168c3f31a3f0bceb-8014C.png │ ├── 0e7bd30c9edad6eb8e0a7a890f666140-A5C72.png │ ├── 1-B2132.png │ ├── 1080667809002037320-8957D.png │ ├── 1083068770823721071-EE4F6.png │ ├── 1096768259770028062-DDF70.png │ ├── 1133784983211888712-1A533.png │ ├── 1194958214408699974-36E12.png │ ├── 1233479995633172584-BBE31.png │ ├── 15cdaa10c5644d913a1ab1f9f06288c1-8A82B.png │ ├── 16947216bb5508b0ec8cb666ce9c68e7-7B124.png │ ├── 1f197-BB9F1.svg │ ├── 1f31f-1B968.svg │ ├── 1f340-360EA.svg │ ├── 1f389-5C738.svg │ ├── 1f3c1-445DC.svg │ ├── 1f43a-EB486.svg │ ├── 1f440-6C64D.svg │ ├── 1f44b-8A059.svg │ ├── 1f44d-27259.svg │ ├── 1f44f-3D381.svg │ ├── 1f499-93A1C.svg │ ├── 1f49c-71A75.svg │ ├── 1f4ac-4B485.svg │ ├── 1f4b8-E3468.svg │ ├── 1f4c6-44E30.svg │ ├── 1f50d-195C0.svg │ ├── 1f525-8FE4F.svg │ ├── 1f602-168C5.svg │ ├── 1f605-42B43.svg │ ├── 1f614-BB5EE.svg │ ├── 1f638-02313.svg │ ├── 1f642-83E8A.svg │ ├── 1f643-F8BDC.svg │ ├── 1f648-B83C5.svg │ ├── 1f64c-7C820.svg │ ├── 1f64f-1f3fc-34E32.svg │ ├── 1f64f-22B8D.svg │ ├── 1f680-A35CE.svg │ ├── 1f6a8-A8AB3.svg │ ├── 1f6ab-A6294.svg │ ├── 1f911-F346C.svg │ ├── 1f916-AD810.svg │ ├── 1f91d-5A0F2.svg │ ├── 1f92a-55954.svg │ ├── 1f972-F415D.svg │ ├── 1f973-88B39.svg │ ├── 1f97a-1F57B.svg │ ├── 1fa75-60476.svg │ ├── 1fae1-B19DE.svg │ ├── 2-ADBB4.png │ ├── 237f8315bfd31d0464b62371d56423b1-6F4B9.png │ ├── 258efb44a5e3f3ccd0db6c4b11462ff2-08900.png │ ├── 26a0-D845B.svg │ ├── 2705-0589F.svg │ ├── 2764-A3D25.svg │ ├── 27e40b0499e32304fe17900046221e11-1AE74.png │ ├── 2a9faff195fe333526cfe6ae6fce1420-49B98.png │ ├── 3-FB033.png │ ├── 300410401819ea00f31fbcdbf9f5080a-23405.png │ ├── 34d2d7de2893f18e31aa4da767ee758f-58D64.png │ ├── 3928e3ff67b7682969bdc7a453f2ea3f-D93A4.png │ ├── 3b01c38b7c5b905fd8e8a1d72f7d7492-53427.png │ ├── 3bd0435b02740ac1a73a63af82d6e0c4-BEF2A.png │ ├── 3d5c2154d5a8da0a21d3a439a3685daa-54334.png │ ├── 3f30ac0da6a33abd185bfea2e6eee89e-05EDC.png │ ├── 3fcb70ed63af9d6c70cb3d649756c18e-909F3.png │ ├── 4cb420a78835bbf23bd2486403b12935-633AA.png │ ├── 4cdbe792d7b2d66afcc35b24f86d1fce-A3D4A.png │ ├── 4eaf99d01e4b5042454b4a6a8809687a-44C9E.png │ ├── 5-E9BDB.png │ ├── 5b71d15a9bcde45fd5520e3fb580eb53-E1A50.png │ ├── 5f978694dec96afc1640b5426003aef1-60E5B.png │ ├── 607cd03e07fc254bffbe76733290879a-7263E.png │ ├── 6382a608106beeaeaa8699d076f4b308-1FC1F.png │ ├── 63ddfe9345ae8359809cf027e458f3df-7AF32.png │ ├── 65e786c26c24f3ad91e011e953037ae5-6A1EB.png │ ├── 67594ee4b4d1fc03bca468327a0d145b-BD76A.png │ ├── 6eef2e8f35be0adcb2bd829d1a16f2a2-F341B.png │ ├── 750d791c21ffd3863332eea023124fa5-05646.png │ ├── 7667349f1925131ca93bc7023e6ff423-C6532.png │ ├── 78025b51fbd5016bf80d568e3b199528-E03F8.png │ ├── 80354a06375a2226575626d8ec6955c9-DD359.png │ ├── 80e24bbc2fada669f9f8a03aef044a7b-73586.png │ ├── 851893827089727568-5FD38.png │ ├── 851893827315826708-F59C0.png │ ├── 851893828280909886-FBF42.png │ ├── 88824bea991f559aa20a27323971b668-072D7.png │ ├── 8d091b0a2af24b881423c4caff6bc4b7-38483.png │ ├── 8f53411d3af94b5a6d0e4f6a2f48a6bb-68553.png │ ├── 912382242494480424-7D6D0.png │ ├── 93754346069447ca483458e8ea0be85e-0E299.png │ ├── 940149617683759104-64594.png │ ├── 962bd350e7eeb0507e4602c5c8e29fb2-D4D7E.png │ ├── 967f79c143742188d3268ca4caa2d667-9374B.png │ ├── 970d2e2f00cd7ef2134a1a3f21326349-404EA.png │ ├── Phi_Protocol-B01CB.png │ ├── Screenshot_from_2024-08-23_09-19-23-AD679.png │ ├── Screenshot_from_2024-08-23_09-27-50-D85D8.png │ ├── ae57a58d4684528be5738116869a381f-1B7D0.png │ ├── afca0e84e90e7172fdf16a089da1d694-947DB.png │ ├── apple-touch-icon%402-C50E4.png │ ├── b1ae038dfd4812eeaf4d15cdbdf14fe9-79287.png │ ├── b35842f1a8e77e0451125938c01e7c42-E05AC.png │ ├── b7e4b99bdbfab58a51d7ba020ded4376-E0875.png │ ├── c26466e3d184bbf0da048d9df9df8026-C8B14.png │ ├── c9cb30134c634c9e02d0c64df4922803-98E33.png │ ├── cb1491a50375c510fb66a6795ed7f94b-52531.png │ ├── cc21418793637794f3cc14a92678ea17-E7375.png │ ├── d17cb151966d230d5eb26a6fc3ac100c-9BF75.png │ ├── dc6e0072c993da12b699bb569417b6d1-25F7D.png │ ├── e1f76b4991e3e30a29f367db6bf42050-20038.png │ ├── e76f3132eea3c32215edab77e2e270d9-24F97.png │ ├── edbbcb1722b52a5d710513b51e3cfdfc-DD60A.png │ ├── efc72d9e28a1eacd5ec86918b3dde6cd-8ED8F.png │ ├── f3fb00a1b63ab2eea1ad7e0686aa5201-B6B2E.png │ ├── fccd54b7a125322eda867c3af397062c-F896F.png │ ├── ggsans-italic-400-E988B.woff2 │ ├── ggsans-italic-500-0777F.woff2 │ ├── ggsans-italic-600-CB411.woff2 │ ├── ggsans-italic-700-891AC.woff2 │ ├── ggsans-italic-800-D36B0.woff2 │ ├── ggsans-normal-400-1456D.woff2 │ ├── ggsans-normal-500-89CE5.woff2 │ ├── ggsans-normal-600-C1EA8.woff2 │ ├── ggsans-normal-700-1949A.woff2 │ ├── ggsans-normal-800-58487.woff2 │ ├── highlight.min-D8D27.js │ ├── image-6A5DF.png │ ├── image-9ED20.png │ ├── image-BE956.png │ ├── image-C0A7F.png │ ├── image-C9E20.png │ ├── joe-biden-presidential-debate-16BF1.mp4 │ ├── lottie.min-99657.js │ └── solarized-dark.min-BA98F.css ├── Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].txt └── Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].txt_Files │ ├── Phi_Protocol-B01CB.png │ ├── Screenshot_from_2024-08-23_09-19-23-AD679.png │ ├── Screenshot_from_2024-08-23_09-27-50-D85D8.png │ ├── apple-touch-icon%402-C50E4.png │ ├── image-6A5DF.png │ ├── image-9ED20.png │ ├── image-BE956.png │ ├── image-C0A7F.png │ ├── image-C9E20.png │ └── joe-biden-presidential-debate-66CDA.png ├── docs ├── ArchitectureDiagram.png ├── ClaimSigSequence.png ├── CreateCredSequence.png ├── arweave.md ├── audit │ └── Renascence - Phi Audit Report.pdf ├── dev.md ├── overview.md └── users.md ├── foundry.toml ├── merkle-tree ├── buildTree.ts └── tree.json ├── natspec-smells.config.js ├── out_of_scope.txt ├── package.json ├── remappings.txt ├── scope.txt ├── script ├── Base.s.sol ├── Deploy.s.sol ├── DeployWoCred.s.sol └── UpgradeCred.s.sol ├── slither.config.json ├── slither.txt ├── src ├── Cred.sol ├── PhiFactory.sol ├── abstract │ ├── Claimable.sol │ ├── CreatorRoyaltiesControl.sol │ └── RewardControl.sol ├── art │ └── PhiNFT1155.sol ├── curve │ └── BondingCurve.sol ├── interfaces │ ├── IBondingCurve.sol │ ├── IContributeRewards.sol │ ├── ICreatorRoyaltiesControl.sol │ ├── ICred.sol │ ├── ICuratorRewardsDistributor.sol │ ├── IOwnable.sol │ ├── IPhiFactory.sol │ ├── IPhiNFT1155.sol │ ├── IPhiNFT1155Ownable.sol │ ├── IPhiRewards.sol │ └── IRewards.sol ├── lib │ ├── ContributeRewards.sol │ ├── FixedPriceBondingCurve.sol │ ├── Logo.sol │ └── MerkleProof.sol └── reward │ ├── CuratorRewardsDistributor.sol │ └── PhiRewards.sol └── test ├── Claimable.t.sol ├── ContributeRewards.t.sol ├── Cred.t.sol ├── CuratorRewardsDistributor.t.sol ├── DeployAndUpgradeTest.t.sol ├── PhiFactory.t.sol ├── RewardControl.t.sol └── helpers ├── CredV2.sol ├── Settings.sol └── TestUtils.sol /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # All files 7 | [*] 8 | charset = utf-8 9 | end_of_line = lf 10 | indent_size = 2 11 | indent_style = space 12 | insert_final_newline = true 13 | trim_trailing_whitespace = true 14 | 15 | [*.sol] 16 | indent_size = 4 17 | 18 | [*.tree] 19 | indent_size = 1 20 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | export API_KEY_ALCHEMY="YOUR_API_KEY_ALCHEMY" 2 | export API_KEY_ARBISCAN="YOUR_API_KEY_ARBISCAN" 3 | export API_KEY_BSCSCAN="YOUR_API_KEY_BSCSCAN" 4 | export API_KEY_ETHERSCAN="YOUR_API_KEY_ETHERSCAN" 5 | export API_KEY_GNOSISSCAN="YOUR_API_KEY_GNOSISSCAN" 6 | export API_KEY_INFURA="YOUR_API_KEY_INFURA" 7 | export API_KEY_OPTIMISTIC_ETHERSCAN="YOUR_API_KEY_OPTIMISTIC_ETHERSCAN" 8 | export API_KEY_POLYGONSCAN="YOUR_API_KEY_POLYGONSCAN" 9 | export API_KEY_SNOWTRACE="YOUR_API_KEY_SNOWTRACE" 10 | export API_KEY_SEPOLIA="YOUR_API_KEY" 11 | export API_KEY_GOERLI="YOUR_API_KEY" 12 | export API_KEY_BASE="YOUR_API_KEY" 13 | export API_KEY_BASE_SEPOLIA="YOUR_API_KEY" 14 | export MNEMONIC="YOUR_MNEMONIC" 15 | export FOUNDRY_PROFILE="default" 16 | export TEST_CLAIM_SIGNER_PRIVATE_KEY="0x4af1bceebf7f3634ec3cff8a2c38e51178d5d4ce585c52d6043e5e2cc3418bb0" 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # directories 2 | cache 3 | coverage 4 | node_modules 5 | out 6 | 7 | # files 8 | *.env 9 | *.log 10 | .DS_Store 11 | .pnp.* 12 | lcov.info 13 | package-lock.json 14 | pnpm-lock.yaml 15 | yarn.lock 16 | 17 | # broadcasts 18 | !broadcast 19 | broadcast/* 20 | broadcast/*/31337/ 21 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/openzeppelin-contracts-upgradeable"] 2 | path = lib/openzeppelin-contracts-upgradeable 3 | url = https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable 4 | [submodule "lib/solady"] 5 | path = lib/solady 6 | url = https://github.com/vectorized/solady 7 | [submodule "lib/openzeppelin-foundry-upgrades"] 8 | path = lib/openzeppelin-foundry-upgrades 9 | url = https://github.com/OpenZeppelin/openzeppelin-foundry-upgrades 10 | [submodule "lib/foundry-devops"] 11 | path = lib/foundry-devops 12 | url = https://github.com/Cyfrin/foundry-devops 13 | [submodule "lib/forge-std"] 14 | path = lib/forge-std 15 | url = https://github.com/foundry-rs/forge-std 16 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # directories 2 | broadcast 3 | cache 4 | coverage 5 | node_modules 6 | out 7 | lib 8 | merkle-tree 9 | 10 | # files 11 | *.env 12 | *.log 13 | .DS_Store 14 | .pnp.* 15 | bun.lockb 16 | lcov.info 17 | package-lock.json 18 | pnpm-lock.yaml 19 | yarn.lock 20 | -------------------------------------------------------------------------------- /.prettierrc.yml: -------------------------------------------------------------------------------- 1 | bracketSpacing: true 2 | printWidth: 120 3 | proseWrap: "always" 4 | singleQuote: false 5 | tabWidth: 2 6 | trailingComma: "all" 7 | useTabs: false 8 | -------------------------------------------------------------------------------- /.python-version: -------------------------------------------------------------------------------- 1 | 3.10.11 2 | -------------------------------------------------------------------------------- /.solhint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solhint:recommended", 3 | "rules": { 4 | "code-complexity": ["error", 11], 5 | "compiler-version": ["error", ">=0.8.23"], 6 | "func-name-mixedcase": "off", 7 | "func-visibility": ["error", { "ignoreConstructors": true }], 8 | "max-line-length": ["error", 121], 9 | "named-parameters-mapping": "warn", 10 | "no-console": "off", 11 | "not-rely-on-time": "off", 12 | "one-contract-per-file": "off", 13 | "no-empty-blocks": "off", 14 | "no-inline-assembly": "off", 15 | "avoid-tx-origin": "off", 16 | "quotes": "off" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "[solidity]": { 3 | "editor.defaultFormatter": "NomicFoundation.hardhat-solidity" 4 | }, 5 | "[toml]": { 6 | "editor.defaultFormatter": "tamasfe.even-better-toml" 7 | }, 8 | "solidity.formatter": "forge" 9 | } 10 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 PHI 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 6 | documentation files (the "Software"), to deal in the Software without restriction, including without limitation the 7 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit 8 | persons to whom the Software is furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the 11 | Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 14 | WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 15 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 16 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17 | -------------------------------------------------------------------------------- /README-sponsor.md: -------------------------------------------------------------------------------- 1 | # PHI [![Open in Gitpod][gitpod-badge]][gitpod] [![Github Actions][gha-badge]][gha] [![Foundry][foundry-badge]][foundry] [![License: MIT][license-badge]][license] 2 | 3 | [gitpod]: https://gitpod.io/#https://github.com/ZaK3939/phi-protocol 4 | [gitpod-badge]: https://img.shields.io/badge/Gitpod-Open%20in%20Gitpod-FFB45B?logo=gitpod 5 | [gha]: https://github.com/ZaK3939/phi-protocol/actions 6 | [gha-badge]: https://github.com/ZaK3939/phi-protocol/actions/workflows/ci.yml/badge.svg 7 | [foundry]: https://getfoundry.sh/ 8 | [foundry-badge]: https://img.shields.io/badge/Built%20with-Foundry-FFDB1C.svg 9 | [license]: https://opensource.org/licenses/MIT 10 | [license-badge]: https://img.shields.io/badge/License-MIT-blue.svg 11 | 12 | ## Getting Started 13 | 14 | Phi Protocol is an open credentialing protocol to help users form, visualize, showcase their onchain identity. It 15 | incentivizes individuals to index blockchain transaction data as onchain credential blocks, curate them, host the 16 | verification process, and mint onchain credential contents. 17 | 18 | - Docs => docs/overview.md 19 | 20 | - Demo 21 | 22 | - https://base-sepolia.terminal.phiprotocol.xyz/ 23 | 24 | - Verifier-Base https://github.com/ZaK3939/base-creds 25 | 26 | ## Install 27 | 28 | ```sh 29 | $ bun install 30 | ``` 31 | 32 | ## Test 33 | 34 | ```sh 35 | $ forge test -vvv 36 | ``` 37 | 38 | ## Contract 39 | -------------------------------------------------------------------------------- /bun.lockb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/bun.lockb -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/0-EB806.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/0-EB806.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/01865d1bed0a1da3a253f05b3e79cb07-CB286.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/01865d1bed0a1da3a253f05b3e79cb07-CB286.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/0473983eb60155e816c3a2e070dcd889-A67D3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/0473983eb60155e816c3a2e070dcd889-A67D3.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/060ef80acbf6b2cb6cc9d7ad25665daf-4D4FE.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/060ef80acbf6b2cb6cc9d7ad25665daf-4D4FE.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/0c3222f16c36405c5c7024593d1b7225-7BCE7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/0c3222f16c36405c5c7024593d1b7225-7BCE7.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/0c70f1a9b0c75c0499ba9960169dc468-F07E0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/0c70f1a9b0c75c0499ba9960169dc468-F07E0.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/0cf7719f20f0aaed168c3f31a3f0bceb-8014C.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/0cf7719f20f0aaed168c3f31a3f0bceb-8014C.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/0e7bd30c9edad6eb8e0a7a890f666140-A5C72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/0e7bd30c9edad6eb8e0a7a890f666140-A5C72.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/1-B2132.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/1-B2132.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/1080667809002037320-8957D.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/1080667809002037320-8957D.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/1083068770823721071-EE4F6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/1083068770823721071-EE4F6.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/1096768259770028062-DDF70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/1096768259770028062-DDF70.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/1133784983211888712-1A533.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/1133784983211888712-1A533.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/1194958214408699974-36E12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/1194958214408699974-36E12.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/1233479995633172584-BBE31.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/1233479995633172584-BBE31.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/15cdaa10c5644d913a1ab1f9f06288c1-8A82B.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/15cdaa10c5644d913a1ab1f9f06288c1-8A82B.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/16947216bb5508b0ec8cb666ce9c68e7-7B124.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/16947216bb5508b0ec8cb666ce9c68e7-7B124.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/1f197-BB9F1.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/1f31f-1B968.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/1f340-360EA.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/1f389-5C738.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/1f3c1-445DC.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/1f43a-EB486.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/1f440-6C64D.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/1f44b-8A059.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/1f44d-27259.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/1f44f-3D381.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/1f499-93A1C.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/1f49c-71A75.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/1f4ac-4B485.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/1f4b8-E3468.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/1f4c6-44E30.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/1f50d-195C0.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/1f525-8FE4F.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/1f602-168C5.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/1f605-42B43.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/1f614-BB5EE.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/1f638-02313.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/1f642-83E8A.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/1f643-F8BDC.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/1f648-B83C5.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/1f64c-7C820.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/1f64f-1f3fc-34E32.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/1f64f-22B8D.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/1f680-A35CE.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/1f6a8-A8AB3.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/1f6ab-A6294.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/1f911-F346C.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/1f916-AD810.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/1f91d-5A0F2.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/1f92a-55954.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/1f972-F415D.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/1f973-88B39.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/1f97a-1F57B.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/1fa75-60476.svg: -------------------------------------------------------------------------------- 1 | Couldn't find the requested file /assets/svg/1fa75.svg in twitter/twemoji. -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/1fae1-B19DE.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/2-ADBB4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/2-ADBB4.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/237f8315bfd31d0464b62371d56423b1-6F4B9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/237f8315bfd31d0464b62371d56423b1-6F4B9.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/258efb44a5e3f3ccd0db6c4b11462ff2-08900.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/258efb44a5e3f3ccd0db6c4b11462ff2-08900.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/26a0-D845B.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/2705-0589F.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/2764-A3D25.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/27e40b0499e32304fe17900046221e11-1AE74.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/27e40b0499e32304fe17900046221e11-1AE74.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/2a9faff195fe333526cfe6ae6fce1420-49B98.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/2a9faff195fe333526cfe6ae6fce1420-49B98.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/3-FB033.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/3-FB033.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/300410401819ea00f31fbcdbf9f5080a-23405.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/300410401819ea00f31fbcdbf9f5080a-23405.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/34d2d7de2893f18e31aa4da767ee758f-58D64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/34d2d7de2893f18e31aa4da767ee758f-58D64.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/3928e3ff67b7682969bdc7a453f2ea3f-D93A4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/3928e3ff67b7682969bdc7a453f2ea3f-D93A4.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/3b01c38b7c5b905fd8e8a1d72f7d7492-53427.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/3b01c38b7c5b905fd8e8a1d72f7d7492-53427.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/3bd0435b02740ac1a73a63af82d6e0c4-BEF2A.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/3bd0435b02740ac1a73a63af82d6e0c4-BEF2A.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/3d5c2154d5a8da0a21d3a439a3685daa-54334.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/3d5c2154d5a8da0a21d3a439a3685daa-54334.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/3f30ac0da6a33abd185bfea2e6eee89e-05EDC.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/3f30ac0da6a33abd185bfea2e6eee89e-05EDC.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/3fcb70ed63af9d6c70cb3d649756c18e-909F3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/3fcb70ed63af9d6c70cb3d649756c18e-909F3.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/4cb420a78835bbf23bd2486403b12935-633AA.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/4cb420a78835bbf23bd2486403b12935-633AA.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/4cdbe792d7b2d66afcc35b24f86d1fce-A3D4A.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/4cdbe792d7b2d66afcc35b24f86d1fce-A3D4A.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/4eaf99d01e4b5042454b4a6a8809687a-44C9E.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/4eaf99d01e4b5042454b4a6a8809687a-44C9E.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/5-E9BDB.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/5-E9BDB.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/5b71d15a9bcde45fd5520e3fb580eb53-E1A50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/5b71d15a9bcde45fd5520e3fb580eb53-E1A50.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/5f978694dec96afc1640b5426003aef1-60E5B.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/5f978694dec96afc1640b5426003aef1-60E5B.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/607cd03e07fc254bffbe76733290879a-7263E.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/607cd03e07fc254bffbe76733290879a-7263E.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/6382a608106beeaeaa8699d076f4b308-1FC1F.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/6382a608106beeaeaa8699d076f4b308-1FC1F.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/63ddfe9345ae8359809cf027e458f3df-7AF32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/63ddfe9345ae8359809cf027e458f3df-7AF32.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/65e786c26c24f3ad91e011e953037ae5-6A1EB.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/65e786c26c24f3ad91e011e953037ae5-6A1EB.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/67594ee4b4d1fc03bca468327a0d145b-BD76A.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/67594ee4b4d1fc03bca468327a0d145b-BD76A.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/6eef2e8f35be0adcb2bd829d1a16f2a2-F341B.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/6eef2e8f35be0adcb2bd829d1a16f2a2-F341B.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/750d791c21ffd3863332eea023124fa5-05646.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/750d791c21ffd3863332eea023124fa5-05646.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/7667349f1925131ca93bc7023e6ff423-C6532.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/7667349f1925131ca93bc7023e6ff423-C6532.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/78025b51fbd5016bf80d568e3b199528-E03F8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/78025b51fbd5016bf80d568e3b199528-E03F8.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/80354a06375a2226575626d8ec6955c9-DD359.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/80354a06375a2226575626d8ec6955c9-DD359.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/80e24bbc2fada669f9f8a03aef044a7b-73586.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/80e24bbc2fada669f9f8a03aef044a7b-73586.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/851893827089727568-5FD38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/851893827089727568-5FD38.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/851893827315826708-F59C0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/851893827315826708-F59C0.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/851893828280909886-FBF42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/851893828280909886-FBF42.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/88824bea991f559aa20a27323971b668-072D7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/88824bea991f559aa20a27323971b668-072D7.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/8d091b0a2af24b881423c4caff6bc4b7-38483.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/8d091b0a2af24b881423c4caff6bc4b7-38483.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/8f53411d3af94b5a6d0e4f6a2f48a6bb-68553.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/8f53411d3af94b5a6d0e4f6a2f48a6bb-68553.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/912382242494480424-7D6D0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/912382242494480424-7D6D0.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/93754346069447ca483458e8ea0be85e-0E299.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/93754346069447ca483458e8ea0be85e-0E299.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/940149617683759104-64594.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/940149617683759104-64594.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/962bd350e7eeb0507e4602c5c8e29fb2-D4D7E.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/962bd350e7eeb0507e4602c5c8e29fb2-D4D7E.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/967f79c143742188d3268ca4caa2d667-9374B.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/967f79c143742188d3268ca4caa2d667-9374B.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/970d2e2f00cd7ef2134a1a3f21326349-404EA.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/970d2e2f00cd7ef2134a1a3f21326349-404EA.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/Phi_Protocol-B01CB.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/Phi_Protocol-B01CB.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/Screenshot_from_2024-08-23_09-19-23-AD679.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/Screenshot_from_2024-08-23_09-19-23-AD679.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/Screenshot_from_2024-08-23_09-27-50-D85D8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/Screenshot_from_2024-08-23_09-27-50-D85D8.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/ae57a58d4684528be5738116869a381f-1B7D0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/ae57a58d4684528be5738116869a381f-1B7D0.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/afca0e84e90e7172fdf16a089da1d694-947DB.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/afca0e84e90e7172fdf16a089da1d694-947DB.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/apple-touch-icon%402-C50E4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/apple-touch-icon%402-C50E4.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/b1ae038dfd4812eeaf4d15cdbdf14fe9-79287.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/b1ae038dfd4812eeaf4d15cdbdf14fe9-79287.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/b35842f1a8e77e0451125938c01e7c42-E05AC.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/b35842f1a8e77e0451125938c01e7c42-E05AC.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/b7e4b99bdbfab58a51d7ba020ded4376-E0875.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/b7e4b99bdbfab58a51d7ba020ded4376-E0875.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/c26466e3d184bbf0da048d9df9df8026-C8B14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/c26466e3d184bbf0da048d9df9df8026-C8B14.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/c9cb30134c634c9e02d0c64df4922803-98E33.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/c9cb30134c634c9e02d0c64df4922803-98E33.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/cb1491a50375c510fb66a6795ed7f94b-52531.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/cb1491a50375c510fb66a6795ed7f94b-52531.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/cc21418793637794f3cc14a92678ea17-E7375.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/cc21418793637794f3cc14a92678ea17-E7375.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/d17cb151966d230d5eb26a6fc3ac100c-9BF75.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/d17cb151966d230d5eb26a6fc3ac100c-9BF75.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/dc6e0072c993da12b699bb569417b6d1-25F7D.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/dc6e0072c993da12b699bb569417b6d1-25F7D.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/e1f76b4991e3e30a29f367db6bf42050-20038.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/e1f76b4991e3e30a29f367db6bf42050-20038.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/e76f3132eea3c32215edab77e2e270d9-24F97.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/e76f3132eea3c32215edab77e2e270d9-24F97.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/edbbcb1722b52a5d710513b51e3cfdfc-DD60A.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/edbbcb1722b52a5d710513b51e3cfdfc-DD60A.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/efc72d9e28a1eacd5ec86918b3dde6cd-8ED8F.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/efc72d9e28a1eacd5ec86918b3dde6cd-8ED8F.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/f3fb00a1b63ab2eea1ad7e0686aa5201-B6B2E.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/f3fb00a1b63ab2eea1ad7e0686aa5201-B6B2E.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/fccd54b7a125322eda867c3af397062c-F896F.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/fccd54b7a125322eda867c3af397062c-F896F.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/ggsans-italic-400-E988B.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/ggsans-italic-400-E988B.woff2 -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/ggsans-italic-500-0777F.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/ggsans-italic-500-0777F.woff2 -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/ggsans-italic-600-CB411.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/ggsans-italic-600-CB411.woff2 -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/ggsans-italic-700-891AC.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/ggsans-italic-700-891AC.woff2 -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/ggsans-italic-800-D36B0.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/ggsans-italic-800-D36B0.woff2 -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/ggsans-normal-400-1456D.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/ggsans-normal-400-1456D.woff2 -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/ggsans-normal-500-89CE5.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/ggsans-normal-500-89CE5.woff2 -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/ggsans-normal-600-C1EA8.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/ggsans-normal-600-C1EA8.woff2 -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/ggsans-normal-700-1949A.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/ggsans-normal-700-1949A.woff2 -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/ggsans-normal-800-58487.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/ggsans-normal-800-58487.woff2 -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/image-6A5DF.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/image-6A5DF.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/image-9ED20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/image-9ED20.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/image-BE956.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/image-BE956.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/image-C0A7F.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/image-C0A7F.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/image-C9E20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/image-C9E20.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/joe-biden-presidential-debate-16BF1.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/joe-biden-presidential-debate-16BF1.mp4 -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].html_Files/solarized-dark.min-BA98F.css: -------------------------------------------------------------------------------- 1 | .hljs{display:block;overflow-x:auto;padding:.5em;background:#002b36;color:#839496}.hljs-comment,.hljs-quote{color:#586e75}.hljs-keyword,.hljs-selector-tag,.hljs-addition{color:#859900}.hljs-number,.hljs-string,.hljs-meta .hljs-meta-string,.hljs-literal,.hljs-doctag,.hljs-regexp{color:#2aa198}.hljs-title,.hljs-section,.hljs-name,.hljs-selector-id,.hljs-selector-class{color:#268bd2}.hljs-attribute,.hljs-attr,.hljs-variable,.hljs-template-variable,.hljs-class .hljs-title,.hljs-type{color:#b58900}.hljs-symbol,.hljs-bullet,.hljs-subst,.hljs-meta,.hljs-meta .hljs-keyword,.hljs-selector-attr,.hljs-selector-pseudo,.hljs-link{color:#cb4b16}.hljs-built_in,.hljs-deletion{color:#dc322f}.hljs-formula{background:#073642}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:bold} -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].txt_Files/Phi_Protocol-B01CB.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].txt_Files/Phi_Protocol-B01CB.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].txt_Files/Screenshot_from_2024-08-23_09-19-23-AD679.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].txt_Files/Screenshot_from_2024-08-23_09-19-23-AD679.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].txt_Files/Screenshot_from_2024-08-23_09-27-50-D85D8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].txt_Files/Screenshot_from_2024-08-23_09-27-50-D85D8.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].txt_Files/apple-touch-icon%402-C50E4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].txt_Files/apple-touch-icon%402-C50E4.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].txt_Files/image-6A5DF.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].txt_Files/image-6A5DF.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].txt_Files/image-9ED20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].txt_Files/image-9ED20.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].txt_Files/image-BE956.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].txt_Files/image-BE956.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].txt_Files/image-C0A7F.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].txt_Files/image-C0A7F.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].txt_Files/image-C9E20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].txt_Files/image-C9E20.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].txt_Files/joe-biden-presidential-debate-66CDA.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/discord-export/Code4rena - ARCHIVE-PUBLIC - phi-aug22 [1273806655422992406].txt_Files/joe-biden-presidential-debate-66CDA.png -------------------------------------------------------------------------------- /docs/ArchitectureDiagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/docs/ArchitectureDiagram.png -------------------------------------------------------------------------------- /docs/ClaimSigSequence.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/docs/ClaimSigSequence.png -------------------------------------------------------------------------------- /docs/CreateCredSequence.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/docs/CreateCredSequence.png -------------------------------------------------------------------------------- /docs/arweave.md: -------------------------------------------------------------------------------- 1 | ## Cred 2 | 3 | - cred (MERKLE) https://arweave.net/07UwhAbzabbVCONzOGFBI_RSGAR53VuIWg5QtLc8MPY 4 | - address list https://arweave.net/iu9o8vHQmOyq0QzlY9nf4rHZqAOVCpQ6hJKcgm0PdYg 5 | - merkle tree https://arweave.net/YasBn_PQuBwyNJ3LDdovhRJjYrtQZWl4S0MylkJkRF4 6 | 7 | - cred (SIGNATURE) https://arweave.net/05h3kHrNG8iNcVcRPnQb6hdmdkjQ--NnmcteMirQQXg 8 | - verifier list https://arweave.net/es6AuKe9D6etVyGccjyVqMWYf8SDN_hd2lS1BX48mVw 9 | - whitelist https://arweave.net/ZTAqLQi4FJwohj17nzyIsAGZg_Cxjmp6CyQ7xY6aWzA 10 | 11 | # Artwork 12 | 13 | - art (BASIC) https://arweave.net/VTqPWOvHuWqGE1QJkWu03tMXE-a-c86551hYmGOGwlc 14 | - art (ADVANCED) https://arweave.net/ySmBFIiD7EzlqPnLvGXASSIGIdv3rSMVKFTUhIH0ScA 15 | -------------------------------------------------------------------------------- /docs/audit/Renascence - Phi Audit Report.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-phi/2465e04364b759c721f1a0aebace69920411f8aa/docs/audit/Renascence - Phi Audit Report.pdf -------------------------------------------------------------------------------- /docs/users.md: -------------------------------------------------------------------------------- 1 | ## Phi Protocol has 5 key types of users: 2 | 3 | - Credential Creator 4 | - Curator 5 | - Verifier 6 | - Artist 7 | - Minter/Collector 8 | 9 | --- 10 | 11 | - Credential Creator Credential Creators can suggest/create an Onchain credential by indexing blockchain data. 12 | 13 | - Curator Curators show support for specific credentials by buying or selling Shares. Purchasing Shares helps the 14 | protocol understand which credentials are in higher demand. This is defined by the bonding curve for each Credential 15 | along with how early the Curator bought their share. 16 | 17 | Curators also earn a portion of mint fees for the credentials 18 | 19 | - Verifier Verifier's help create the logical criteria to verify that the Minter is eligible to claim a credential by 20 | querying the user's onchain data. Verifier's achieve this by creating the backend validation logic and hosting this 21 | for Phi Protocol. 22 | 23 | Verifier's also earn a portion of mint fees for the credential they help verify. 24 | 25 | - Artist Artists help create credential content which can be minted by Minters who want to collect the credentials. 26 | Multiple artists and artworks can be created for an onchain credential, which allows various styles of art to be 27 | collected. Artist's also earn a portion of mint fees for the credential they help verify. 28 | 29 | - Collector Collectors focus on meeting eligibility requirements to collect Credentials. Collectors mint Credential 30 | NFT's and pay a small fee in the process. 31 | -------------------------------------------------------------------------------- /foundry.toml: -------------------------------------------------------------------------------- 1 | # Full reference https://github.com/foundry-rs/foundry/tree/master/crates/config 2 | 3 | [profile.default] 4 | auto_detect_solc = false 5 | block_timestamp = 1_680_220_800 # March 31, 2023 at 00:00 GMT 6 | bytecode_hash = "none" 7 | evm_version = "paris" # See https://www.evmdiff.com/features?name=PUSH0&kind=opcode 8 | fuzz = { runs = 1_000 } 9 | gas_reports = ["*"] 10 | optimizer = true 11 | optimizer_runs = 10_000 12 | out = "out" 13 | script = "script" 14 | solc = "0.8.25" 15 | src = "src" 16 | test = "test" 17 | build_info = true 18 | extra_output = ["storageLayout"] 19 | fs_permissions = [ 20 | { access = "read", path = "./broadcast" }, 21 | { access = "read", path = "./reports" }, 22 | ] 23 | 24 | [profile.ci] 25 | fuzz = { runs = 10_000 } 26 | verbosity = 4 27 | 28 | [etherscan] 29 | arbitrum = { key = "${API_KEY_ARBISCAN}" } 30 | arbitrum_sepolia = { key = "${API_KEY_ARBISCAN}" } 31 | avalanche = { key = "${API_KEY_SNOWTRACE}" } 32 | goerli = { key = "${API_KEY_ETHERSCAN}" } 33 | mainnet = { key = "${API_KEY_ETHERSCAN}" } 34 | optimism = { key = "${API_KEY_OPTIMISTIC_ETHERSCAN}" } 35 | optimism_sepolia = { key = "${API_KEY_OPTIMISTIC_ETHERSCAN}" } 36 | polygon = { key = "${API_KEY_POLYGONSCAN}" } 37 | sepolia = { key = "${API_KEY_ETHERSCAN}" } 38 | base = { key = "${API_KEY_BASE}" } 39 | base_sepolia = { key = "${API_KEY_BASE}" } 40 | bera_testnet = { key = "${API_KEY_BERA}" } 41 | 42 | 43 | [fmt] 44 | bracket_spacing = true 45 | int_types = "long" 46 | line_length = 120 47 | multiline_func_header = "all" 48 | number_underscore = "thousands" 49 | quote_style = "double" 50 | tab_width = 4 51 | wrap_comments = true 52 | 53 | [rpc_endpoints] 54 | arbitrum = "https://arbitrum-mainnet.infura.io/v3/${API_KEY_INFURA}" 55 | arbitrum_sepolia = "${ARB_SEPOLIA_RPC}" 56 | avalanche = "https://avalanche-mainnet.infura.io/v3/${API_KEY_INFURA}" 57 | goerli = "https://eth-goerli.g.alchemy.com/v2/${API_KEY_GOERLI}" 58 | localhost = "http://localhost:8545" 59 | mainnet = "https://eth-mainnet.g.alchemy.com/v2/${API_KEY_ALCHEMY}" 60 | optimism = "https://opt-mainnet.g.alchemy.com/v2/${API_KEY_OPTIMISTIC}" 61 | optimism_sepolia = "https://opt-sepolia.g.alchemy.com/v2/${API_KEY_OPTIMISTIC_SEPOLIA}" 62 | polygon = "https://polygon-mainnet.infura.io/v3/${API_KEY_INFURA}" 63 | sepolia = "https://rpc.ankr.com/eth_sepolia" 64 | phi = "https://rpc-test-ph-uop5quepc8.t.conduit.xyz" 65 | orbit_test = "https://orbit-demo.alt.technology" 66 | base = "${BASE_MAINNET_RPC}" 67 | base_sepolia = "https://base-sepolia.g.alchemy.com/v2/${API_KEY_BASE_SEPOLIA}" 68 | syndicate = "https://rpc-frame.syndicate.io" 69 | bera_testnet = "https://bitter-rough-owl.bera-bartio.quiknode.pro/4e2fcb78f8ac6cd2d04e471385cb875623f4c57a" 70 | zora_sepolia = "${ZORA_SEPOLIA_RPC}" 71 | cyber_testnet = "https://cyber-testnet.alt.technology/" 72 | cyber = "https://cyber.alt.technology/" 73 | -------------------------------------------------------------------------------- /merkle-tree/buildTree.ts: -------------------------------------------------------------------------------- 1 | import { StandardMerkleTree } from "@openzeppelin/merkle-tree"; 2 | import fs from "fs"; 3 | import { parseEther, toHex } from "viem"; 4 | 5 | // (1) 6 | 7 | const values = [ 8 | ["0x1111111111111111111111111111111111111111", "5000000000000000000"], 9 | ["0x2222222222222222222222222222222222222222", "2500000000000000000"], 10 | ["0x3333333333333333333333333333333333333333"], 11 | ]; 12 | 13 | // (2) 14 | const convertedValues = values.map(([address, value]) => { 15 | if (value) { 16 | try { 17 | const convertedValue = parseEther(value); 18 | const bytes32Value = toHex(convertedValue, { size: 32 }); 19 | return [address, bytes32Value]; 20 | } catch (error) { 21 | throw new Error(`Invalid value: ${value}`); 22 | } 23 | } else { 24 | return [address, "0x0000000000000000000000000000000000000000000000000000000000000000"]; // bytes32(0) 25 | } 26 | }); 27 | // (3) 28 | const tree = StandardMerkleTree.of(convertedValues, ["address", "bytes32"]); 29 | 30 | // (4) 31 | console.log("Merkle Root:", tree.root); 32 | 33 | // (5) 34 | fs.writeFileSync("./merkle-tree/tree.json", JSON.stringify(tree.dump())); 35 | 36 | // (6) 37 | for (const [i, v] of tree.entries()) { 38 | if (v[0] === "0x1111111111111111111111111111111111111111") { 39 | const proof = tree.getProof(i); 40 | console.log("Value:", v); 41 | console.log("Proof:", proof); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /merkle-tree/tree.json: -------------------------------------------------------------------------------- 1 | {"format":"standard-v1","tree":["0xe70e719557c28ce2f2f3545d64c633728d70fbcfe6ae3db5fa01420573e0f34b","0xdc4c272ceaa7b5ba9e0f3f9972f735f14e1b4ed191e15d0d17afc62792226428","0xdd05ddd79adc5569806124d3c5d8151b75bc81032a0ea21d4cd74fd964947bf5","0x479d2670cf2a6e25a15184f06c1902f5c9346e73ecf18d6e6c3cf8531ca9b815","0x0927f012522ebd33191e00fe62c11db25288016345e12e6b63709bb618d777d4"],"values":[{"value":["0x1111111111111111111111111111111111111111","0x0000000000000000000000000000000003c2f7086aed236c807a1b5000000000"],"treeIndex":3},{"value":["0x2222222222222222222222222222222222222222","0x0000000000000000000000000000000001e17b84357691b6403d0da800000000"],"treeIndex":4},{"value":["0x3333333333333333333333333333333333333333","0x0000000000000000000000000000000000000000000000000000000000000000"],"treeIndex":2}],"leafEncoding":["address","bytes32"]} -------------------------------------------------------------------------------- /natspec-smells.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * List of supported options: https://github.com/defi-wonderland/natspec-smells?tab=readme-ov-file#options 3 | */ 4 | 5 | /** @type {import('@defi-wonderland/natspec-smells').Config} */ 6 | module.exports = { 7 | include: "src", 8 | exclude: ["./(test|scripts)/**/*.sol"], 9 | }; 10 | -------------------------------------------------------------------------------- /out_of_scope.txt: -------------------------------------------------------------------------------- 1 | ./script/Base.s.sol 2 | ./script/Deploy.s.sol 3 | ./script/DeployWoCred.s.sol 4 | ./script/UpgradeCred.s.sol 5 | ./src/interfaces/IBondingCurve.sol 6 | ./src/interfaces/IContributeRewards.sol 7 | ./src/interfaces/ICreatorRoyaltiesControl.sol 8 | ./src/interfaces/ICred.sol 9 | ./src/interfaces/ICuratorRewardsDistributor.sol 10 | ./src/interfaces/IOwnable.sol 11 | ./src/interfaces/IPhiFactory.sol 12 | ./src/interfaces/IPhiNFT1155.sol 13 | ./src/interfaces/IPhiNFT1155Ownable.sol 14 | ./src/interfaces/IPhiRewards.sol 15 | ./src/interfaces/IRewards.sol 16 | ./src/lib/ContributeRewards.sol 17 | ./src/lib/FixedPriceBondingCurve.sol 18 | ./src/lib/Logo.sol 19 | ./src/lib/MerkleProof.sol 20 | ./test/Claimable.t.sol 21 | ./test/ContributeRewards.t.sol 22 | ./test/Cred.t.sol 23 | ./test/CuratorRewardsDistributor.t.sol 24 | ./test/DeployAndUpgradeTest.t.sol 25 | ./test/PhiFactory.t.sol 26 | ./test/RewardControl.t.sol 27 | ./test/helpers/CredV2.sol 28 | ./test/helpers/Settings.sol 29 | ./test/helpers/TestUtils.sol -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ZaK3939/phi-protocol", 3 | "description": "", 4 | "version": "1.0.0", 5 | "author": { 6 | "name": "ZaK3939", 7 | "url": "https://github.com/ZaK3939" 8 | }, 9 | "dependencies": { 10 | "@defi-wonderland/natspec-smells": "^1.0.3", 11 | "@openzeppelin/contracts": "^5.0.1", 12 | "@openzeppelin/merkle-tree": "^1.0.6", 13 | "fs": "^0.0.1-security", 14 | "prettier-plugin-solidity": "^1.3.1", 15 | "viem": "^2.13.8" 16 | }, 17 | "devDependencies": { 18 | "@prb/test": "^0.6.4", 19 | "forge-std": "github:foundry-rs/forge-std#v1", 20 | "prettier": "^3.0.0", 21 | "solhint": "^3.6.2" 22 | }, 23 | "keywords": [ 24 | "blockchain", 25 | "ethereum", 26 | "forge", 27 | "foundry", 28 | "smart-contracts", 29 | "solidity", 30 | "template" 31 | ], 32 | "private": true, 33 | "scripts": { 34 | "clean": "rm -rf cache out", 35 | "build": "forge build", 36 | "lint": "bun run lint:sol && bun run prettier:check", 37 | "lint:sol": "forge fmt --check && bun solhint {script,src}/**/*.sol && bun solhint {script,src}/*.sol", 38 | "prettier:check": "prettier --check **/*.{json,md,yml} --ignore-path=.prettierignore", 39 | "prettier:write": "prettier --write **/*.{json,md,yml} --ignore-path=.prettierignore", 40 | "test": "forge test", 41 | "test:coverage": "forge coverage", 42 | "test:coverage:report": "forge coverage --report lcov && genhtml lcov.info --branch-coverage --output-dir coverage", 43 | "natspec": "natspec-smells" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /remappings.txt: -------------------------------------------------------------------------------- 1 | @prb/test/=node_modules/@prb/test/ 2 | forge-std/=lib/forge-std/src/ 3 | solady/=lib/solady/src/ 4 | foundry-devops/=lib/foundry-devops/src/ 5 | @openzeppelin/contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/ 6 | @openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/ -------------------------------------------------------------------------------- /scope.txt: -------------------------------------------------------------------------------- 1 | ./src/Cred.sol 2 | ./src/PhiFactory.sol 3 | ./src/abstract/Claimable.sol 4 | ./src/abstract/CreatorRoyaltiesControl.sol 5 | ./src/abstract/RewardControl.sol 6 | ./src/art/PhiNFT1155.sol 7 | ./src/curve/BondingCurve.sol 8 | ./src/reward/CuratorRewardsDistributor.sol 9 | ./src/reward/PhiRewards.sol -------------------------------------------------------------------------------- /script/Base.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.23 <0.9.0; 3 | 4 | import { Script } from "forge-std/Script.sol"; 5 | 6 | abstract contract BaseScript is Script { 7 | /// @dev Included to enable compilation of the script without a $MNEMONIC environment variable. 8 | string internal constant TEST_MNEMONIC = "test test test test test test test test test test test junk"; 9 | 10 | /// @dev Needed for the deterministic deployments. 11 | bytes32 internal constant ZERO_SALT = bytes32(0); 12 | 13 | /// @dev The address of the transaction broadcaster. 14 | address internal broadcaster; 15 | 16 | /// @dev Used to derive the broadcaster's address if $ETH_FROM is not defined. 17 | string internal mnemonic; 18 | 19 | /// @dev Initializes the transaction broadcaster like this: 20 | /// 21 | /// - If $ETH_FROM is defined, use it. 22 | /// - Otherwise, derive the broadcaster address from $MNEMONIC. 23 | /// - If $MNEMONIC is not defined, default to a test mnemonic. 24 | /// 25 | /// The use case for $ETH_FROM is to specify the broadcaster key and its address via the command line. 26 | constructor() { 27 | address from = vm.envOr({ name: "ETH_FROM", defaultValue: address(0) }); 28 | if (from != address(0)) { 29 | broadcaster = from; 30 | } else { 31 | mnemonic = vm.envOr({ name: "MNEMONIC", defaultValue: TEST_MNEMONIC }); 32 | (broadcaster,) = deriveRememberKey({ mnemonic: mnemonic, index: 0 }); 33 | } 34 | } 35 | 36 | modifier broadcast() { 37 | vm.startBroadcast(broadcaster); 38 | _; 39 | vm.stopBroadcast(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /script/Deploy.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.23 <0.9.0; 3 | 4 | import { console2 } from "forge-std/console2.sol"; 5 | 6 | import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; 7 | import { LibClone } from "solady/utils/LibClone.sol"; 8 | import { Cred } from "../src/Cred.sol"; 9 | import { PhiNFT1155 } from "../src/art/PhiNFT1155.sol"; 10 | import { BondingCurve } from "../src/curve/BondingCurve.sol"; 11 | import { FixedPriceBondingCurve } from "../src/lib/FixedPriceBondingCurve.sol"; 12 | import { PhiRewards } from "../src/reward/PhiRewards.sol"; 13 | import { CuratorRewardsDistributor } from "../src/reward/CuratorRewardsDistributor.sol"; 14 | import { PhiFactory } from "../src/PhiFactory.sol"; 15 | import { BaseScript } from "./Base.s.sol"; 16 | 17 | // https://github.com/Cyfrin/foundry-upgrades-f23/blob/main/script/DeployBox.s.sol 18 | /// @dev See the Solidity Scripting tutorial: https://book.getfoundry.sh/tutorials/solidity-scripting 19 | contract Deploy is BaseScript { 20 | using LibClone for address; 21 | 22 | address public deployer; 23 | Cred public cred; 24 | PhiFactory public phiFactory; 25 | PhiNFT1155 public phiNFT1155; 26 | BondingCurve public bondingCurve; 27 | FixedPriceBondingCurve public fixedPriceBondingCurve; 28 | PhiRewards public phiRewards; 29 | CuratorRewardsDistributor public curatorRewardsDistributor; 30 | 31 | address public oji3 = 0x5cD18dA4C84758319C8E1c228b48725f5e4a3506; 32 | address public signer = 0x29C76e6aD8f28BB1004902578Fb108c507Be341b; 33 | 34 | function setUp() public virtual { 35 | string memory mnemonic = vm.envString("MNEMONIC"); 36 | (deployer,) = deriveRememberKey(mnemonic, 0); 37 | } 38 | 39 | function run() public broadcast { 40 | console2.log("Chain Info: %s", block.chainid); 41 | phiNFT1155 = new PhiNFT1155(); 42 | 43 | phiRewards = new PhiRewards(deployer); 44 | console2.log("PhiRewards deployed at address: %s", address(phiRewards)); 45 | 46 | phiFactory = new PhiFactory(); 47 | ERC1967Proxy phiFactoryProxy = new ERC1967Proxy(address(phiFactory), ""); 48 | PhiFactory(payable(address(phiFactoryProxy))).initialize( 49 | signer, oji3, address(phiNFT1155), address(phiRewards), oji3, 0.00005 ether, 0 50 | ); 51 | console2.log("PhiFactory deployed at address: %s", address(phiFactoryProxy)); 52 | 53 | bondingCurve = new BondingCurve(oji3); 54 | console2.log("BondingCurve deployed at address: %s", address(bondingCurve)); 55 | 56 | fixedPriceBondingCurve = new FixedPriceBondingCurve(oji3); 57 | console2.log("FixedPriceBondingCurve deployed at address: %s", address(fixedPriceBondingCurve)); 58 | 59 | ERC1967Proxy credProxy = new ERC1967Proxy(address(new Cred()), ""); 60 | console2.log("Cred deployed at address: %s", address(credProxy)); 61 | Cred(payable(address(credProxy))).initialize( 62 | signer, oji3, deployer, 500, address(bondingCurve), address(phiRewards) 63 | ); 64 | 65 | curatorRewardsDistributor = new CuratorRewardsDistributor(address(phiRewards), payable(address(credProxy))); 66 | console2.log("CuratorRewardsDistributor deployed at address: %s", address(curatorRewardsDistributor)); 67 | 68 | phiRewards.updateCuratorRewardsDistributor(address(curatorRewardsDistributor)); 69 | // bondingCurve.setCredContract(address(credProxy)); 70 | 71 | // BondingCurve(bondingCurveAddress).setCredContract(address(credProxy)); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /script/DeployWoCred.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.23 <0.9.0; 3 | 4 | import { console2 } from "forge-std/console2.sol"; 5 | 6 | import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; 7 | import { LibClone } from "solady/utils/LibClone.sol"; 8 | import { Cred } from "../src/Cred.sol"; 9 | import { PhiNFT1155 } from "../src/art/PhiNFT1155.sol"; 10 | import { BondingCurve } from "../src/curve/BondingCurve.sol"; 11 | import { FixedPriceBondingCurve } from "../src/lib/FixedPriceBondingCurve.sol"; 12 | import { PhiRewards } from "../src/reward/PhiRewards.sol"; 13 | import { CuratorRewardsDistributor } from "../src/reward/CuratorRewardsDistributor.sol"; 14 | import { PhiFactory } from "../src/PhiFactory.sol"; 15 | import { BaseScript } from "./Base.s.sol"; 16 | 17 | // https://github.com/Cyfrin/foundry-upgrades-f23/blob/main/script/DeployBox.s.sol 18 | /// @dev See the Solidity Scripting tutorial: https://book.getfoundry.sh/tutorials/solidity-scripting 19 | contract Deploy is BaseScript { 20 | using LibClone for address; 21 | 22 | address public deployer; 23 | Cred public cred; 24 | PhiFactory public phiFactory; 25 | PhiNFT1155 public phiNFT1155; 26 | BondingCurve public bondingCurve; 27 | FixedPriceBondingCurve public fixedPriceBondingCurve; 28 | PhiRewards public phiRewards; 29 | CuratorRewardsDistributor public curatorRewardsDistributor; 30 | 31 | address public oji3 = 0x5cD18dA4C84758319C8E1c228b48725f5e4a3506; 32 | address public signer = 0x29C76e6aD8f28BB1004902578Fb108c507Be341b; 33 | 34 | function setUp() public virtual { 35 | string memory mnemonic = vm.envString("MNEMONIC"); 36 | (deployer,) = deriveRememberKey(mnemonic, 0); 37 | } 38 | 39 | function run() public broadcast { 40 | console2.log("Chain Info: %s", block.chainid); 41 | phiNFT1155 = new PhiNFT1155(); 42 | 43 | phiRewards = new PhiRewards(deployer); 44 | console2.log("PhiRewards deployed at address: %s", address(phiRewards)); 45 | 46 | phiFactory = new PhiFactory(); 47 | ERC1967Proxy phiFactoryProxy = new ERC1967Proxy(address(phiFactory), ""); 48 | PhiFactory(payable(address(phiFactoryProxy))).initialize( 49 | signer, oji3, address(phiNFT1155), address(phiRewards), oji3, 0.0002 ether, 0 50 | ); 51 | console2.log("PhiFactory deployed at address: %s", address(phiFactoryProxy)); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /script/UpgradeCred.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import { Cred } from "../src/Cred.sol"; 5 | import { CredV2 } from "../test/helpers/CredV2.sol"; 6 | import { DevOpsTools } from "foundry-devops/DevOpsTools.sol"; 7 | import { BaseScript } from "./Base.s.sol"; 8 | 9 | contract UpgradeCred is BaseScript { 10 | function run() external returns (address) { 11 | address mostRecentlyDeployedProxy = DevOpsTools.get_most_recent_deployment("ERC1967Proxy", block.chainid); 12 | 13 | vm.startBroadcast(); 14 | CredV2 newCred = new CredV2(); 15 | vm.stopBroadcast(); 16 | address proxy = upgradeCred(mostRecentlyDeployedProxy, address(newCred)); 17 | return proxy; 18 | } 19 | 20 | function upgradeCred(address proxyAddress, address newCred) public returns (address) { 21 | Cred credProxy = Cred(payable(proxyAddress)); 22 | credProxy.upgradeToAndCall(address(newCred), abi.encodeWithSelector(CredV2.initializeV2.selector)); 23 | return address(credProxy); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /slither.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "filter_paths": "lib|node_modules|test|Mock*|Test*", 3 | "compile_force_framework": "foundry", 4 | "exclude_informational": true 5 | } 6 | -------------------------------------------------------------------------------- /src/abstract/Claimable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.25; 3 | 4 | import { IPhiFactory } from "../interfaces/IPhiFactory.sol"; 5 | 6 | abstract contract Claimable { 7 | /*////////////////////////////////////////////////////////////// 8 | ERRORS 9 | //////////////////////////////////////////////////////////////*/ 10 | error InvalidMerkleClaimData(); 11 | 12 | /*////////////////////////////////////////////////////////////// 13 | INTERNAL FUNCTIONS 14 | //////////////////////////////////////////////////////////////*/ 15 | function getPhiFactoryContract() public view virtual returns (IPhiFactory); 16 | function getFactoryArtId(uint256 tokenId) public view virtual returns (uint256); 17 | 18 | /*////////////////////////////////////////////////////////////// 19 | EXTERNAL FUNCTIONS 20 | //////////////////////////////////////////////////////////////*/ 21 | /// @notice Processes a Signature claim. 22 | function signatureClaim() external payable { 23 | ( 24 | bytes32 r_, 25 | bytes32 vs_, 26 | address ref_, 27 | address verifier_, 28 | address minter_, 29 | uint256 tokenId_, 30 | uint256 quantity_, 31 | uint256 expiresIn_, 32 | string memory imageURI_, 33 | bytes32 data_ 34 | ) = abi.decode( 35 | msg.data[4:], (bytes32, bytes32, address, address, address, uint256, uint256, uint256, string, bytes32) 36 | ); 37 | uint256 artId = getFactoryArtId(tokenId_); 38 | bytes memory claimData_ = abi.encode(expiresIn_, minter_, ref_, verifier_, artId, block.chainid, data_); 39 | bytes memory signature = abi.encodePacked(r_, vs_); 40 | 41 | IPhiFactory phiFactoryContract = getPhiFactoryContract(); 42 | IPhiFactory.MintArgs memory mintArgs_ = IPhiFactory.MintArgs(tokenId_, quantity_, imageURI_); 43 | phiFactoryContract.signatureClaim{ value: msg.value }(signature, claimData_, mintArgs_); 44 | } 45 | 46 | /// @notice Processes a merkle claim. 47 | function merkleClaim() external payable { 48 | ( 49 | address minter, 50 | bytes32[] memory proof, 51 | address ref, 52 | uint256 tokenId, 53 | uint256 quantity, 54 | bytes32 leafPart, 55 | string memory imageURI 56 | ) = _decodeMerkleClaimData(); 57 | 58 | uint256 artId = getFactoryArtId(tokenId); 59 | IPhiFactory phiFactory = getPhiFactoryContract(); 60 | 61 | bytes memory claimData = abi.encode(minter, ref, artId); 62 | 63 | IPhiFactory.MintArgs memory mintArgs = IPhiFactory.MintArgs(tokenId, quantity, imageURI); 64 | phiFactory.merkleClaim{ value: msg.value }(proof, claimData, mintArgs, leafPart); 65 | } 66 | 67 | function _decodeMerkleClaimData() 68 | private 69 | pure 70 | returns ( 71 | address minter, 72 | bytes32[] memory proof, 73 | address ref, 74 | uint256 tokenId, 75 | uint256 quantity, 76 | bytes32 leafPart, 77 | string memory imageURI 78 | ) 79 | { 80 | if (msg.data.length < 260) revert InvalidMerkleClaimData(); 81 | 82 | (minter, proof, ref, tokenId, quantity, leafPart, imageURI) = 83 | abi.decode(msg.data[4:], (address, bytes32[], address, uint256, uint256, bytes32, string)); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/abstract/CreatorRoyaltiesControl.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.25; 3 | 4 | import { ICreatorRoyaltiesControl } from "../interfaces/ICreatorRoyaltiesControl.sol"; 5 | import { IERC2981 } from "@openzeppelin/contracts/interfaces/IERC2981.sol"; 6 | 7 | /// @title CreatorRoyaltiesControl 8 | /// @notice Contract for managing the royalties of an ERC1155 contract 9 | abstract contract CreatorRoyaltiesControl is ICreatorRoyaltiesControl { 10 | mapping(uint256 _tokenId => RoyaltyConfiguration _configuration) public royalties; 11 | uint256 private constant ROYALTY_BPS_TO_PERCENT = 10_000; 12 | address private royaltyRecipient; 13 | bool private initilaized; 14 | 15 | error InvalidRoyaltyRecipient(); 16 | 17 | function initializeRoyalties(address _royaltyRecipient) internal { 18 | if (_royaltyRecipient == address(0)) revert InvalidRoyaltyRecipient(); 19 | if (initilaized) revert AlreadyInitialized(); 20 | royaltyRecipient = _royaltyRecipient; 21 | initilaized = true; 22 | } 23 | 24 | /// @notice The royalty information for a given token. 25 | /// @param tokenId The token ID to get the royalty information for. 26 | /// @return The royalty configuration for the token. 27 | function getRoyalties(uint256 tokenId) public view returns (RoyaltyConfiguration memory) { 28 | if (!initilaized) revert NotInitialized(); 29 | RoyaltyConfiguration memory config = royalties[tokenId]; 30 | if (config.royaltyRecipient != address(0)) { 31 | return config; 32 | } 33 | // Return default configuration 34 | return RoyaltyConfiguration({ royaltyBPS: 500, royaltyRecipient: royaltyRecipient }); 35 | } 36 | 37 | /// @notice Returns the royalty information for a given token. 38 | /// @param tokenId The token ID to get the royalty information for. 39 | /// @param salePrice The sale price of the NFT asset specified by tokenId 40 | /// @return receiver The address of the royalty recipient 41 | /// @return royaltyAmount The royalty amount to be paid 42 | function royaltyInfo( 43 | uint256 tokenId, 44 | uint256 salePrice 45 | ) 46 | public 47 | view 48 | returns (address receiver, uint256 royaltyAmount) 49 | { 50 | RoyaltyConfiguration memory config = getRoyalties(tokenId); 51 | royaltyAmount = (config.royaltyBPS * salePrice) / ROYALTY_BPS_TO_PERCENT; 52 | receiver = config.royaltyRecipient; 53 | } 54 | 55 | /// @notice Updates the royalties for a given token 56 | /// @param tokenId The token ID to update royalties for 57 | /// @param configuration The new royalty configuration 58 | function _updateRoyalties(uint256 tokenId, RoyaltyConfiguration memory configuration) internal { 59 | if (configuration.royaltyRecipient == address(0) && configuration.royaltyBPS > 0) { 60 | revert InvalidRoyaltyRecipient(); 61 | } 62 | 63 | royalties[tokenId] = configuration; 64 | 65 | emit UpdatedRoyalties(tokenId, msg.sender, configuration); 66 | } 67 | 68 | /// @notice Checks if the contract supports a given interface 69 | /// @param interfaceId The interface identifier 70 | /// @return true if the contract supports the interface, false otherwise 71 | function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) { 72 | return interfaceId == type(IERC2981).interfaceId; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/abstract/RewardControl.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.25; 3 | 4 | import { EIP712 } from "solady/utils/EIP712.sol"; 5 | import { IRewards } from "../interfaces/IRewards.sol"; 6 | import { SignatureCheckerLib } from "solady/utils/SignatureCheckerLib.sol"; 7 | import { SafeTransferLib } from "solady/utils/SafeTransferLib.sol"; 8 | 9 | /// @title RewardControl 10 | /// @notice Abstract contract for managing rewards 11 | abstract contract RewardControl is IRewards, EIP712 { 12 | /*////////////////////////////////////////////////////////////// 13 | USING 14 | //////////////////////////////////////////////////////////////*/ 15 | using SafeTransferLib for address; 16 | 17 | /*////////////////////////////////////////////////////////////// 18 | STORAGE 19 | //////////////////////////////////////////////////////////////*/ 20 | uint256 private constant FULL_BALANCE = 0; 21 | 22 | /// @dev The EIP-712 typehash for gasless withdraws 23 | bytes32 public constant WITHDRAW_TYPEHASH = 24 | keccak256("Withdraw(address from,address to,uint256 amount,uint256 nonce,uint256 deadline)"); 25 | 26 | /// @dev An account's nonce for gasless withdraws 27 | mapping(address account => uint256 nonce) public nonces; 28 | 29 | /// @dev An account's balance 30 | mapping(address account => uint256 balance) public balanceOf; 31 | 32 | /*////////////////////////////////////////////////////////////// 33 | UPDATE FUNCTIONS 34 | //////////////////////////////////////////////////////////////*/ 35 | /// @notice Deposits ETH for a recipient, with an optional comment 36 | /// @param to Address to deposit to 37 | /// @param reason System reason for deposit (used for indexing) 38 | /// @param comment Optional comment as reason for deposit 39 | function deposit(address to, bytes4 reason, string calldata comment) external payable { 40 | if (to == address(0)) revert InvalidAddressZero(); 41 | 42 | unchecked { 43 | balanceOf[to] += msg.value; 44 | } 45 | 46 | emit Deposit(msg.sender, to, reason, msg.value, comment); 47 | } 48 | 49 | /// @notice Deposits ETH for multiple recipients, with an optional comment 50 | /// @param recipients Recipients to send the amounts to, array aligns with amounts 51 | /// @param amounts Amounts to send to each recipient, array aligns with recipients 52 | /// @param reasons Optional bytes4 hashes for indexing 53 | /// @param comment Optional comment to include with deposit 54 | function depositBatch( 55 | address[] calldata recipients, 56 | uint256[] calldata amounts, 57 | bytes4[] calldata reasons, 58 | string calldata comment 59 | ) 60 | external 61 | payable 62 | { 63 | uint256 numRecipients = recipients.length; 64 | 65 | if (numRecipients != amounts.length || numRecipients != reasons.length) { 66 | revert ArrayLengthMismatch(); 67 | } 68 | 69 | uint256 expectedTotalValue; 70 | for (uint256 i = 0; i < numRecipients; i++) { 71 | expectedTotalValue += amounts[i]; 72 | } 73 | 74 | if (msg.value != expectedTotalValue) { 75 | revert InvalidDeposit(); 76 | } 77 | 78 | for (uint256 i = 0; i < numRecipients; i++) { 79 | address recipient = recipients[i]; 80 | uint256 amount = amounts[i]; 81 | 82 | if (recipient == address(0)) { 83 | revert InvalidAddressZero(); 84 | } 85 | 86 | balanceOf[recipient] += amount; 87 | 88 | emit Deposit(msg.sender, recipient, reasons[i], amount, comment); 89 | } 90 | } 91 | 92 | function withdraw(address to, uint256 amount) external { 93 | _withdraw(msg.sender, to, amount); 94 | } 95 | 96 | function withdrawFor(address from, uint256 amount) external { 97 | _withdraw(from, from, amount); 98 | } 99 | 100 | function withdrawWithSig(address from, address to, uint256 amount, uint256 deadline, bytes calldata sig) external { 101 | if (block.timestamp > deadline) revert DeadlineExpired(); 102 | if (!_verifySignature(from, to, amount, nonces[from], deadline, sig)) revert InvalidSignature(); 103 | 104 | unchecked { 105 | ++nonces[from]; 106 | } 107 | 108 | _withdraw(from, to, amount); 109 | } 110 | 111 | /*////////////////////////////////////////////////////////////// 112 | VIEW FUNCTIONS 113 | //////////////////////////////////////////////////////////////*/ 114 | /// @notice Returns the total amount of ETH held in the contract 115 | function totalSupply() external view returns (uint256) { 116 | return address(this).balance; 117 | } 118 | 119 | /*////////////////////////////////////////////////////////////// 120 | INTERNAL FUNCTIONS 121 | //////////////////////////////////////////////////////////////*/ 122 | function _withdraw(address from, address to, uint256 amount) internal { 123 | if (to == address(0)) revert InvalidAddressZero(); 124 | 125 | uint256 balance = balanceOf[from]; 126 | if (amount == FULL_BALANCE) { 127 | amount = balance; 128 | } 129 | 130 | if (amount > balance) revert InvalidAmount(); 131 | 132 | unchecked { 133 | balanceOf[from] = balance - amount; 134 | } 135 | 136 | emit Withdraw(from, to, amount); 137 | 138 | to.safeTransferETH(amount); 139 | } 140 | 141 | function _domainNameAndVersion() internal pure override returns (string memory name, string memory version) { 142 | return ("PHI Rewards", "1"); 143 | } 144 | 145 | /// @dev Verifies EIP-712 `Mint` signature 146 | function _verifySignature( 147 | address from, 148 | address to, 149 | uint256 amount, 150 | uint256 nonce, 151 | uint256 deadline, 152 | bytes calldata sig 153 | ) 154 | internal 155 | view 156 | returns (bool) 157 | { 158 | bytes32 structHash = keccak256(abi.encode(WITHDRAW_TYPEHASH, from, to, amount, nonce, deadline)); 159 | bytes32 digest = _hashTypedData(structHash); 160 | return SignatureCheckerLib.isValidSignatureNowCalldata(from, digest, sig); 161 | } 162 | 163 | /// @dev EIP-712 helper 164 | function hashTypedData(bytes32 structHash) public view returns (bytes32) { 165 | return _hashTypedData(structHash); 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /src/curve/BondingCurve.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.25; 3 | 4 | import { Ownable2Step } from "@openzeppelin/contracts/access/Ownable2Step.sol"; 5 | import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; 6 | import { IBondingCurve } from "../interfaces/IBondingCurve.sol"; 7 | import { ICred } from "../interfaces/ICred.sol"; 8 | 9 | import { console2 } from "forge-std/console2.sol"; 10 | /// @title bondingCurve 11 | 12 | contract BondingCurve is Ownable2Step, IBondingCurve { 13 | /*////////////////////////////////////////////////////////////// 14 | STORAGE 15 | //////////////////////////////////////////////////////////////*/ 16 | ICred public credContract; 17 | 18 | uint256 private constant TOTAL_SUPPLY_FACTOR = 1000 ether; 19 | uint256 private constant CURVE_FACTOR = 10; 20 | uint256 private constant INITIAL_PRICE_FACTOR = 9; 21 | uint256 private immutable RATIO_BASE = 10_000; 22 | 23 | /*////////////////////////////////////////////////////////////// 24 | CONSTRUCTOR 25 | //////////////////////////////////////////////////////////////*/ 26 | /// @notice Initializes the contract with the given owner address. 27 | constructor(address owner_) Ownable(owner_) { } 28 | 29 | /*////////////////////////////////////////////////////////////// 30 | SETTERS 31 | //////////////////////////////////////////////////////////////*/ 32 | /// @notice Sets the address of the cred contract. 33 | /// @param credContract_ The address of the cred contract. 34 | function setCredContract(address credContract_) external onlyOwner { 35 | credContract = ICred(credContract_); 36 | } 37 | 38 | /*////////////////////////////////////////////////////////////// 39 | EXTERNAL VIEW 40 | //////////////////////////////////////////////////////////////*/ 41 | /// @notice Gets the address of the cred contract. 42 | /// @return The address of the cred contract. 43 | function getCredContract() external view returns (address) { 44 | return address(credContract); 45 | } 46 | 47 | function getPrice(uint256 supply_, uint256 amount_) public pure returns (uint256) { 48 | return _curve((supply_ + amount_) * 1 ether) - _curve(supply_ * 1 ether); 49 | } 50 | 51 | function getPriceData( 52 | uint256 credId_, 53 | uint256 supply_, 54 | uint256 amount_, 55 | bool isSign_ 56 | ) 57 | public 58 | view 59 | returns (uint256 price, uint256 protocolFee, uint256 creatorFee) 60 | { 61 | (uint16 buyShareRoyalty, uint16 sellShareRoyalty) = credContract.getCreatorRoyalty(credId_); 62 | 63 | price = isSign_ ? getPrice(supply_, amount_) : getPrice(supply_ - amount_, amount_); 64 | 65 | protocolFee = _getProtocolFee(price); 66 | if (supply_ == 0) { 67 | creatorFee = 0; 68 | return (price, protocolFee, creatorFee); 69 | } 70 | uint16 royaltyRate = isSign_ ? buyShareRoyalty : sellShareRoyalty; 71 | creatorFee = (price * royaltyRate) / RATIO_BASE; 72 | } 73 | 74 | /// @notice Calculates the buy price for a given supply and amount. 75 | /// @param supply_ The current supply. 76 | /// @param amount_ The amount to calculate the buy price for. 77 | /// @return The calculated buy price. 78 | function getBuyPrice(uint256 supply_, uint256 amount_) public pure returns (uint256) { 79 | return getPrice(supply_, amount_); 80 | } 81 | 82 | /// @notice Calculates the buy price after fees for a given supply and amount. 83 | /// @param supply_ The current supply. 84 | /// @param amount_ The amount to calculate the buy price for. 85 | /// @return The calculated buy price after fees. 86 | function getBuyPriceAfterFee(uint256 credId_, uint256 supply_, uint256 amount_) public view returns (uint256) { 87 | uint256 price = getBuyPrice(supply_, amount_); 88 | uint256 protocolFee = _getProtocolFee(price); 89 | uint256 creatorFee = _getCreatorFee(credId_, supply_, price, true); 90 | 91 | return price + protocolFee + creatorFee; 92 | } 93 | 94 | /// @notice Calculates the sell price for a given supply and amount. 95 | /// @param supply_ The current supply. 96 | /// @param amount_ The amount to calculate the sell price for. 97 | /// @return The calculated sell price. 98 | function getSellPrice(uint256 supply_, uint256 amount_) public pure returns (uint256) { 99 | return getPrice(supply_ - amount_, amount_); 100 | } 101 | 102 | /// @notice Calculates the sell price after fees for a given supply and amount. 103 | /// @param supply_ The current supply. 104 | /// @param amount_ The amount to calculate the sell price for. 105 | /// @return The calculated sell price after fees. 106 | function getSellPriceAfterFee(uint256 credId_, uint256 supply_, uint256 amount_) public view returns (uint256) { 107 | uint256 price = getSellPrice(supply_, amount_); 108 | uint256 protocolFee = _getProtocolFee(price); 109 | uint256 creatorFee = _getCreatorFee(credId_, supply_, price, false); 110 | return price - protocolFee - creatorFee; 111 | } 112 | 113 | /*////////////////////////////////////////////////////////////// 114 | INTERNAL 115 | //////////////////////////////////////////////////////////////*/ 116 | /// @dev Returns the curve of `targetAmount` 117 | function _curve(uint256 targetAmount_) private pure returns (uint256) { 118 | return (TOTAL_SUPPLY_FACTOR * CURVE_FACTOR * 1 ether) / (TOTAL_SUPPLY_FACTOR - targetAmount_) 119 | - CURVE_FACTOR * 1 ether - INITIAL_PRICE_FACTOR * targetAmount_ / 1000; 120 | } 121 | 122 | /// @dev Returns the protocol fee. 123 | function _getProtocolFee(uint256 price_) internal view returns (uint256) { 124 | return price_ * credContract.protocolFeePercent() / RATIO_BASE; 125 | } 126 | 127 | /// @dev Returns the creator fee. 128 | function _getCreatorFee( 129 | uint256 credId_, 130 | uint256 supply_, 131 | uint256 price_, 132 | bool isSign_ 133 | ) 134 | internal 135 | view 136 | returns (uint256 creatorFee) 137 | { 138 | if (!credContract.isExist(credId_)) { 139 | return 0; 140 | } 141 | if (supply_ == 0) { 142 | creatorFee = 0; 143 | } 144 | 145 | (uint16 buyShareRoyalty, uint16 sellShareRoyalty) = credContract.getCreatorRoyalty(credId_); 146 | 147 | uint16 royaltyRate = isSign_ ? buyShareRoyalty : sellShareRoyalty; 148 | creatorFee = (price_ * royaltyRate) / RATIO_BASE; 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /src/interfaces/IBondingCurve.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.25; 3 | 4 | interface IBondingCurve { 5 | /*////////////////////////////////////////////////////////////// 6 | ERRORS 7 | //////////////////////////////////////////////////////////////*/ 8 | /// @notice Error thrown when an invalid zero address is provided. 9 | error InvalidAddressZero(); 10 | error InvalidSupply(); 11 | 12 | /*////////////////////////////////////////////////////////////// 13 | EVENTS 14 | //////////////////////////////////////////////////////////////*/ 15 | // (no events) 16 | 17 | /*////////////////////////////////////////////////////////////// 18 | EXTERNAL FUNCTIONS 19 | //////////////////////////////////////////////////////////////*/ 20 | /// @notice Sets the address of the cred contract. 21 | /// @param credContract_ The address of the cred contract. 22 | function setCredContract(address credContract_) external; 23 | 24 | /// @notice Gets the address of the cred contract. 25 | /// @return The address of the cred contract. 26 | function getCredContract() external view returns (address); 27 | 28 | function getPriceData( 29 | uint256 credId_, 30 | uint256 supply_, 31 | uint256 amount_, 32 | bool isSign_ 33 | ) 34 | external 35 | view 36 | returns (uint256 price, uint256 protcolFee, uint256 creatorFee); 37 | 38 | /// @notice Calculates the price for a given supply and amount. 39 | /// @param supply_ The current supply. 40 | /// @param amount_ The amount to calculate the price for. 41 | /// @return The calculated price. 42 | function getPrice(uint256 supply_, uint256 amount_) external pure returns (uint256); 43 | 44 | /// @notice Calculates the buy price after fees for a given supply and amount. 45 | /// @param supply_ The current supply. 46 | /// @param amount_ The amount to calculate the buy price for. 47 | /// @return The calculated buy price after fees. 48 | function getBuyPriceAfterFee(uint256 credId_, uint256 supply_, uint256 amount_) external view returns (uint256); 49 | 50 | /// @notice Calculates the sell price after fees for a given supply and amount. 51 | /// @param supply_ The current supply. 52 | /// @param amount_ The amount to calculate the sell price for. 53 | /// @return The calculated sell price after fees. 54 | function getSellPriceAfterFee(uint256 credId_, uint256 supply_, uint256 amount_) external view returns (uint256); 55 | } 56 | -------------------------------------------------------------------------------- /src/interfaces/IContributeRewards.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.25; 3 | 4 | import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5 | 6 | interface IContributeRewards { 7 | /*////////////////////////////////////////////////////////////// 8 | ERRORS 9 | //////////////////////////////////////////////////////////////*/ 10 | error InvalidProof(); 11 | error TokensAlreadyClaimed(uint256 index); 12 | error CredDoesNotExist(uint256 credId); 13 | error Unauthorized(address caller); 14 | error TransferFailed(); 15 | error ClaimPeriodEnded(); 16 | error ExceedsTotalRewardAmount(); 17 | error SweepFailed(); 18 | error RewardAlreadyClosed(); 19 | error RewardClosed(); 20 | error NotMinted(); 21 | error Reentrancy(); 22 | 23 | /*////////////////////////////////////////////////////////////// 24 | EVENTS 25 | //////////////////////////////////////////////////////////////*/ 26 | event Claim(uint256 indexed credId, uint256 indexed rewardId, address indexed claimant, uint256 amount); 27 | event RewardInfoSet( 28 | address indexed setter, 29 | uint256 indexed credId, 30 | uint256 indexed rewardId, 31 | uint256 claimPeriodEnds, 32 | bytes32 merkleRoot, 33 | address rewardToken, 34 | uint256 totalRewardAmount 35 | ); 36 | event RewardClosedAndSwept( 37 | uint256 indexed credId, uint256 indexed rewardId, address indexed sweeper, uint256 amount 38 | ); 39 | 40 | /*////////////////////////////////////////////////////////////// 41 | STRUCTS 42 | //////////////////////////////////////////////////////////////*/ 43 | struct RewardInfo { 44 | uint256 claimPeriodEnds; 45 | bytes32 merkleRoot; 46 | IERC20 rewardToken; 47 | uint256 totalRewardAmount; 48 | uint256 claimedAmount; 49 | address rewardSetter; 50 | bool isCheckMinted; 51 | bool isOpen; 52 | } 53 | 54 | /*////////////////////////////////////////////////////////////// 55 | FUNCTIONS 56 | //////////////////////////////////////////////////////////////*/ 57 | function setRewardInfo( 58 | uint256 credId, 59 | uint256 claimPeriodEnds, 60 | bytes32 merkleRoot, 61 | address rewardToken, 62 | uint256 totalRewardAmount, 63 | bool isCheckMinted 64 | ) 65 | external; 66 | 67 | function claimReward(uint256 credId, uint256 rewardId, uint256 amount, bytes32[] calldata merkleProof) external; 68 | 69 | function closeAndSweep(uint256 credId, uint256 rewardId) external; 70 | 71 | function getRewardInfo( 72 | uint256 credId, 73 | uint256 rewardId 74 | ) 75 | external 76 | view 77 | returns ( 78 | uint256 claimPeriodEnds, 79 | bytes32 merkleRoot, 80 | address rewardToken, 81 | uint256 totalRewardAmount, 82 | uint256 claimedAmount, 83 | address rewardSetter, 84 | bool isOpen 85 | ); 86 | 87 | function isClaimed(uint256 credId, uint256 rewardId, uint256 index) external view returns (bool); 88 | } 89 | -------------------------------------------------------------------------------- /src/interfaces/ICreatorRoyaltiesControl.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.25; 3 | 4 | import { IERC2981 } from "@openzeppelin/contracts/interfaces/IERC2981.sol"; 5 | 6 | interface ICreatorRoyaltiesControl is IERC2981 { 7 | /*////////////////////////////////////////////////////////////// 8 | ERRORS 9 | //////////////////////////////////////////////////////////////*/ 10 | error InvalidMintSchedule(); 11 | error AlreadyInitialized(); 12 | error NotInitialized(); 13 | 14 | /*////////////////////////////////////////////////////////////// 15 | EVENTS 16 | //////////////////////////////////////////////////////////////*/ 17 | /// @notice Event emitted when royalties are updated 18 | event UpdatedRoyalties(uint256 indexed tokenId, address indexed user, RoyaltyConfiguration configuration); 19 | 20 | /*////////////////////////////////////////////////////////////// 21 | STRUCTS 22 | //////////////////////////////////////////////////////////////*/ 23 | /// @notice The RoyaltyConfiguration struct is used to store the royalty configuration for a given token. 24 | /// @param royaltyMintSchedule Every nth token will go to the royalty recipient. 25 | /// @param royaltyBPS The royalty amount in basis points for secondary sales. 26 | /// @param royaltyRecipient The address that will receive the royalty payments. 27 | struct RoyaltyConfiguration { 28 | uint32 royaltyBPS; 29 | address royaltyRecipient; 30 | } 31 | 32 | /*////////////////////////////////////////////////////////////// 33 | EXTERNAL FUNCTIONS 34 | //////////////////////////////////////////////////////////////*/ 35 | /// @notice External data getter to get royalties for a token 36 | /// @param tokenId tokenId to get royalties configuration for 37 | function getRoyalties(uint256 tokenId) external view returns (RoyaltyConfiguration memory); 38 | } 39 | -------------------------------------------------------------------------------- /src/interfaces/ICuratorRewardsDistributor.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.25; 3 | 4 | interface ICuratorRewardsDistributor { 5 | /*////////////////////////////////////////////////////////////// 6 | ERRORS 7 | //////////////////////////////////////////////////////////////*/ 8 | error InvalidAddressZero(); 9 | error NoBalanceToDistribute(); 10 | error NoSharesToDistribute(); 11 | error InvalidTokenAmounts(uint256 gotAmounts); 12 | error InvalidValue(uint256 gotValue, uint256 expectedValue); 13 | error UnauthorizedCaller(address caller); 14 | error Reentrancy(); 15 | error InvalidRoyalty(uint256 royalty); 16 | error InvalidCredId(); 17 | 18 | /*////////////////////////////////////////////////////////////// 19 | EVENTS 20 | //////////////////////////////////////////////////////////////*/ 21 | event Deposit(uint256 indexed credId, uint256 amount); 22 | event RewardsDistributed( 23 | uint256 indexed credId, address indexed sender, uint256 royaltyfee, uint256 distributeAmount, uint256 total 24 | ); 25 | event RewardsSweeped(uint256 indexed credId, address indexed sender, uint256 total); 26 | event PhiRewardsContractUpdated(address newPhiRewardsContract); 27 | event RoyaltyUpdated(uint256 newRoyalty); 28 | 29 | /*////////////////////////////////////////////////////////////// 30 | EXTERNAL FUNCTIONS 31 | //////////////////////////////////////////////////////////////*/ 32 | function balanceOf(uint256 credId) external view returns (uint256); 33 | /// @notice Deposits an amount of ETH into the contract. 34 | /// @param credId The ID of the cred. 35 | /// @param amount The amount of ETH to deposit. 36 | function deposit(uint256 credId, uint256 amount) external payable; 37 | /// @notice Distributes the rewards to the addresses. 38 | /// @param credId The ID of the cred. 39 | function distribute(uint256 credId) external; 40 | } 41 | -------------------------------------------------------------------------------- /src/interfaces/IOwnable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.25; 3 | 4 | interface IOwnable { 5 | /*////////////////////////////////////////////////////////////// 6 | EVENTS 7 | //////////////////////////////////////////////////////////////*/ 8 | event OwnershipTransferred(address indexed oldOwner, address indexed newOwner); 9 | event OwnershipHandoverRequested(address indexed pendingOwner); 10 | event OwnershipHandoverCanceled(address indexed pendingOwner); 11 | 12 | /*////////////////////////////////////////////////////////////// 13 | EXTERNAL FUNCTIONS 14 | //////////////////////////////////////////////////////////////*/ 15 | // Update functions 16 | function transferOwnership(address newOwner) external payable; 17 | function renounceOwnership() external payable; 18 | function requestOwnershipHandover() external payable; 19 | function cancelOwnershipHandover() external payable; 20 | function completeOwnershipHandover(address pendingOwner) external payable; 21 | 22 | // Read functions 23 | function owner() external view returns (address); 24 | function ownershipHandoverExpiresAt(address pendingOwner) external view returns (uint256); 25 | } 26 | -------------------------------------------------------------------------------- /src/interfaces/IPhiNFT1155.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.25; 3 | 4 | import { ICreatorRoyaltiesControl } from "./ICreatorRoyaltiesControl.sol"; 5 | 6 | interface IPhiNFT1155 is ICreatorRoyaltiesControl { 7 | /*////////////////////////////////////////////////////////////// 8 | ERRORS 9 | //////////////////////////////////////////////////////////////*/ 10 | error TokenNotTransferable(); 11 | error InsufficientTokenBalance(); 12 | error NotStarted(); 13 | error NotEnded(); 14 | error NotPhiFactory(); 15 | error AddressNotSigned(); 16 | error AddressAlreadyMinted(); 17 | error OverMaxAllowedToMint(); 18 | error NotArtCreator(); 19 | error InValdidTokenId(); 20 | 21 | /*////////////////////////////////////////////////////////////// 22 | EVENTS 23 | //////////////////////////////////////////////////////////////*/ 24 | event ArtClaimedData( 25 | address indexed recipient, 26 | address indexed artistRewardReceiver, 27 | address indexed referrer, 28 | address verifier, 29 | uint256 artId, 30 | uint256 tokenId, 31 | uint256 amount, 32 | bytes32 data 33 | ); 34 | event InitializePhiNFT1155(uint256 credId, string verificationType); 35 | event MintComment(address indexed to, address from, uint256 tokenId, string tokenURI); 36 | event ArtCreated(uint256 artId, uint256 tokenId); 37 | event ContractURIUpdated(); 38 | 39 | /*////////////////////////////////////////////////////////////// 40 | EXTERNAL FUNCTIONS 41 | //////////////////////////////////////////////////////////////*/ 42 | // Initializer/Constructor Function 43 | function initialize( 44 | uint256 credChainId, 45 | uint256 credId, 46 | string memory verificationType, 47 | address protocoFeeDest 48 | ) 49 | external; 50 | 51 | // Read Functions 52 | function tokenIdCounter() external view returns (uint256); 53 | function credId() external view returns (uint256); 54 | function getTokenIdFromFactoryArtId(uint256 artId) external view returns (uint256); 55 | function getFactoryArtId(uint256 tokenId_) external view returns (uint256); 56 | function verificationType() external view returns (string memory); 57 | 58 | // Update Functions 59 | function pause() external; 60 | function unPause() external; 61 | function updateRoyalties(uint256 tokenId, RoyaltyConfiguration memory configuration) external; 62 | } 63 | -------------------------------------------------------------------------------- /src/interfaces/IPhiNFT1155Ownable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.25; 3 | 4 | import { IOwnable } from "./IOwnable.sol"; 5 | import { IPhiNFT1155 } from "./IPhiNFT1155.sol"; 6 | 7 | // solhint-disable-next-line no-empty-blocks 8 | interface IPhiNFT1155Ownable is IPhiNFT1155, IOwnable { } 9 | -------------------------------------------------------------------------------- /src/interfaces/IPhiRewards.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.25; 3 | 4 | import { IRewards } from "./IRewards.sol"; 5 | 6 | interface IPhiRewards is IRewards { 7 | /*////////////////////////////////////////////////////////////// 8 | EVENTS 9 | //////////////////////////////////////////////////////////////*/ 10 | event ArtistRewardUpdated(uint256 artistReward); 11 | event ReferralRewardUpdated(uint256 referralReward); 12 | event VerifierRewardUpdated(uint256 verifierReward); 13 | event CurateRewardUpdated(uint256 curateReward); 14 | event CuratorRewardsDistributorUpdated(address curatorRewardsDistributor); 15 | event RewardsDeposit( 16 | bytes credData, 17 | address minter, 18 | address indexed receiver, 19 | address indexed referral, 20 | address indexed verifier, 21 | bytes rewardsData 22 | ); 23 | 24 | /*////////////////////////////////////////////////////////////// 25 | EXTERNAL FUNCTIONS 26 | //////////////////////////////////////////////////////////////*/ 27 | /// @notice Updates the curator rewards distributor address 28 | /// @param curatorRewardsDistributor_ The new curator rewards distributor address 29 | function updateCuratorRewardsDistributor(address curatorRewardsDistributor_) external; 30 | 31 | /// @notice Handles rewards and gets the value sent 32 | /// @param artId_ The art ID 33 | /// @param credId_ The credential ID 34 | /// @param quantity_ The quantity 35 | /// @param mintFee_ The minting fee 36 | /// @param addressesData_ The encoded addresses data (minter, receiver, referral, verifier) 37 | /// @param chainSync_ Whether to sync with the chain or not 38 | function handleRewardsAndGetValueSent( 39 | uint256 artId_, 40 | uint256 credId_, 41 | uint256 quantity_, 42 | uint256 mintFee_, 43 | bytes calldata addressesData_, 44 | bool chainSync_ 45 | ) 46 | external 47 | payable; 48 | 49 | /// @notice Computes the minting reward 50 | /// @param quantity_ The quantity 51 | /// @param mintFee_ The minting fee 52 | /// @return The computed minting reward 53 | function computeMintReward(uint256 quantity_, uint256 mintFee_) external view returns (uint256); 54 | } 55 | -------------------------------------------------------------------------------- /src/interfaces/IRewards.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.25; 3 | 4 | /// @title IRewards 5 | /// @notice The interface for deposits and withdrawals 6 | interface IRewards { 7 | /*////////////////////////////////////////////////////////////// 8 | ERRORS 9 | //////////////////////////////////////////////////////////////*/ 10 | /// @notice Thrown when trying to send to the zero address 11 | error InvalidAddressZero(); 12 | 13 | /// @notice Thrown when function argument array lengths mismatch 14 | error ArrayLengthMismatch(); 15 | 16 | /// @notice Thrown when an invalid deposit is made 17 | error InvalidDeposit(); 18 | 19 | /// @notice Thrown when an invalid signature is provided for a deposit 20 | error InvalidSignature(); 21 | 22 | /// @notice Thrown when an invalid withdrawal is attempted 23 | error InvalidWithdraw(); 24 | 25 | /// @notice Thrown when the signature for a withdrawal has expired 26 | error SignatureDeadlineExpired(); 27 | 28 | /// @notice Thrown when a low-level ETH transfer fails 29 | error TransferFailed(); 30 | 31 | error InvalidAmount(); 32 | 33 | error DeadlineExpired(); 34 | 35 | /*////////////////////////////////////////////////////////////// 36 | EVENTS 37 | //////////////////////////////////////////////////////////////*/ 38 | /// @notice Emitted when a deposit is made 39 | /// @param from The address making the deposit 40 | /// @param to The address receiving the deposit 41 | /// @param reason Optional bytes4 reason for indexing 42 | /// @param amount Amount of the deposit 43 | /// @param comment Optional user comment 44 | event Deposit(address indexed from, address indexed to, bytes4 indexed reason, uint256 amount, string comment); 45 | 46 | /// @notice Emitted when a withdrawal is made 47 | /// @param from The address making the withdrawal 48 | /// @param to The address receiving the withdrawal 49 | /// @param amount Amount of the withdrawal 50 | event Withdraw(address indexed from, address indexed to, uint256 amount); 51 | 52 | /*////////////////////////////////////////////////////////////// 53 | EXTERNAL FUNCTIONS 54 | //////////////////////////////////////////////////////////////*/ 55 | /// @notice Deposits ETH for a recipient, with an optional comment 56 | /// @param to Address to deposit to 57 | /// @param reason System reason for deposit (used for indexing) 58 | /// @param comment Optional comment as reason for deposit 59 | function deposit(address to, bytes4 reason, string calldata comment) external payable; 60 | 61 | /// @notice Deposits ETH for multiple recipients, with an optional comment 62 | /// @param recipients Recipients to send the amounts to, array aligns with amounts 63 | /// @param amounts Amounts to send to each recipient, array aligns with recipients 64 | /// @param reasons Optional bytes4 hashes for indexing 65 | /// @param comment Optional comment to include with deposit 66 | function depositBatch( 67 | address[] calldata recipients, 68 | uint256[] calldata amounts, 69 | bytes4[] calldata reasons, 70 | string calldata comment 71 | ) 72 | external 73 | payable; 74 | 75 | /// @notice Withdraws protocol rewards 76 | /// @param to Address to withdraw to 77 | /// @param amount Amount to withdraw 78 | function withdraw(address to, uint256 amount) external; 79 | 80 | /// @notice Withdraws rewards on behalf of an address 81 | /// @param to Address to withdraw for 82 | /// @param amount Amount to withdraw (0 for total balance) 83 | function withdrawFor(address to, uint256 amount) external; 84 | 85 | /// @notice Executes a withdrawal of protocol rewards via signature 86 | /// @param from Address to withdraw from 87 | /// @param to Address to withdraw to 88 | /// @param amount Amount to withdraw 89 | /// @param deadline Deadline for the signature to be valid 90 | /// @param sig Signature for the withdrawal 91 | function withdrawWithSig(address from, address to, uint256 amount, uint256 deadline, bytes calldata sig) external; 92 | 93 | /*////////////////////////////////////////////////////////////// 94 | VIEW FUNCTIONS 95 | //////////////////////////////////////////////////////////////*/ 96 | /// @notice Returns the total amount of ETH held in the contract 97 | function totalSupply() external view returns (uint256); 98 | } 99 | -------------------------------------------------------------------------------- /src/lib/FixedPriceBondingCurve.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.25; 3 | 4 | import { Ownable2Step } from "@openzeppelin/contracts/access/Ownable2Step.sol"; 5 | import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; 6 | import { IBondingCurve } from "../interfaces/IBondingCurve.sol"; 7 | import { ICred } from "../interfaces/ICred.sol"; 8 | 9 | /// JUST FOR TESTING CONTRACTS 10 | /// NO NEED TO CHECK THIS CONTRACT 11 | contract FixedPriceBondingCurve is Ownable2Step, IBondingCurve { 12 | ICred public credContract; 13 | uint256 public constant MAX_SUPPLY = 999; 14 | uint256 public constant FIXED_PRICE = 0.00001 ether; 15 | uint256 private immutable RATIO_BASE = 10_000; 16 | 17 | constructor(address owner_) Ownable(owner_) { } 18 | 19 | function setCredContract(address credContract_) external onlyOwner { 20 | if (credContract_ == address(0)) revert InvalidAddressZero(); 21 | credContract = ICred(credContract_); 22 | } 23 | 24 | function getCredContract() external view returns (address) { 25 | return address(credContract); 26 | } 27 | 28 | function getPriceData( 29 | uint256 credId_, 30 | uint256 supply_, 31 | uint256 amount_, 32 | bool isSign_ 33 | ) 34 | public 35 | view 36 | returns (uint256 price, uint256 protocolFee, uint256 creatorFee) 37 | { 38 | if (supply_ + amount_ > MAX_SUPPLY) revert InvalidSupply(); 39 | price = FIXED_PRICE * amount_; 40 | protocolFee = _getProtocolFee(price); 41 | (uint16 buyShareRoyalty, uint16 sellShareRoyalty) = credContract.getCreatorRoyalty(credId_); 42 | uint16 royaltyRate = isSign_ ? buyShareRoyalty : sellShareRoyalty; 43 | creatorFee = (price * royaltyRate) / RATIO_BASE; 44 | } 45 | 46 | function getPrice(uint256 supply_, uint256 amount_) public pure returns (uint256) { 47 | if (supply_ + amount_ > MAX_SUPPLY) revert InvalidSupply(); 48 | return FIXED_PRICE * amount_; 49 | } 50 | 51 | function getBuyPriceAfterFee(uint256 credId_, uint256 supply_, uint256 amount_) public view returns (uint256) { 52 | (uint256 price, uint256 protocolFee, uint256 creatorFee) = getPriceData(credId_, supply_, amount_, true); 53 | return price + protocolFee + creatorFee; 54 | } 55 | 56 | function getSellPriceAfterFee(uint256 credId_, uint256 supply_, uint256 amount_) public view returns (uint256) { 57 | (uint256 price, uint256 protocolFee, uint256 creatorFee) = getPriceData(credId_, supply_, amount_, false); 58 | return price - protocolFee - creatorFee; 59 | } 60 | 61 | function _getProtocolFee(uint256 price_) internal view returns (uint256) { 62 | return price_ * credContract.protocolFeePercent() / RATIO_BASE; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/lib/Logo.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.25; 3 | 4 | /* 5 | ____ ____ 6 | /\___\ /\___\ 7 | ________/ / /_ \/___/ 8 | /\_______\/ /__\___\ 9 | / / / / / 10 | / / / / / / / 11 | / / /___/___/___/___/ 12 | / / / 13 | \/___/ 14 | 15 | */ 16 | 17 | interface Logo { 18 | // This is a simple Logo image 19 | } 20 | -------------------------------------------------------------------------------- /src/lib/MerkleProof.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Modified from 3 | // https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.3.0/contracts/utils/cryptography/MerkleProof.sol 4 | 5 | pragma solidity ^0.8.23; 6 | 7 | /** 8 | * @dev These functions deal with verification of Merkle Trees proofs. 9 | * 10 | * The proofs can be generated using the JavaScript library 11 | * https://github.com/miguelmota/merkletreejs[merkletreejs]. 12 | * Note: the hashing algorithm should be keccak256 and pair sorting should be enabled. 13 | * 14 | * See `test/utils/cryptography/MerkleProof.test.js` for some examples. 15 | */ 16 | library MerkleProof { 17 | /** 18 | * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree 19 | * defined by `root`. For this, a `proof` must be provided, containing 20 | * sibling hashes on the branch from the leaf to the root of the tree. Each 21 | * pair of leaves and each pair of pre-images are assumed to be sorted. 22 | */ 23 | function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf) internal pure returns (bool, uint256) { 24 | bytes32 computedHash = leaf; 25 | uint256 index = 0; 26 | 27 | for (uint256 i = 0; i < proof.length; i++) { 28 | index *= 2; 29 | bytes32 proofElement = proof[i]; 30 | 31 | if (computedHash <= proofElement) { 32 | // Hash(current computed hash + current element of the proof) 33 | computedHash = keccak256(abi.encodePacked(computedHash, proofElement)); 34 | } else { 35 | // Hash(current element of the proof + current computed hash) 36 | computedHash = keccak256(abi.encodePacked(proofElement, computedHash)); 37 | index += 1; 38 | } 39 | } 40 | 41 | // Check if the computed hash (root) is equal to the provided root 42 | return (computedHash == root, index); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/reward/CuratorRewardsDistributor.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.25; 3 | 4 | import { Logo } from "../lib/Logo.sol"; 5 | import { ICuratorRewardsDistributor } from "../interfaces/ICuratorRewardsDistributor.sol"; 6 | import { IPhiRewards } from "../interfaces/IPhiRewards.sol"; 7 | import { ICred } from "../interfaces/ICred.sol"; 8 | import { SafeTransferLib } from "solady/utils/SafeTransferLib.sol"; 9 | import { Ownable2Step } from "@openzeppelin/contracts/access/Ownable2Step.sol"; 10 | import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; 11 | 12 | import { console2 } from "forge-std/console2.sol"; 13 | /// @title CuratorRewardsDistributor 14 | /// @notice Manager of deposits & withdrawals for curator rewards 15 | /// @dev This contract is deployed to same network as the cred contract 16 | 17 | contract CuratorRewardsDistributor is Logo, Ownable2Step, ICuratorRewardsDistributor { 18 | /*////////////////////////////////////////////////////////////// 19 | USING 20 | //////////////////////////////////////////////////////////////*/ 21 | using SafeTransferLib for address; 22 | 23 | /*////////////////////////////////////////////////////////////// 24 | STORAGE 25 | //////////////////////////////////////////////////////////////*/ 26 | IPhiRewards public phiRewardsContract; 27 | ICred public credContract; 28 | 29 | uint256 private withdrawRoyalty = 100; 30 | uint256 private immutable RATIO_BASE = 10_000; 31 | uint256 private immutable MAX_ROYALTY_RANGE = 1000; 32 | mapping(uint256 credId => uint256 balance) public balanceOf; 33 | 34 | /*////////////////////////////////////////////////////////////// 35 | CONSTRUCTOR 36 | //////////////////////////////////////////////////////////////*/ 37 | constructor(address phiRewardsContract_, address credContract_) payable Ownable(msg.sender) { 38 | if (phiRewardsContract_ == address(0) || credContract_ == address(0)) { 39 | revert InvalidAddressZero(); 40 | } 41 | 42 | phiRewardsContract = IPhiRewards(phiRewardsContract_); 43 | credContract = ICred(credContract_); 44 | } 45 | 46 | /*////////////////////////////////////////////////////////////// 47 | SETTER FUNCTIONS 48 | //////////////////////////////////////////////////////////////*/ 49 | function updatePhiRewardsContract(address phiRewardsContract_) external onlyOwner { 50 | if (phiRewardsContract_ == address(0)) { 51 | revert InvalidAddressZero(); 52 | } 53 | phiRewardsContract = IPhiRewards(phiRewardsContract_); 54 | emit PhiRewardsContractUpdated(phiRewardsContract_); 55 | } 56 | 57 | function updateRoyalty(uint256 newRoyalty_) external onlyOwner { 58 | if (newRoyalty_ > MAX_ROYALTY_RANGE) { 59 | revert InvalidRoyalty(newRoyalty_); 60 | } 61 | withdrawRoyalty = newRoyalty_; 62 | emit RoyaltyUpdated(newRoyalty_); 63 | } 64 | 65 | /*////////////////////////////////////////////////////////////// 66 | UPDATE FUNCTIONS 67 | //////////////////////////////////////////////////////////////*/ 68 | function deposit(uint256 credId, uint256 amount) external payable { 69 | if (!credContract.isExist(credId)) revert InvalidCredId(); 70 | if (msg.value != amount) { 71 | revert InvalidValue(msg.value, amount); 72 | } 73 | balanceOf[credId] += amount; 74 | emit Deposit(credId, amount); 75 | } 76 | 77 | function distribute(uint256 credId) external { 78 | if (!credContract.isExist(credId)) revert InvalidCredId(); 79 | uint256 totalBalance = balanceOf[credId]; 80 | if (totalBalance == 0) { 81 | revert NoBalanceToDistribute(); 82 | } 83 | 84 | address[] memory distributeAddresses = credContract.getCuratorAddresses(credId, 0, 0); 85 | uint256 totalNum; 86 | 87 | for (uint256 i = 0; i < distributeAddresses.length; i++) { 88 | totalNum += credContract.getShareNumber(credId, distributeAddresses[i]); 89 | } 90 | 91 | if (totalNum == 0) { 92 | revert NoSharesToDistribute(); 93 | } 94 | 95 | uint256[] memory amounts = new uint256[](distributeAddresses.length); 96 | bytes4[] memory reasons = new bytes4[](distributeAddresses.length); 97 | 98 | uint256 royaltyfee = (totalBalance * withdrawRoyalty) / RATIO_BASE; 99 | uint256 distributeAmount = totalBalance - royaltyfee; 100 | 101 | // actualDistributeAmount is used to avoid rounding errors 102 | // amount[0] = 333 333 333 333 333 333 103 | // amount[1] = 333 333 333 333 333 333 104 | // amount[2] = 333 333 333 333 333 333 105 | uint256 actualDistributeAmount = 0; 106 | for (uint256 i = 0; i < distributeAddresses.length; i++) { 107 | address user = distributeAddresses[i]; 108 | 109 | uint256 userAmounts = credContract.getShareNumber(credId, user); 110 | uint256 userRewards = (distributeAmount * userAmounts) / totalNum; 111 | 112 | if (userRewards > 0) { 113 | amounts[i] = userRewards; 114 | actualDistributeAmount += userRewards; 115 | } 116 | } 117 | 118 | balanceOf[credId] -= totalBalance; 119 | 120 | _msgSender().safeTransferETH(royaltyfee + distributeAmount - actualDistributeAmount); 121 | 122 | //slither-disable-next-line arbitrary-send-eth 123 | phiRewardsContract.depositBatch{ value: actualDistributeAmount }( 124 | distributeAddresses, amounts, reasons, "deposit from curator rewards distributor" 125 | ); 126 | 127 | emit RewardsDistributed( 128 | credId, _msgSender(), royaltyfee + distributeAmount - actualDistributeAmount, distributeAmount, totalBalance 129 | ); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /test/ContributeRewards.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.25; 3 | 4 | import { PRBTest } from "@prb/test/src/PRBTest.sol"; 5 | import { StdCheats } from "forge-std/StdCheats.sol"; 6 | import { console2 } from "forge-std/console2.sol"; 7 | import { ECDSA } from "solady/utils/ECDSA.sol"; 8 | import { Settings } from "./helpers/Settings.sol"; 9 | import { IContributeRewards } from "../src/interfaces/IContributeRewards.sol"; 10 | import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 11 | 12 | contract ContributeRewardsTest is Settings { 13 | string constant credUUID = "18cd3748-9a76-4a05-8c69-ba0b8c1a9d17"; 14 | 15 | function setUp() public override { 16 | super.setUp(); 17 | // Create a cred 18 | vm.warp(START_TIME + 1); 19 | vm.startPrank(owner); 20 | string memory credURL = "test"; 21 | uint256 expiresIn = START_TIME + 100; 22 | bytes memory signCreateData = 23 | abi.encode(expiresIn, owner, 31_337, address(bondingCurve), credURL, "BASIC", "SIGNATURE", bytes32(0)); 24 | bytes32 createMsgHash = keccak256(signCreateData); 25 | bytes32 createDigest = ECDSA.toEthSignedMessageHash(createMsgHash); 26 | (uint8 cv, bytes32 cr, bytes32 cs) = vm.sign(claimSignerPrivateKey, createDigest); 27 | if (cv != 27) cs = cs | bytes32(uint256(1) << 255); 28 | 29 | uint256 buyPrice = bondingCurve.getBuyPriceAfterFee(0, 0, 1); 30 | cred.createCred{ value: buyPrice }(participant, signCreateData, abi.encodePacked(cr, cs), 100, 100); 31 | vm.stopPrank(); 32 | } 33 | 34 | function testSetRewardInfo() public { 35 | uint256 credId = 1; 36 | uint256 claimPeriodEnds = block.timestamp + 7 days; 37 | bytes32 merkleRoot = keccak256("test"); 38 | uint256 totalRewardAmount = 1000 ether; 39 | 40 | assertGe(mockToken.balanceOf(owner), totalRewardAmount, "Insufficient token balance"); 41 | 42 | vm.startPrank(owner); 43 | bool approved = mockToken.approve(address(contributeRewards), totalRewardAmount); 44 | assertTrue(approved, "Approval failed"); 45 | 46 | contributeRewards.setRewardInfo( 47 | credId, claimPeriodEnds, merkleRoot, address(mockToken), totalRewardAmount, false 48 | ); 49 | vm.stopPrank(); 50 | 51 | ( 52 | uint256 storedClaimPeriodEnds, 53 | bytes32 storedMerkleRoot, 54 | address storedRewardToken, 55 | uint256 storedTotalRewardAmount, 56 | uint256 storedClaimedAmount, 57 | address storedRewardSetter, 58 | bool isOpen 59 | ) = contributeRewards.getRewardInfo(credId, 0); 60 | 61 | assertEq(storedClaimPeriodEnds, claimPeriodEnds); 62 | assertEq(storedMerkleRoot, merkleRoot); 63 | assertEq(storedRewardToken, address(mockToken)); 64 | assertEq(storedTotalRewardAmount, totalRewardAmount); 65 | assertEq(storedClaimedAmount, 0); 66 | assertEq(storedRewardSetter, owner); 67 | assertTrue(isOpen); 68 | } 69 | 70 | function testClaimReward() public { 71 | uint256 credId = 1; 72 | uint256 claimPeriodEnds = block.timestamp + 7 days; 73 | bytes32 merkleRoot = 0x1234567890123456789012345678901234567890123456789012345678901234; 74 | uint256 totalRewardAmount = 1000 ether; 75 | 76 | vm.startPrank(owner); 77 | mockToken.approve(address(contributeRewards), totalRewardAmount); 78 | 79 | contributeRewards.setRewardInfo( 80 | credId, claimPeriodEnds, merkleRoot, address(mockToken), totalRewardAmount, false 81 | ); 82 | vm.stopPrank(); 83 | 84 | uint256 rewardId = 0; 85 | uint256 claimAmount = 10 ether; 86 | bytes32[] memory proof = new bytes32[](1); 87 | proof[0] = bytes32(0x2345678901234567890123456789012345678901234567890123456789012345); 88 | 89 | vm.prank(user1); 90 | vm.expectRevert(IContributeRewards.InvalidProof.selector); 91 | contributeRewards.claimReward(credId, rewardId, claimAmount, proof); 92 | } 93 | 94 | function testCloseAndSweep() public { 95 | uint256 credId = 1; 96 | uint256 claimPeriodEnds = block.timestamp + 1 days; 97 | bytes32 merkleRoot = keccak256("test"); 98 | uint256 totalRewardAmount = 1000 ether; 99 | 100 | vm.startPrank(owner); 101 | mockToken.approve(address(contributeRewards), totalRewardAmount); 102 | 103 | contributeRewards.setRewardInfo( 104 | credId, claimPeriodEnds, merkleRoot, address(mockToken), totalRewardAmount, false 105 | ); 106 | 107 | uint256 rewardId = 0; 108 | 109 | vm.warp(claimPeriodEnds + 1); 110 | 111 | uint256 initialBalance = mockToken.balanceOf(owner); 112 | contributeRewards.closeAndSweep(credId, rewardId); 113 | uint256 finalBalance = mockToken.balanceOf(owner); 114 | vm.stopPrank(); 115 | 116 | assertEq(finalBalance - initialBalance, totalRewardAmount); 117 | 118 | // Test that the reward is closed 119 | (,,,,,, bool isOpen) = contributeRewards.getRewardInfo(credId, rewardId); 120 | assertFalse(isOpen); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /test/CuratorRewardsDistributor.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.25; 3 | 4 | import { PRBTest } from "@prb/test/src/PRBTest.sol"; 5 | import { StdCheats } from "forge-std/StdCheats.sol"; 6 | import { console2 } from "forge-std/console2.sol"; 7 | 8 | import { Settings } from "./helpers/Settings.sol"; 9 | import { ECDSA } from "solady/utils/ECDSA.sol"; 10 | import { ICred } from "../src/interfaces/ICred.sol"; 11 | import { ICuratorRewardsDistributor } from "../src/interfaces/ICuratorRewardsDistributor.sol"; 12 | import { IPhiRewards } from "../src/interfaces/IPhiRewards.sol"; 13 | 14 | contract CuratorRewardsDistributorTest is Settings { 15 | string constant credUUID = "18cd3748-9a76-4a05-8c69-ba0b8c1a9d17"; 16 | 17 | function setUp() public override { 18 | super.setUp(); 19 | vm.warp(START_TIME + 1); 20 | vm.startPrank(owner); 21 | // Create a cred 22 | string memory credURL = "test"; 23 | uint256 expiresIn = START_TIME + 100; 24 | bytes memory signCreateData = 25 | abi.encode(expiresIn, owner, 31_337, address(bondingCurve), credURL, "BASIC", "SIGNATURE", bytes32(0)); 26 | bytes32 createMsgHash = keccak256(signCreateData); 27 | bytes32 createDigest = ECDSA.toEthSignedMessageHash(createMsgHash); 28 | (uint8 cv, bytes32 cr, bytes32 cs) = vm.sign(claimSignerPrivateKey, createDigest); 29 | if (cv != 27) cs = cs | bytes32(uint256(1) << 255); 30 | 31 | uint256 buyPrice = bondingCurve.getBuyPriceAfterFee(0, 0, 1); 32 | cred.createCred{ value: buyPrice }(participant, signCreateData, abi.encodePacked(cr, cs), 100, 100); 33 | 34 | vm.stopPrank(); 35 | } 36 | 37 | function testDistribute() public { 38 | uint256 credId = 1; 39 | uint256 depositAmount = 1 ether; 40 | 41 | // Deposit some ETH to the curatorRewardsDistributor 42 | curatorRewardsDistributor.deposit{ value: depositAmount }(credId, depositAmount); 43 | 44 | // Signal creds for different users 45 | vm.startPrank(user1); 46 | vm.deal(user1, bondingCurve.getBuyPriceAfterFee(credId, 1, 1)); 47 | cred.buyShareCred{ value: bondingCurve.getBuyPriceAfterFee(credId, 1, 1) }(credId, 1, 0); 48 | vm.stopPrank(); 49 | 50 | vm.startPrank(user2); 51 | vm.deal(user2, bondingCurve.getBuyPriceAfterFee(credId, 2, 2)); 52 | cred.buyShareCred{ value: bondingCurve.getBuyPriceAfterFee(credId, 2, 2) }(credId, 2, 0); 53 | vm.stopPrank(); 54 | 55 | assertEq(cred.getShareNumber(credId, user1), 1, "Signal count should be 1"); 56 | assertEq(cred.getShareNumber(credId, user2), 2, "Signal count should be 2"); 57 | assertEq(cred.getCurrentSupply(credId), 4, "Signal count should be 3"); 58 | assertEq(cred.getCuratorAddressLength(credId), 3, "Signal count should be 3"); 59 | 60 | // Record initial balances 61 | uint256 initialUser1Balance = phiRewards.balanceOf(user1); 62 | uint256 initialUser2Balance = phiRewards.balanceOf(user2); 63 | uint256 ownerBalance = owner.balance; 64 | 65 | // Distribute rewards 66 | vm.prank(owner); 67 | curatorRewardsDistributor.distribute(credId); 68 | 69 | // Check final balances 70 | uint256 finalUser1Balance = phiRewards.balanceOf(user1); 71 | uint256 finalUser2Balance = phiRewards.balanceOf(user2); 72 | uint256 finalOwnerBalance = owner.balance; 73 | 74 | // Assert the distribution 75 | assertEq( 76 | finalUser1Balance - initialUser1Balance, 77 | (depositAmount - depositAmount / 100) / 4, 78 | "User1 should receive 1/4 of the rewards" 79 | ); 80 | assertEq( 81 | finalUser2Balance - initialUser2Balance, 82 | ((depositAmount - depositAmount / 100) * 2) / 4, 83 | "User2 should receive 2/4 of the rewards" 84 | ); 85 | assertEq( 86 | finalOwnerBalance - ownerBalance, (depositAmount / 100), "Distributer should receive 1/100 of the rewards" 87 | ); 88 | 89 | // Check that the balance in curatorRewardsDistributor is now 0 90 | assertEq( 91 | curatorRewardsDistributor.balanceOf(credId), 92 | 0, 93 | "CuratorRewardsDistributor balance should be 0 after distribution" 94 | ); 95 | } 96 | 97 | function testDistributeNoBalance() public { 98 | uint256 credId = 1; 99 | 100 | vm.expectRevert(ICuratorRewardsDistributor.NoBalanceToDistribute.selector); 101 | curatorRewardsDistributor.distribute(credId); 102 | } 103 | 104 | function testDistributeNoShares() public { 105 | uint256 credId = 1; 106 | vm.prank(owner); 107 | 108 | vm.warp(block.timestamp + 10 minutes + 1 seconds); 109 | cred.sellShareCred(credId, 1, 0); 110 | uint256 depositAmount = 1 ether; 111 | 112 | // Deposit some ETH to the curatorRewardsDistributor 113 | curatorRewardsDistributor.deposit{ value: depositAmount }(credId, depositAmount); 114 | 115 | vm.expectRevert(ICuratorRewardsDistributor.NoSharesToDistribute.selector); 116 | curatorRewardsDistributor.distribute(credId); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /test/DeployAndUpgradeTest.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import { UpgradeCred } from "../script/UpgradeCred.s.sol"; 5 | import { Test, console } from "forge-std/Test.sol"; 6 | import { StdCheats } from "forge-std/StdCheats.sol"; 7 | import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; 8 | import { Settings } from "./helpers/Settings.sol"; 9 | import { Cred } from "../src/Cred.sol"; 10 | import { CredV2 } from "./helpers/CredV2.sol"; 11 | import { StdCheats } from "forge-std/StdCheats.sol"; 12 | import { console2 } from "forge-std/console2.sol"; 13 | 14 | contract DeployAndUpgradeTest is StdCheats, Settings { 15 | function setUp() public override { 16 | super.setUp(); 17 | } 18 | 19 | function testPhiFactory() public { 20 | uint256 expectedValue = 1; 21 | assertEq(expectedValue, phiFactory.version()); 22 | } 23 | 24 | // function testDeploymentIsV1() public { 25 | // address proxyAddress = deployBox.deployBox(); 26 | // uint256 expectedValue = 7; 27 | // vm.expectRevert(); 28 | // BoxV2(proxyAddress).setValue(expectedValue); 29 | // } 30 | 31 | function testUpgradeWorks() public { 32 | cred = new Cred(); 33 | ERC1967Proxy credProxy = new ERC1967Proxy(address(cred), ""); 34 | Cred(payable(address(credProxy))).initialize(owner, owner, owner, 500, address(0),address(0)); 35 | 36 | CredV2 cred2 = new CredV2(); 37 | 38 | vm.prank(owner); 39 | Cred(payable(address(credProxy))).upgradeToAndCall( 40 | address(cred2), abi.encodeWithSelector(CredV2.initializeV2.selector) 41 | ); 42 | uint256 expectedValue = 2; 43 | assertEq(expectedValue, CredV2(payable(credProxy)).version()); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /test/RewardControl.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Unlicense 2 | pragma solidity 0.8.25; 3 | 4 | import { PRBTest } from "@prb/test/src/PRBTest.sol"; 5 | import { Test } from "forge-std/Test.sol"; 6 | import { console2 } from "forge-std/console2.sol"; 7 | 8 | import { PhiRewards } from "../src/reward/PhiRewards.sol"; 9 | import { Settings } from "./helpers/Settings.sol"; 10 | 11 | contract TestRewardControl is Settings { 12 | function setUp() public override { 13 | super.setUp(); 14 | } 15 | 16 | // function getDomainSeparator() internal view virtual returns (bytes32) { 17 | // return keccak256( 18 | // abi.encode( 19 | // keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), 20 | // keccak256(bytes("PhiRewards")), 21 | // keccak256(bytes("1")), 22 | // block.chainid, 23 | // address(phiRewards) 24 | // ) 25 | // ); 26 | // } 27 | 28 | /*////////////////////////////////////////////////////////////// 29 | Deposit 30 | //////////////////////////////////////////////////////////////*/ 31 | function testDeposit(uint256 amount, address to) public { 32 | vm.assume(amount < ETH_SUPPLY); 33 | vm.assume(to != address(0)); 34 | 35 | vm.deal(participant, amount); 36 | 37 | assertEq(phiRewards.balanceOf(to), 0); 38 | vm.prank(participant); 39 | phiRewards.deposit{ value: amount }(to, bytes4(0), "test"); 40 | assertEq(phiRewards.balanceOf(to), amount); 41 | } 42 | 43 | function testDepositBatch(uint8 numRecipients) public { 44 | address[] memory recipients = new address[](numRecipients); 45 | uint256[] memory amounts = new uint256[](numRecipients); 46 | bytes4[] memory reasons = new bytes4[](numRecipients); 47 | 48 | uint256 totalValue; 49 | 50 | for (uint256 i; i < numRecipients; ++i) { 51 | recipients[i] = makeAddr(vm.toString(i + 1)); 52 | amounts[i] = i + 1 ether; 53 | 54 | totalValue += amounts[i]; 55 | } 56 | 57 | vm.deal(participant, totalValue); 58 | vm.prank(participant); 59 | phiRewards.depositBatch{ value: totalValue }(recipients, amounts, reasons, "test"); 60 | 61 | for (uint256 i; i < numRecipients; ++i) { 62 | assertEq(phiRewards.balanceOf(recipients[i]), amounts[i]); 63 | } 64 | } 65 | 66 | /*////////////////////////////////////////////////////////////// 67 | Withdraw 68 | //////////////////////////////////////////////////////////////*/ 69 | function testWithdraw() public { 70 | uint256 beforeCreatorBalance = artCreator.balance; 71 | uint256 beforeTotalSupply = phiRewards.totalSupply(); 72 | 73 | uint256 creatorRewardsBalance = phiRewards.balanceOf(artCreator); 74 | 75 | vm.prank(artCreator); 76 | phiRewards.withdraw(artCreator, creatorRewardsBalance); 77 | 78 | assertEq(artCreator.balance, beforeCreatorBalance + creatorRewardsBalance); 79 | assertEq(phiRewards.totalSupply(), beforeTotalSupply - creatorRewardsBalance); 80 | } 81 | 82 | function testWithdrawFullBalance() public { 83 | uint256 beforeCreatorBalance = artCreator.balance; 84 | uint256 beforeTotalSupply = phiRewards.totalSupply(); 85 | 86 | uint256 creatorRewardsBalance = phiRewards.balanceOf(artCreator); 87 | 88 | vm.prank(artCreator); 89 | phiRewards.withdraw(artCreator, 0); 90 | 91 | assertEq(artCreator.balance, beforeCreatorBalance + creatorRewardsBalance); 92 | assertEq(phiRewards.totalSupply(), beforeTotalSupply - creatorRewardsBalance); 93 | } 94 | 95 | function testWithdrawFor() public { 96 | uint256 beforeCreatorBalance = artCreator.balance; 97 | uint256 beforeTotalSupply = phiRewards.totalSupply(); 98 | 99 | uint256 creatorRewardsBalance = phiRewards.balanceOf(artCreator); 100 | 101 | phiRewards.withdrawFor(artCreator, creatorRewardsBalance); 102 | 103 | assertEq(artCreator.balance, beforeCreatorBalance + creatorRewardsBalance); 104 | assertEq(phiRewards.totalSupply(), beforeTotalSupply - creatorRewardsBalance); 105 | } 106 | 107 | function testWithdrawWithSig() public { 108 | uint256 artCreatorRewardsBalance = phiRewards.balanceOf(artCreator); 109 | 110 | (, uint256 artCreatorPrivateKey) = makeAddrAndKey("artCreator"); 111 | 112 | uint256 nonce = phiRewards.nonces(artCreator); 113 | uint256 deadline = block.timestamp + 1 days; 114 | 115 | uint256 beforeartCreatorBalance = artCreator.balance; 116 | uint256 beforeTotalSupply = phiRewards.totalSupply(); 117 | 118 | bytes memory sig = 119 | _signMint(artCreatorPrivateKey, artCreator, artCreator, artCreatorRewardsBalance, nonce, deadline); 120 | phiRewards.withdrawWithSig(artCreator, artCreator, artCreatorRewardsBalance, deadline, sig); 121 | 122 | assertEq(artCreator.balance, beforeartCreatorBalance + artCreatorRewardsBalance); 123 | assertEq(phiRewards.totalSupply(), beforeTotalSupply - artCreatorRewardsBalance); 124 | } 125 | 126 | function _signMint( 127 | uint256 pk, 128 | address from, 129 | address to, 130 | uint256 amount, 131 | uint256 nonce, 132 | uint256 deadline 133 | ) 134 | public 135 | returns (bytes memory signature) 136 | { 137 | bytes32 digest = phiRewards.hashTypedData( 138 | keccak256(abi.encode(phiRewards.WITHDRAW_TYPEHASH(), from, to, amount, nonce++, deadline)) 139 | ); 140 | (uint8 v, bytes32 r, bytes32 s) = vm.sign(pk, digest); 141 | signature = abi.encodePacked(r, s, v); 142 | assertEq(signature.length, 65); 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /test/helpers/TestUtils.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Unlicense 2 | pragma solidity 0.8.25; 3 | 4 | import { ECDSA } from "solady/utils/ECDSA.sol"; 5 | import { Test } from "forge-std/Test.sol"; 6 | 7 | contract TestUtils is Test { 8 | function calculateTotalRewardsPlusFee( 9 | uint256 totalParticipants, 10 | uint256 rewardAmount, 11 | uint16 questFee 12 | ) 13 | internal 14 | pure 15 | returns (uint256) 16 | { 17 | return calculateTotalRewards(totalParticipants, rewardAmount) 18 | + calculateTotalFees(totalParticipants, rewardAmount, questFee); 19 | } 20 | 21 | function calculateTotalRewards(uint256 totalParticipants, uint256 rewardAmount) internal pure returns (uint256) { 22 | return totalParticipants * rewardAmount; 23 | } 24 | 25 | function calculateTotalFees( 26 | uint256 totalParticipants, 27 | uint256 rewardAmount, 28 | uint16 questFee 29 | ) 30 | internal 31 | pure 32 | returns (uint256) 33 | { 34 | return (totalParticipants * rewardAmount * questFee) / 10_000; 35 | } 36 | 37 | function signHash(bytes32 msgHash, uint256 privateKey) internal pure returns (bytes memory) { 38 | bytes32 digest = ECDSA.toEthSignedMessageHash(msgHash); 39 | (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, digest); 40 | return abi.encodePacked(r, s, v); 41 | } 42 | } 43 | --------------------------------------------------------------------------------