├── .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 |
--------------------------------------------------------------------------------