├── .changeset ├── README.md └── config.json ├── .env.example ├── .gas-snapshot ├── .github ├── ISSUE_TEMPLATE │ ├── bug-report-template.md │ ├── config.yaml │ ├── feature-request-template.md │ ├── task.md │ └── tracking_issue.md ├── pull_request_template.md └── workflows │ └── test.yml ├── .gitignore ├── .gitmodules ├── .prettierignore ├── .prettierrc ├── .solhint.json ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── GUIDELINES.md ├── LICENSE ├── Makefile ├── README.md ├── SPUML-v1.pdf ├── StoryProtocol-AlphaTestingAgreement-17942166.3.pdf ├── contracts ├── IPAssetRegistry.sol ├── StoryProtocol.sol ├── access-control │ ├── AccessControlSingleton.sol │ ├── AccessControlled.sol │ └── AccessControlledUpgradeable.sol ├── hooks │ ├── PolygonTokenHook.sol │ ├── TokenGatedHook.sol │ └── base │ │ ├── AsyncBaseHook.sol │ │ ├── BaseHook.sol │ │ └── SyncBaseHook.sol ├── interfaces │ ├── IIPAssetRegistry.sol │ ├── access-control │ │ └── IAccessControlled.sol │ ├── hooks │ │ └── base │ │ │ ├── ICallbackHandler.sol │ │ │ └── IHook.sol │ ├── ip-org │ │ ├── IIPOrg.sol │ │ └── IIPOrgController.sol │ ├── modules │ │ ├── IGateway.sol │ │ ├── IModuleRegistry.sol │ │ ├── base │ │ │ └── IModule.sol │ │ ├── licensing │ │ │ └── ILicensingModule.sol │ │ ├── registration │ │ │ └── IRegistrationModule.sol │ │ └── relationships │ │ │ └── IRelationshipModule.sol │ └── utils │ │ └── IPolygonTokenClient.sol ├── ip-org │ ├── IPOrg.sol │ └── IPOrgController.sol ├── lib │ ├── AccessControl.sol │ ├── BitMask.sol │ ├── Errors.sol │ ├── IPAsset.sol │ ├── IPOrgParams.sol │ ├── hooks │ │ ├── Hook.sol │ │ ├── PolygonToken.sol │ │ └── TokenGated.sol │ └── modules │ │ ├── LibRelationship.sol │ │ ├── Licensing.sol │ │ ├── Module.sol │ │ ├── ModuleRegistryKeys.sol │ │ ├── Registration.sol │ │ ├── Royalties.sol │ │ └── SPUMLParams.sol ├── modules │ ├── Gateway.sol │ ├── ModuleRegistry.sol │ ├── base │ │ ├── BaseModule.sol │ │ └── HookRegistry.sol │ ├── licensing │ │ ├── LicenseRegistry.sol │ │ ├── LicensingFrameworkRepo.sol │ │ └── LicensingModule.sol │ ├── registration │ │ └── RegistrationModule.sol │ └── relationships │ │ └── RelationshipModule.sol └── utils │ ├── FixedSet.sol │ └── ShortStringOps.sol ├── deployment-11155111.json ├── foundry.toml ├── hardhat.config.js ├── image.png ├── legacy ├── contracts │ ├── interfaces │ │ ├── ip-accounts │ │ │ ├── IERC6551Account.sol │ │ │ ├── IIPAccount.sol │ │ │ └── IIPAccountRegistry.sol │ │ └── modules │ │ │ ├── collect │ │ │ ├── ICollectModule.sol │ │ │ ├── ICollectNFT.sol │ │ │ └── ICollectPaymentModule.sol │ │ │ └── royalties │ │ │ ├── IRoyaltyDistributor.sol │ │ │ ├── IRoyaltyPolicy.sol │ │ │ ├── ISplitMain.sol │ │ │ └── policies │ │ │ └── IRoyaltyPolicy.sol │ ├── ip-accounts │ │ ├── IPAccountImpl.sol │ │ └── IPAccountRegistry.sol │ └── modules │ │ ├── collect │ │ ├── CollectModuleBase.sol │ │ ├── CollectPaymentModuleBase.sol │ │ ├── SimpleCollectModule.sol │ │ └── nft │ │ │ ├── CollectNFTBase.sol │ │ │ └── ERC721.sol │ │ └── royalties │ │ ├── RoyaltyDistributor.sol │ │ ├── RoyaltyNFT.sol │ │ ├── RoyaltyNFTFactory.sol │ │ └── policies │ │ └── MutableRoyaltyProportionPolicy.sol └── test │ └── foundry │ ├── IPAccount.t.sol │ ├── IPAccountRegistry.t.sol │ ├── RoyaltyDistributor.t.sol │ ├── RoyaltyDistributorFork.t.sol │ ├── _old_modules │ └── collect │ │ ├── BaseCollectModuleTest.sol │ │ ├── CollectPaymentModuleBase.t.sol │ │ ├── SimpleCollectModule.t.sol │ │ └── nft │ │ ├── BaseERC721Test.sol │ │ ├── CollectNFTBase.t.sol │ │ └── ERC721.t.sol │ └── mocks │ ├── MockCollectModule.sol │ ├── MockCollectModuleERC721.sol │ ├── MockCollectNFT.sol │ ├── MockCollectPaymentModule.sol │ ├── MockIPAccount.sol │ ├── MockSplit.sol │ └── MockSplitMain.sol ├── lib └── forge-std │ ├── .gitignore │ ├── .gitmodules │ ├── LICENSE-APACHE │ ├── LICENSE-MIT │ ├── README.md │ ├── foundry.toml │ ├── lib │ └── ds-test │ │ ├── .gitignore │ │ ├── LICENSE │ │ ├── Makefile │ │ ├── default.nix │ │ ├── demo │ │ └── demo.sol │ │ ├── package.json │ │ └── src │ │ ├── test.sol │ │ └── test.t.sol │ ├── package.json │ ├── src │ ├── Base.sol │ ├── Script.sol │ ├── StdAssertions.sol │ ├── StdChains.sol │ ├── StdCheats.sol │ ├── StdError.sol │ ├── StdInvariant.sol │ ├── StdJson.sol │ ├── StdMath.sol │ ├── StdStorage.sol │ ├── StdStyle.sol │ ├── StdUtils.sol │ ├── Test.sol │ ├── Vm.sol │ ├── console.sol │ ├── console2.sol │ └── interfaces │ │ ├── IERC1155.sol │ │ ├── IERC165.sol │ │ ├── IERC20.sol │ │ ├── IERC4626.sol │ │ ├── IERC721.sol │ │ └── IMulticall3.sol │ └── test │ ├── StdAssertions.t.sol │ ├── StdChains.t.sol │ ├── StdCheats.t.sol │ ├── StdError.t.sol │ ├── StdMath.t.sol │ ├── StdStorage.t.sol │ ├── StdStyle.t.sol │ ├── StdUtils.t.sol │ ├── compilation │ ├── CompilationScript.sol │ ├── CompilationScriptBase.sol │ ├── CompilationTest.sol │ └── CompilationTestBase.sol │ └── fixtures │ └── broadcast.log.json ├── package-lock.json ├── package.json ├── script ├── data │ └── data_example.json ├── foundry │ ├── deployment │ │ └── Main.s.sol │ └── utils │ │ ├── BroadcastManager.s.sol │ │ ├── HooksFactory.sol │ │ ├── JsonDeploymentHandler.s.sol │ │ ├── RolesPrinter.sol │ │ ├── ShortStringPrinter.sol │ │ └── StringUtil.sol └── hardhat │ ├── createFranchise.js │ ├── defender │ ├── adminProposal.js │ └── proposals │ │ └── initialGrantRoles.js │ ├── loadDeployment.js │ ├── namespacedStorageKey.js │ └── utils │ ├── moduleKeys.json │ └── roles.json ├── slither.config.json ├── test └── foundry │ ├── access-control │ ├── AccessControlSingleton.t.sol │ ├── AccessControlled.t.sol │ └── AccessControlledUpgradeable.t.sol │ ├── e2e │ └── e2e.t.sol │ ├── hooks │ ├── PolygonTokenHookTest.t.sol │ ├── TestAsyncBaseHook.t.sol │ ├── TestBaseHook.t.sol │ ├── TestSyncBaseHook.t.sol │ └── TokenGatedHookTest.t.sol │ ├── interfaces │ ├── IE2ETest.sol │ └── IERC721Events.sol │ ├── ip-org │ └── IPOrgController.t.sol │ ├── lib │ ├── BitMask.t.sol │ ├── FixedSet.t.sol │ └── modules │ │ ├── Licensing.t.sol │ │ └── SPUMLParams.t.sol │ ├── mocks │ ├── MockAccessControlled.sol │ ├── MockAccessControlledUpgradeable.sol │ ├── MockAsyncHook.sol │ ├── MockBaseHook.sol │ ├── MockBaseModule.sol │ ├── MockCallbackHandler.sol │ ├── MockERC20.sol │ ├── MockERC721.sol │ ├── MockERC721Receiver.sol │ ├── MockGateway.sol │ ├── MockHookRegistry.sol │ ├── MockIPAssetOrgFactory.sol │ ├── MockIPOrg.sol │ ├── MockIPOrgController.sol │ ├── MockNativeTokenNonReceiver.sol │ ├── MockPolygonTokenClient.sol │ ├── MockSyncHook.sol │ ├── MockThrowingERC20.sol │ └── MockWETH.sol │ ├── modules │ ├── ModuleRegistry.t.sol │ ├── base │ │ ├── BaseModule.t.sol │ │ └── HookRegistryTest.t.sol │ ├── licensing │ │ ├── LicenseRegistry.t.sol │ │ ├── LicensingFrameworkRepo.t.sol │ │ ├── LicensingModule.Config.t.sol │ │ └── LicensingModule.Licensing.t.sol │ ├── registration │ │ └── RegistrationTest.sol │ └── relationships │ │ ├── RelationshipModule.Config.t.sol │ │ └── RelationshipModule.Setting.t.sol │ └── utils │ ├── AccessControlHelper.sol │ ├── BaseTest.sol │ ├── BaseTestUtils.sol │ └── ProxyHelper.sol └── yarn.lock /.changeset/README.md: -------------------------------------------------------------------------------- 1 | # Changesets 2 | 3 | Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works 4 | with multi-package repos, or single-package repos to help you version and publish your code. You can 5 | find the full documentation for it [in our repository](https://github.com/changesets/changesets) 6 | 7 | We have a quick list of common questions to get you started engaging with this project in 8 | [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) 9 | -------------------------------------------------------------------------------- /.changeset/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://unpkg.com/@changesets/config@2.3.1/schema.json", 3 | "changelog": "@changesets/cli/changelog", 4 | "commit": false, 5 | "fixed": [], 6 | "linked": [], 7 | "access": "restricted", 8 | "baseBranch": "main", 9 | "updateInternalDependencies": "patch", 10 | "ignore": [] 11 | } 12 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | # DEPLOYMENT_CONFIG 2 | ## IF true, Main deploy script will deploy with multisig as AccessControlSingleton admin 3 | ## directly. Role Granting and config must be done through multisig (Defender) proposals. 4 | ## IF false, Main deploy script will deploy with deployer address as admin, then grant 5 | ## AccessControlSingleton admin role to multisig and renounce it. Configuration will be done 6 | ## by the deployer address. 7 | DEPLOYMENT_CONFIG_BY_MULTISIG=false 8 | 9 | # MAINNET 10 | MAINNET_RPC_URL = https://eth-mainnet.g.alchemy.com/v2/1234123412341234 11 | MAINNET_PRIVATEKEY= 12341234123412341234123412341234 12 | MAINNET_DEPLOYER_ADDRESS = 0x12341234123412341234123412341234 # Corresponds to MAINNET_PRIVATEKEY 13 | MAINNET_MULTISIG_ADDRESS = 0x12341234123412341234123412341234 14 | 15 | # SEPOLIA 16 | SEPOLIA_RPC_URL = https://eth-sepolia.g.alchemy.com/v2/1234123412341234 17 | SEPOLIA_PRIVATEKEY = 12341234123412341234123412341234 18 | SEPOLIA_DEPLOYER_ADDRESS = 0x12341234123412341234123412341234 # Corresponds to SEPOLIA_PRIVATEKEY 19 | SEPOLIA_MULTISIG_ADDRESS = 0x12341234123412341234123412341234 20 | 21 | # ETHSCAN 22 | ETHERSCAN_API_KEY = ETHERSCANAPIKEYETHERSCANAPIKEY 23 | 24 | # PROTOCOL LICENSE URL 25 | SPUML_URL=https://url-to-license-file.pdf 26 | 27 | # POLYGON TOKEN ORACLE 28 | POLYGON_TOKEN_ORACLE_CLIENT=0x123412341234123412341234123412341234 29 | POLYGON_TOKEN_ORACLE_COORDINATOR=0x123412341234123412341234123412341234 30 | 31 | # DEFENDER 32 | DEFENDER_API_KEY=DEFENDERAPIKEYDEFENDERAPIKEY 33 | DEFENDER_API_SECRET=DEFENDERSECRETKEYDEFENDERSECRETKEY -------------------------------------------------------------------------------- /.gas-snapshot: -------------------------------------------------------------------------------- 1 | CounterTest:testIncrement() (gas: 28286) 2 | CounterTest:testSetNumber(uint256) (runs: 256, μ: 27045, ~: 28367) -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report-template.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: bug report 3 | about: Use this template for tracking bugs 4 | title: "[BUG]: [short description]" 5 | labels: '' 6 | assignees: '' 7 | release: 'alpha' 8 | --- 9 | 10 | **Describe the bug** 11 | 12 | A clear and concise description of what the bug is. 13 | For security related issues, please contact the team via immunefi directly. 14 | 15 | **More details** 16 | 1. Which network (Sepolia, Mainnet)? 17 | 2. Which smart contract (Address)? 18 | 3. Which function? 19 | 4. Call parameters? 20 | 5. Error messages? 21 | 6. Etherscan Link if any? 22 | 23 | **Expected behavior** 24 | 25 | A clear and concise description of what you expected to happen. 26 | 27 | **Screenshots** 28 | 29 | If applicable, add screenshots to help explain your problem. 30 | 31 | **Additional context** 32 | 33 | Add any other context about the problem here. 34 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yaml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Story Protocol Official Discord 4 | url: https://discord.gg/storyprotocol 5 | about: If you're a user, this is the fastest way to get help. Do not give your wallet private key or mnemonic words to anyone. 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request-template.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for the protocol 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | --- 8 | 9 | **Is your feature request related to a problem? Please describe.** 10 | 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like or just add a user story** 14 | 15 | A clear and concise description of what you want to happen or add description of the use case. 16 | 17 | **Describe alternatives you've considered** 18 | 19 | A clear and concise description of any alternative solutions or features you've considered. 20 | 21 | **Additional context** 22 | 23 | Add any other context or screenshots about the feature request here. 24 | 25 | **Area of this feature request (tbd by the team)** 26 | - [ ] core 27 | - [ ] IPA 28 | - [ ] IPOrg 29 | - [ ] registration 30 | - [ ] licensing / LNFT 31 | - [ ] hooks 32 | - [ ] others 33 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/task.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Task 3 | about: Create a regular work item to be picked up by a contributor. 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## Description and context 11 | 12 | 13 | 14 | 15 | 16 | ## Suggested solution 17 | 18 | 19 | ## Definition of done 20 | 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/tracking_issue.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Tracking issue 3 | about: Tracking issues are task lists used to better organize regular work items. 4 | title: 'Tracking issue for *ADD_PROJECT* - *ADD_COMPONENT*' 5 | labels: 'epic' 6 | assignees: '' 7 | 8 | --- 9 | 10 | This issue is for grouping *ADD_COMPONENT* related tasks that are necessary for *ADD_PROJECT*. 11 | 12 | ### Other tracking issues for the same project: 13 | 14 | 15 | 16 | - #XXXX 17 | - #XXXX 18 | - #XXXX 19 | 20 | 21 | ```[tasklist] 22 | ### Task list 23 | - [ ] XXXX 24 | - [ ] XXXX 25 | ``` 26 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | Example: 4 | This pr adds user login function, includes: 5 | 6 | - 1. add user login page. 7 | - 2. ... 8 | 9 | ## Test Plan 10 | 12 | Example: 13 | - 1. Use different test accounts for login tests, including correct user names and passwords, and incorrect user names and passwords. 14 | - 2. ... 15 | 16 | ## Related Issue 17 | 18 | 19 | Example: Issue #123 20 | 21 | ## Notes 22 | 23 | 24 | - Example: Links and navigation need to be added to the front-end interface -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: UnitTest 2 | 3 | on: [pull_request] 4 | 5 | env: 6 | FOUNDRY_PROFILE: ci 7 | MAINNET_RPC_URL: ${{ secrets.MAINNET_RPC_URL }} 8 | 9 | jobs: 10 | 11 | foundry-test: 12 | strategy: 13 | fail-fast: true 14 | 15 | name: Foundry Unit Test 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v3 19 | with: 20 | submodules: recursive 21 | fetch-depth: 0 22 | 23 | - name: List files in the repository 24 | run: | 25 | ls -R ${{ github.workspace }} 26 | 27 | - name: Test Env Variables 28 | env: 29 | MAINNET_RPC_URL: ${{ secrets.MAINNET_RPC_URL }} 30 | run: | 31 | echo "MAINNET_RPC_URL is ${{ secrets.MAINNET_RPC_URL }}" 32 | echo "env.MAINNET_RPC_URL is $MAINNET_RPC_URL" 33 | echo "env.FOUNDRY_PROFILE is $FOUNDRY_PROFILE" 34 | echo "DONE." 35 | 36 | - name: Run install 37 | uses: borales/actions-yarn@v4 38 | with: 39 | cmd: install # will run `yarn install` command 40 | 41 | - name: Install Foundry 42 | uses: foundry-rs/foundry-toolchain@v1 43 | with: 44 | version: nightly 45 | 46 | - name: List files in the repository 47 | run: | 48 | ls -R ${{ github.workspace }} 49 | 50 | - name: Run Forge build 51 | run: | 52 | forge --version 53 | forge build --force --sizes 54 | id: build 55 | 56 | - name: Run Forge tests 57 | run: | 58 | make test 59 | id: forge-test 60 | 61 | # - name: Gas Difference 62 | # run: 63 | # forge snapshot --gas-report --diff --desc 64 | # id: forge-gas-snapshot-diff 65 | 66 | # - name: Code Coverage 67 | # run: 68 | # forge coverage --report lcov --report summary 69 | # id: forge-code-coverage 70 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiler files 2 | cache/ 3 | out/ 4 | artifacts/ 5 | forge-cache/ 6 | 7 | # Ignores development broadcast logs 8 | !/broadcast 9 | /broadcast/*/ 10 | /broadcast/**/dry-run/ 11 | 12 | # Docs 13 | docs/ 14 | 15 | # Dotenv file 16 | .env 17 | 18 | .idea/ 19 | .vscode 20 | node_modules/ 21 | 22 | # OSX 23 | .DS_Store 24 | 25 | # Local deployment 26 | deployment-31337.json 27 | 28 | # Script data 29 | script/data/ 30 | 31 | # Coverage 32 | coverage/ 33 | lcov.info 34 | 35 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/solmate"] 2 | path = lib/solmate 3 | url = https://github.com/transmissions11/solmate 4 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | build 2 | coverage 3 | out 4 | lib 5 | assets 6 | node_modules 7 | .next 8 | .idea 9 | .github -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "useTabs": false, 3 | "printWidth": 120, 4 | "trailingComma": "es5", 5 | "tabWidth": 4, 6 | "semi": false, 7 | "singleQuote": false, 8 | "bracketSpacing": true 9 | } 10 | -------------------------------------------------------------------------------- /.solhint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solhint:recommended", 3 | "plugins": ["prettier"], 4 | "rules": { 5 | "code-complexity": ["error", 8], 6 | "compiler-version": ["error", ">=0.8.19"], 7 | "const-name-snakecase": "off", 8 | "no-empty-blocks": "off", 9 | "constructor-syntax": "error", 10 | "func-visibility": ["error", { "ignoreConstructors": true }], 11 | "modifier-name-mixedcase": "error", 12 | "max-line-length": ["error", 120], 13 | "not-rely-on-time": "off", 14 | "reason-string": ["warn", { "maxLength": 64 }], 15 | "no-unused-import": "error", 16 | "no-unused-vars": "error", 17 | "no-inline-assembly": "off", 18 | "avoid-low-level-calls": "off", 19 | "no-global-import": "error", 20 | "prettier/prettier": "error" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # @story-protocol/contracts 2 | 3 | ## 0.2.0 4 | 5 | ### Alpha Version 6 | 7 | Unification of the modules and registries of the MVP under a common architecture: 8 | 9 | **Protocol registries and repositories:** 10 | 11 | - IPAssetRegistry: Source of truth for the registered IPAssets across the protocol. 12 | - ModuleRegistry: Registers the protocol modules, and acts router of module execution and configuration. 13 | - LicenseRegistry: Registers the License NFTs. 14 | - LicensingFrameworkRepo: Holds the supported licensing framework, with parameters expressing the licensing terms and their configurations. Currently, it will only hold the SPUML-1.0 framework. 15 | 16 | **IP Org related contracts:** 17 | 18 | - IPOrgController: factory for IPOrg creation, registering organizations that can configure the modules to form a common environment under which IPAs can be registered. 19 | - IPOrg: acts as an NFT view into the global IPAssetRegistry. Adds metadata personalization, and its owner can configure the modules. 20 | 21 | **Modules** 22 | 23 | - BaseModule: defines a common architecture and entrypoints for all modules, along with references to the registries. 24 | - HookRegistry: every module can register hooks, configurable actions to be executed before and after the module action. 25 | - Registration: logic to create IPAs under an IPOrg. 26 | - RelationshipModule: allow the creation of relationship types (protocol wide and specific to ipOrgs) that can relate different elements of the protocol (for example, to express a remixing content graph within an IPOrg) 27 | - Licensing: allows IpOrg to register a common legal framework with some default terms, and has the logic for users to create licenses and link them to IPAs 28 | 29 | **Hooks** 30 | 31 | - SyncBaseHook: to be inherited by all the syncrhonous actions (payment, token gating, etc) 32 | - AsyncBaseHook: to be inherited by all the asyncrhonous actions (oracles, multi step/sig actions, etc) 33 | - TokenGatedHook: checks if caller owns an NFT, if not the module action cannot continue. 34 | - PolygonTokenHook: example async hook, checks if caller owns tokens in the Polygon network. 35 | 36 | **"Frontend" Contracts** 37 | 38 | - StoryProtocol: exposes the write functionality for all the modules, combined. 39 | 40 | ## 0.1.0 41 | 42 | ### First Changes 43 | 44 | - 946b145: First version of core components and modules 45 | - FranchiseRegistry 46 | - IPAssetRegistry 47 | - IPAssetRegistryFactory 48 | - Licensing Module 49 | - Collect Module 50 | - Royalties Module 51 | - Relationship Module 52 | - Access Control 53 | - IP Accounts 54 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at contact@storyprotocol.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from [OpenZeppelin Contracts](https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-contracts/master/CODE_OF_CONDUCT.md) who adapted it from 71 | the [Contributor Covenant][homepage], version 1.4, 72 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 73 | 74 | [homepage]: https://www.contributor-covenant.org -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | -include .env 2 | 3 | .PHONY: all test clean coverage 4 | 5 | all: clean install build 6 | 7 | # Clean the repo 8 | forge-clean :; forge clean 9 | clean :; npx hardhat clean 10 | 11 | # Remove modules 12 | forge-remove :; rm -rf .gitmodules && rm -rf .git/modules/* && rm -rf lib && touch .gitmodules && git add . && git commit -m "modules" 13 | 14 | install :; npm install 15 | 16 | # Update Dependencies 17 | forge-update:; forge update 18 | 19 | forge-build:; forge build 20 | build :; npx hardhat compile 21 | 22 | # TODO: remove --no-match-path after refactor 23 | test :; forge test -vvv --no-match-path 'test/foundry/_old_modules/*' 24 | 25 | snapshot :; forge snapshot 26 | 27 | slither :; slither ./contracts 28 | 29 | format :; npx prettier --write --plugin=prettier-plugin-solidity 'contracts/**/*.sol' && npx prettier --write --plugin=prettier-plugin-solidity --write 'contracts/*.sol' 30 | 31 | # remove `test` and `script` folders from coverage 32 | coverage: 33 | mkdir -p coverage 34 | forge coverage --report lcov 35 | lcov --remove lcov.info -o lcov.info 'test/*' 'script/*' 36 | genhtml lcov.info --output-dir coverage 37 | 38 | # solhint should be installed globally 39 | lint :; npx solhint 'contracts/**/*.sol' 40 | 41 | deploy-goerli :; npx hardhat run ./script/deploy-reveal-engine.js --network goerli 42 | verify-goerli :; npx hardhat verify --network goerli ${contract} 43 | 44 | anvil :; anvil -m 'test test test test test test test test test test test junk' 45 | 46 | -------------------------------------------------------------------------------- /SPUML-v1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storyprotocol/protocol-contracts/af73b9d39a1d001472828558b0c1ed0a46c326d7/SPUML-v1.pdf -------------------------------------------------------------------------------- /StoryProtocol-AlphaTestingAgreement-17942166.3.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storyprotocol/protocol-contracts/af73b9d39a1d001472828558b0c1ed0a46c326d7/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf -------------------------------------------------------------------------------- /contracts/access-control/AccessControlSingleton.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // See https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf 3 | pragma solidity ^0.8.19; 4 | 5 | import { AccessControlUpgradeable } from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; 6 | import { Multicall } from "@openzeppelin/contracts/utils/Multicall.sol"; 7 | import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; 8 | 9 | import { AccessControl } from "contracts/lib/AccessControl.sol"; 10 | import { Errors } from "contracts/lib/Errors.sol"; 11 | 12 | /// @title Access Control Singleton 13 | /// @notice This contract serves as the global source of truth for role-based authorization. 14 | /// Contracts that inherit the AccessControlled contract or its upgradable variant 15 | /// are may be granted granular access to certain roles by utilizing the `onlyRole` 16 | /// modifier with the required role input as a parameter. 17 | contract AccessControlSingleton is AccessControlUpgradeable, UUPSUpgradeable, Multicall { 18 | /// @notice Initialize the Access Control Singleton contract. 19 | /// @param admin_ address to inherit the PROTOCOL_ADMIN_ROLE. 20 | function initialize(address admin_) external initializer { 21 | if (admin_ == address(0)) revert Errors.ZeroAddress(); 22 | __AccessControl_init(); 23 | __UUPSUpgradeable_init(); 24 | _grantRole(AccessControl.PROTOCOL_ADMIN_ROLE, admin_); 25 | } 26 | 27 | /// @notice Defines the admin role associated for a given protocol role. 28 | /// @param role_ The id of the new role, given by keccak256(""). 29 | /// @param admin_ The id of the admin role provisioned for the provided role. 30 | function setRoleAdmin(bytes32 role_, bytes32 admin_) external onlyRole(AccessControl.PROTOCOL_ADMIN_ROLE) { 31 | _setRoleAdmin(role_, admin_); 32 | } 33 | 34 | /// @notice Authorizes an upgrade for the contract. 35 | function _authorizeUpgrade(address) internal virtual override onlyRole(AccessControl.UPGRADER_ROLE) {} 36 | } 37 | -------------------------------------------------------------------------------- /contracts/access-control/AccessControlled.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // See https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf 3 | pragma solidity ^0.8.19; 4 | 5 | // solhint-disable-next-line max-line-length 6 | import { ERC165CheckerUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165CheckerUpgradeable.sol"; 7 | import { IAccessControl } from "@openzeppelin/contracts/access/IAccessControl.sol"; 8 | 9 | import { AccessControl } from "contracts/lib/AccessControl.sol"; 10 | import { IAccessControlled } from "contracts/interfaces/access-control/IAccessControlled.sol"; 11 | import { Errors } from "contracts/lib/Errors.sol"; 12 | 13 | /// @title Access Controlled Contract 14 | /// @notice This contract is to be inherited by any protocol components that require granular 15 | /// roles for execution, as defined by the Access Control Singleton contract. Later on, 16 | /// this contract will be deprecated in favor of authorization through te module registry. 17 | abstract contract AccessControlled is IAccessControlled { 18 | using ERC165CheckerUpgradeable for address; 19 | 20 | /// @notice Pointer to the global Access Control Singleton for protocol auth. 21 | IAccessControl private _accessControl; 22 | 23 | /// @notice Checks if msg.sender has role `role`, reverts otherwise. 24 | /// @param role_ The role being checked for, set by the Access Control Singleton. 25 | modifier onlyRole(bytes32 role_) { 26 | if (!_hasRole(role_, msg.sender)) { 27 | revert Errors.MissingRole(role_, msg.sender); 28 | } 29 | _; 30 | } 31 | 32 | /// @notice Instantiates a new Access Controlled contract. 33 | /// @param accessControl_ The global Access Control Singleton contract address. 34 | constructor(address accessControl_) { 35 | if (!accessControl_.supportsInterface(type(IAccessControl).interfaceId)) 36 | revert Errors.UnsupportedInterface("IAccessControl"); 37 | _accessControl = IAccessControl(accessControl_); 38 | emit AccessControlUpdated(accessControl_); 39 | } 40 | 41 | /// @notice Sets the Access Control Singleton used for authorization. 42 | /// @param accessControl_ The address of the new Access Control Singleton. 43 | function setAccessControl(address accessControl_) public onlyRole(AccessControl.PROTOCOL_ADMIN_ROLE) { 44 | if (!accessControl_.supportsInterface(type(IAccessControl).interfaceId)) 45 | revert Errors.UnsupportedInterface("IAccessControl"); 46 | _accessControl = IAccessControl(accessControl_); 47 | emit AccessControlUpdated(accessControl_); 48 | } 49 | 50 | /// @dev Checks if an account `account_` has role `role_` assigned. 51 | /// @param role_ The role being checked for as defined by the Access Control Singlton. 52 | /// @param account_ The address whose role permissions are being checked for. 53 | /// @return return True if the account has the role, False otherwise. 54 | function _hasRole(bytes32 role_, address account_) internal view returns (bool) { 55 | return _accessControl.hasRole(role_, account_); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /contracts/hooks/TokenGatedHook.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.19; 3 | 4 | import { ERC165Checker } from "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol"; 5 | import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; 6 | 7 | import { Errors } from "contracts/lib/Errors.sol"; 8 | import { SyncBaseHook } from "contracts/hooks/base/SyncBaseHook.sol"; 9 | import { TokenGated } from "contracts/lib/hooks/TokenGated.sol"; 10 | 11 | /// @title Token Gated Hook. 12 | /// @notice Synchronous hook for ensursing a user is the owner of an NFT token. 13 | contract TokenGatedHook is SyncBaseHook { 14 | using ERC165Checker for address; 15 | 16 | /// @notice Constructs the Token Gated Hook contract. 17 | /// @param accessControl_ The address of the global access control contract. 18 | constructor(address accessControl_) SyncBaseHook(accessControl_) {} 19 | 20 | /// @notice Validates the configuration for the token gated hook. 21 | /// @dev This function checks if the tokenAddress is a valid ERC721 contract. 22 | /// @param hookConfig_ The configuration data for the hook. 23 | function _validateConfig(bytes memory hookConfig_) internal view override { 24 | TokenGated.Config memory config = abi.decode(hookConfig_, (TokenGated.Config)); 25 | address tokenAddress = config.tokenAddress; 26 | if (tokenAddress == address(0)) { 27 | revert Errors.ZeroAddress(); 28 | } 29 | // Check if the configured token address is a valid ERC 721 contract. 30 | if (!tokenAddress.supportsInterface(type(IERC721).interfaceId)) { 31 | revert Errors.UnsupportedInterface("IERC721"); 32 | } 33 | } 34 | 35 | /// @notice Executes a token gated check in a synchronous manner. 36 | /// @dev This function checks if the "tokenOwner" owns a token of the specified ERC721 token contract. 37 | /// @param hookConfig_ The configuration of the hook. 38 | /// @param hookParams_ The parameters for the hook. 39 | /// @return An empty bytes object, as no data is retured from this hook. 40 | function _executeSyncCall( 41 | bytes memory hookConfig_, 42 | bytes memory hookParams_ 43 | ) internal virtual override returns (bytes memory) { 44 | TokenGated.Config memory config = abi.decode(hookConfig_, (TokenGated.Config)); 45 | TokenGated.Params memory params = abi.decode(hookParams_, (TokenGated.Params)); 46 | 47 | if (params.tokenOwner == address(0)) { 48 | revert Errors.ZeroAddress(); 49 | } 50 | // check if tokenOwner own any required token 51 | if (IERC721(config.tokenAddress).balanceOf(params.tokenOwner) == 0) { 52 | revert Errors.TokenGatedHook_NotTokenOwner(config.tokenAddress, params.tokenOwner); 53 | } 54 | 55 | return ""; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /contracts/hooks/base/BaseHook.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.19; 3 | 4 | import { AccessControl } from "contracts/lib/AccessControl.sol"; 5 | import { AccessControlled } from "contracts/access-control/AccessControlled.sol"; 6 | import { Errors } from "contracts/lib/Errors.sol"; 7 | import { IHook, HookResult } from "contracts/interfaces/hooks/base/IHook.sol"; 8 | 9 | /// @title Base Hook 10 | /// @notice This contract serves as the foundation for all hook contracts. 11 | /// @dev This contract provides authorization checks shared by all hooks, and is inherited 12 | /// by the AsyncBaseHook and SyncBaseHook, which hook implementations should extend from. 13 | abstract contract BaseHook is IHook, AccessControlled { 14 | /// @notice Creates the Base Hook contract. 15 | /// @param accessControl_ The address of the contract used for authorization. 16 | constructor(address accessControl_) AccessControlled(accessControl_) {} 17 | 18 | /// @notice Executes a synchronous hook. 19 | /// @dev By default, synchronous execution is disabled and this function reverts. 20 | /// Subclasses can enable synchronous execution by overriding this function. 21 | /// Only a caller with the HOOK_CALLER_ROLE can call this function. 22 | function executeSync( 23 | bytes calldata 24 | ) external virtual override onlyRole(AccessControl.HOOK_CALLER_ROLE) returns (HookResult, bytes memory) { 25 | revert Errors.Hook_UnsupportedSyncOperation(); 26 | } 27 | 28 | /// @notice Executes an asynchronous hook. 29 | /// @dev By default, asynchronous execution is disabled and this function reverts. 30 | /// Subclasses can enable asynchronous execution by overriding this function. 31 | /// Only a caller with the HOOK_CALLER_ROLE can call this function. 32 | function executeAsync( 33 | bytes calldata, 34 | address 35 | ) external virtual override onlyRole(AccessControl.HOOK_CALLER_ROLE) returns (HookResult, bytes memory, bytes32) { 36 | revert Errors.Hook_UnsupportedAsyncOperation(); 37 | } 38 | 39 | /// @notice Validates the configuration for the hook. 40 | /// @dev If validation fails, this function will throw. 41 | /// @param hookConfig_ The configuration data for the hook. 42 | function validateConfig(bytes calldata hookConfig_) external view override { 43 | _validateConfig(hookConfig_); 44 | } 45 | 46 | /// @notice Validates the configuration for the hook. 47 | /// @dev This function is intended to be overridden by hook implementations to provide 48 | /// specialized validation logic. If validation fails, this function should throw. 49 | /// @param hookConfig_ The configuration data for the hook. 50 | function _validateConfig(bytes memory hookConfig_) internal view virtual; 51 | } 52 | -------------------------------------------------------------------------------- /contracts/hooks/base/SyncBaseHook.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.19; 3 | 4 | import { AccessControl } from "contracts/lib/AccessControl.sol"; 5 | import { BaseHook } from "contracts/hooks/base/BaseHook.sol"; 6 | import { Hook } from "contracts/lib/hooks/Hook.sol"; 7 | import { HookResult } from "contracts/interfaces/hooks/base/IHook.sol"; 8 | 9 | /// @title Synchronous Base Hook 10 | /// @notice This contract serves as the base for all synchronous hooks. 11 | abstract contract SyncBaseHook is BaseHook { 12 | /// @dev Emits when a synchronous hook is executed. 13 | event SyncHookExecuted(address indexed hookAddress, HookResult indexed result, bytes contextData, bytes returnData); 14 | 15 | /// @notice Constructs the Sync Base Hook contract. 16 | /// @param accessControl_ The address of the access control contract. 17 | constructor(address accessControl_) BaseHook(accessControl_) {} 18 | 19 | /// @notice Executes a synchronous hook. 20 | /// @dev Modules utilize this function to make a synchronous call. 21 | /// Only callers with the HOOK_CALLER_ROLE can call this function. 22 | /// @param hookContext_ The context associated with hook execution. 23 | /// @return result The result of the hook execution. 24 | /// @return hookData The data returned by the hook. 25 | function executeSync( 26 | bytes calldata hookContext_ 27 | ) external override onlyRole(AccessControl.HOOK_CALLER_ROLE) returns (HookResult result, bytes memory hookData) { 28 | Hook.ExecutionContext memory context = abi.decode(hookContext_, (Hook.ExecutionContext)); 29 | _validateConfig(context.config); 30 | hookData = _executeSyncCall(context.config, context.params); 31 | result = HookResult.Completed; 32 | emit SyncHookExecuted(address(this), result, hookContext_, hookData); 33 | } 34 | 35 | /// @dev Executes a synchronous call. This reverts in case of any errors. 36 | /// @param hookConfig_ The configuration of the hook. 37 | /// @param hookParams_ The parameters for the hook. 38 | /// @return hookData The data returned by the hook. 39 | function _executeSyncCall( 40 | bytes memory hookConfig_, 41 | bytes memory hookParams_ 42 | ) internal virtual returns (bytes memory); 43 | } 44 | -------------------------------------------------------------------------------- /contracts/interfaces/IIPAssetRegistry.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.19; 3 | 4 | /// @title Global IP Asset Registry Interface 5 | interface IIPAssetRegistry { 6 | /// @notice Emits when a new IP asset is registered. 7 | /// @param ipAssetId_ The global IP asset identifier. 8 | /// @param name_ The assigned name for the IP asset. 9 | /// @param ipOrg_ The registering governing body for the IP asset. 10 | /// @param registrant_ The initial individual registrant of the IP asset. 11 | /// @param hash_ The content hash associated with the IP asset. 12 | event Registered( 13 | uint256 ipAssetId_, 14 | string name_, 15 | address indexed ipOrg_, 16 | address indexed registrant_, 17 | bytes32 hash_ 18 | ); 19 | 20 | /// @notice Emits when an IP asset is transferred to a new IP Org. 21 | /// @param ipAssetId_ The identifier of the IP asset being transferred. 22 | /// @param oldIPOrg_ The original administering IP Org of the IP asset. 23 | /// @param newIPOrg_ The new administering IP Org of the IP asset. 24 | event IPOrgTransferred(uint256 indexed ipAssetId_, address indexed oldIPOrg_, address indexed newIPOrg_); 25 | 26 | /// @notice Emits when an IP asset has its status changed. 27 | /// @param ipAssetId_ The identifier of the IP asset whose status changed. 28 | /// @param oldStatus_ The original status associated with the IP asset. 29 | /// @param newStatus_ The new status associated with the IP asset. 30 | event StatusChanged(uint256 indexed ipAssetId_, uint8 oldStatus_, uint8 newStatus_); 31 | } 32 | -------------------------------------------------------------------------------- /contracts/interfaces/access-control/IAccessControlled.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.19; 3 | 4 | /// @title Access Controlled Interface 5 | /// @notice This interface must be implemented by all protocol components that require 6 | /// to be authorized via the global Access Control Singleton contract. This 7 | /// initially includes all modules and hooks contracts, but will later be 8 | /// sunset in favor of central authorization via the module registry. 9 | interface IAccessControlled { 10 | /// @notice Emits when the global Access Control Singleton contract is updated. 11 | /// @param accessControl Address of the protocol-wide Access Control singleton contract. 12 | event AccessControlUpdated(address indexed accessControl); 13 | 14 | /// @notice Sets the Access Control Singleton instance. 15 | /// @param accessControl_ address of the new instance of the Access Control Singleton. 16 | function setAccessControl(address accessControl_) external; 17 | } 18 | -------------------------------------------------------------------------------- /contracts/interfaces/hooks/base/ICallbackHandler.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.19; 3 | 4 | import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; 5 | 6 | /// @title Callback Handler Interface 7 | /// @notice This interface defines the method for handling hook callbacks. 8 | /// @dev Modules that call AsyncHooks should implement this interface. 9 | interface ICallbackHandler is IERC165 { 10 | /// @notice Handles a callback from an asynchronous call. 11 | /// @param requestId_ The id of the request. 12 | /// @param callbackData_ The data returned by the callback. 13 | function handleHookCallback(bytes32 requestId_, bytes calldata callbackData_) external; 14 | } 15 | -------------------------------------------------------------------------------- /contracts/interfaces/hooks/base/IHook.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.19; 3 | 4 | /// @notice Enum for representing various states of an async hook. 5 | enum HookResult { 6 | Pending, // Indicates the hook is ongoing execution. 7 | Completed // Indicates the hook has successfully completed. 8 | } 9 | 10 | /// @title Hook Interface. 11 | /// @notice This interface defines methods for synchronous and asynchronous hooks. 12 | /// @dev Hooks are used to execute custom logic in response to certain events or conditions. 13 | interface IHook { 14 | /// @notice Executes a synchronous hook. 15 | /// @param hookContext_ The context of an executing hook. It is an encoded version of Hook.ExecutionContext 16 | /// @return result The result of the hook execution. 17 | /// @return hookData The data returned by the hook. 18 | function executeSync(bytes calldata hookContext_) external returns (HookResult result, bytes memory hookData); 19 | 20 | /// @notice Executes an asynchronous hook. 21 | /// @param hookContext_ The context of an executing hook. It is an encoded version of Hook.ExecutionContext 22 | /// @param callbackHandler_ The address of the callback handler. 23 | /// @return result The result of the hook execution. 24 | /// @return hookData The data returned by the hook. 25 | /// @return requestId The id of the request. 26 | function executeAsync( 27 | bytes calldata hookContext_, 28 | address callbackHandler_ 29 | ) external returns (HookResult result, bytes memory hookData, bytes32 requestId); 30 | 31 | /// @notice Validates the configuration for the hook. 32 | /// @dev This should be overridden by hook implementations to provide custom validation logic. 33 | /// @param hookConfig_ The configuration data for the hook. 34 | function validateConfig(bytes calldata hookConfig_) external view; 35 | } 36 | -------------------------------------------------------------------------------- /contracts/interfaces/ip-org/IIPOrg.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.19; 3 | 4 | /// @notice IP Org Interface 5 | interface IIPOrg { 6 | /// @notice Returns the current owner of the IP asset within the IP Org. 7 | /// @return The address of the owner of the IP asset. 8 | function ownerOf(uint256 id) external view returns (address); 9 | 10 | /// @notice Transfers ownership of the IP asset wrapper within an IP Org. 11 | /// @param from The previous owner of the wrapped IP asset. 12 | /// @param to The new owner of the wrapped IP asset. 13 | /// @param id The identifier of the IP Org asset. 14 | function transferFrom(address from, address to, uint256 id) external; 15 | 16 | /// @notice Burns an IP asset wrapper within the IP Org. 17 | /// @dev This function is only callable by the IP Org registration module. 18 | /// @param id The local identifier of the IP asset within the IP Org. 19 | function burn(uint256 id) external; 20 | 21 | /// @notice Mints an IP Asset wrapper for the IP Org. 22 | /// @dev This function is only callable by the IP Org registration module. 23 | /// @param owner Address of the current owner of the local IP Org asset. 24 | /// @param assetType The IP Org asset type. 25 | /// @return id The local identifier of the minted IP Org wrapped asset. 26 | function mint(address owner, uint8 assetType) external returns (uint256 id); 27 | 28 | /// @notice Gets the current owner of the IP Org. 29 | /// @return The address of the IP Org owner. 30 | function owner() external view returns (address); 31 | 32 | /// @notice Returns contract-level metadata for the IP Org. 33 | /// @return The contract-wide URI associated with the IP Org. 34 | function contractURI() external view returns (string memory); 35 | 36 | /// @notice Returns the IP Org asset type for a given IP Org asset. 37 | /// @return The id associated with the IP Org asset type. 38 | function ipOrgAssetType(uint256 id_) external view returns (uint8); 39 | 40 | /// @notice Gets the global IP asset id associated with this IP Org asset. 41 | /// @param id_ The local id of the IP Org wrapped IP asset. 42 | /// @return The global identifier of the IP asset. 43 | function ipAssetId(uint256 id_) external returns (uint256); 44 | } 45 | -------------------------------------------------------------------------------- /contracts/interfaces/ip-org/IIPOrgController.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.19; 3 | 4 | /// @notice IP Org Controller Interface 5 | interface IIPOrgController { 6 | /// @notice Emits when a new IP Org is registered. 7 | /// @param owner The address of the IP Org owner. 8 | /// @param ipAssetOrg The address of the new IP Org contract. 9 | /// @param name Descriptive name for the new IP Org contract. 10 | /// @param symbol A describe symbol for the new IP Org contract. 11 | /// @param ipAssetTypes String descriptors of the IP asset types available. 12 | event IPOrgRegistered(address owner, address ipAssetOrg, string name, string symbol, string[] ipAssetTypes); 13 | 14 | /// @notice Emits when an IP Org is transferred to a new owner. 15 | /// @param ipOrg The address of the IP Org. 16 | /// @param prevOwner The address of the previous owner of the IP Org. 17 | /// @param newOwner The address of the new owner of the IP Org. 18 | event IPOrgTransferred(address ipOrg, address prevOwner, address newOwner); 19 | 20 | /// @notice Emits when an ownership transfer is initialized for a new owner. 21 | /// @param ipOrg The address of the IP Org. 22 | /// @param pendingOwner The pending owner to set for the IP Org. 23 | event IPOrgPendingOwnerSet(address ipOrg, address pendingOwner); 24 | 25 | /// @notice Registers a new IP Org. 26 | /// @param owner_ The address of the IP Org owner. 27 | /// @param name_ Metadata name to attach to the IP Org. 28 | /// @param symbol_ Metadata symbol to attach to the IP Org. 29 | /// @param ipAssetTypes_ String descriptors of the IP asset types available. 30 | function registerIpOrg( 31 | address owner_, 32 | string calldata name_, 33 | string calldata symbol_, 34 | string[] calldata ipAssetTypes_ 35 | ) external returns (address); 36 | 37 | /// @notice Checks whether an IP Org exists. 38 | function isIpOrg(address ipOrg_) external view returns (bool); 39 | } 40 | -------------------------------------------------------------------------------- /contracts/interfaces/modules/IGateway.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.19; 3 | 4 | import { ModuleDependencies } from "contracts/lib/modules/Module.sol"; 5 | 6 | /// @title Module Gateway Interface 7 | /// @notice Interface for a Story Protocol module gateway, which is a contract 8 | /// that may be granted access by the module registry to call module 9 | /// functions declared by the gateway's module dependency set. 10 | interface IGateway { 11 | /// @notice Synchronizes all downstream dependencies via the module registry. 12 | /// @dev This function may only be called by the module registry. 13 | /// @return dependencies The freshly updated dependencies needed by the gateway. 14 | function updateDependencies() external returns (ModuleDependencies memory dependencies); 15 | 16 | /// @notice Fetches all module dependencies required by the gateway contract. 17 | /// @return dependencies The dependencies that the gateway requires from the protocol. 18 | function getDependencies() external view returns (ModuleDependencies memory dependencies); 19 | } 20 | -------------------------------------------------------------------------------- /contracts/interfaces/modules/IModuleRegistry.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.19; 3 | 4 | import { ModuleKey } from "contracts/lib/modules/Module.sol"; 5 | import { IGateway } from "contracts/interfaces/modules/IGateway.sol"; 6 | import { IModule } from "contracts/interfaces/modules/base/IModule.sol"; 7 | 8 | /// @title IModuleRegistry 9 | /// @notice Module Registry Interface 10 | interface IModuleRegistry { 11 | /// @notice Emits when a gateway was successfully registered by the protocol 12 | /// for a specific dependency (module type + module function). 13 | /// @param key The identifier of the dependent module type. 14 | /// @param fn The function identifier of the dependent module type. 15 | /// @param gateway The gateway address granted permission to use the dependency. 16 | /// @param grant Whether the gateway was authorized to use the dependency. 17 | event ModuleAuthorizationGranted(ModuleKey indexed key, bytes4 fn, address indexed gateway, bool grant); 18 | 19 | /// @notice Emits when a brand new module is enrolled to the protocol. 20 | /// @param ipOrg The IP Org to which the module belongs. 21 | /// @param moduleKey The string identifier of the module type that was added. 22 | /// @param module The address of the module. 23 | event ModuleAdded(address indexed ipOrg, string moduleKey, address indexed module); 24 | 25 | /// @notice Emits when the protocol module for a module type is removed. 26 | /// @param key The identifier of the module type that was added. 27 | /// @param module The address of the removed module 28 | event ModuleRemoved(ModuleKey indexed key, address indexed module); 29 | 30 | /// @notice Emits when a module is executed for an IP Org. 31 | event ModuleExecuted( 32 | address indexed ipOrg, 33 | string moduleKey, 34 | address indexed caller, 35 | bytes selfParams, 36 | bytes[] preHookParams, 37 | bytes[] postHookParams 38 | ); 39 | 40 | /// @notice Emits when a module is configured for an IP Org. 41 | event ModuleConfigured(address indexed ipOrg, string moduleKey, address indexed caller, bytes params); 42 | 43 | /// @notice Emits when a new hook is added for a specific IP Org. 44 | event HookAdded(address indexed ipOrg, string hookKey, address indexed hook); 45 | 46 | /// @notice Emits when a hook is removed for an IP Org. 47 | event HookRemoved(address indexed ipOrg, string hookKey, address indexed hook); 48 | 49 | /// @notice Registers a new module of a provided type to Story Protocol. 50 | /// @param key_ The bytes32 type of the module being registered. 51 | /// @param module_ The actual module being registered. 52 | function registerProtocolModule(ModuleKey key_, IModule module_) external; 53 | 54 | /// @notice Fetches the protocol module by its string identifier. 55 | /// @param key_ The string module type. 56 | /// @return The module associated with the module key. 57 | function protocolModule(string calldata key_) external view returns (address); 58 | 59 | /// @notice Fetches the protocol module bound to a module type. 60 | /// @param key_ The bytes32 module type. 61 | /// @return The module associated with the module key. 62 | function protocolModule(ModuleKey key_) external view returns (address); 63 | 64 | /// @notice Checks whether a gateway has permission to call a module function. 65 | /// @param key_ The module type. 66 | /// @param gateway_ The gateway which has the module as a dependency. 67 | /// @param fn_ The module function whose access is being checked for. 68 | function isAuthorized(ModuleKey key_, IGateway gateway_, bytes4 fn_) external view returns (bool); 69 | } 70 | -------------------------------------------------------------------------------- /contracts/interfaces/modules/base/IModule.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.19; 3 | 4 | import { IIPOrg } from "contracts/interfaces/ip-org/IIPOrg.sol"; 5 | import { ModuleKey } from "contracts/lib/modules/Module.sol"; 6 | 7 | /// @title Story Protocol Module Interface. 8 | /// @notice This interface must be implemented by all protocol modules in Story Protocol, 9 | /// providing the base functionality needed for authorization and execution 10 | /// logic centered around IP assets. 11 | interface IModule { 12 | /// The execution of the module is pending, and will need to be executed again. 13 | event RequestPending(address indexed sender); 14 | /// Module execution completed successfully. 15 | event RequestCompleted(address indexed sender); 16 | /// Module execution failed. 17 | event RequestFailed(address indexed sender, string reason); 18 | 19 | /// @notice Gets the protocol-wide key associated with the module. 20 | /// @return The bytes32 identifier of the module. 21 | function moduleKey() external pure returns (ModuleKey); 22 | 23 | /// @notice Main execution entrypoint. 24 | /// @dev This function verifies encoded module params, executes any pre-action hooks, 25 | /// performs the main module logic, and then executes any post-action hooks. 26 | /// Modules must decide themselves how parameters are encoded and decoded. 27 | /// @param ipOrg_ Address of the IP Org or the zero address (for protocol-wide modules). 28 | /// @param caller_ Address of the caller. 29 | /// @param moduleParams_ Encoded params to be decoded for module execution. 30 | /// @param preHookParams_ Encoded params used for pre-hook execution logic. 31 | /// @param postHookParams_ Encoded params used for post-hook execution logic. 32 | /// @return result of the module action 33 | function execute( 34 | IIPOrg ipOrg_, 35 | address caller_, 36 | bytes calldata moduleParams_, 37 | bytes[] calldata preHookParams_, 38 | bytes[] calldata postHookParams_ 39 | ) external returns (bytes memory result); 40 | 41 | /// @notice Module configuration entrypoint. 42 | /// @dev Note that it is up to the module on how the parameters should be 43 | /// encoded, unpacked, and used for configuration. 44 | /// @param ipOrg_ Address of the IP Org or the zero address (for protocol-wide modules). 45 | /// @param caller_ Address of configuration caller. 46 | /// @param params_ ABI-encoded parameters used for module configuration. 47 | /// @return result Result of the module configuration expressed as a bytes array. 48 | function configure(IIPOrg ipOrg_, address caller_, bytes calldata params_) external returns (bytes memory result); 49 | } 50 | -------------------------------------------------------------------------------- /contracts/interfaces/modules/licensing/ILicensingModule.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.19; 3 | 4 | import { IModule } from "contracts/interfaces/modules/base/IModule.sol"; 5 | import { Licensing } from "contracts/lib/modules/Licensing.sol"; 6 | 7 | /// @title Licensing Module Interface 8 | interface ILicensingModule is IModule { 9 | /// Emits when an IP org picks a licensing framework and sets its configuration. 10 | /// @param ipOrg Address of the IP org whose license framework is being set. 11 | /// @param frameworkId The uint256 id of the set licensing framework. 12 | /// @param url A string URL which points to the associated legal document. 13 | /// @param licensorConfig Configuration associated with the framework's licensor. 14 | /// @param values A list of terms describing the licensing framework. 15 | event IpOrgLicensingFrameworkSet( 16 | address indexed ipOrg, 17 | string frameworkId, 18 | string url, 19 | Licensing.LicensorConfig licensorConfig, 20 | Licensing.ParamValue[] values 21 | ); 22 | 23 | /// @notice Gets the licensing framework for an IP org. 24 | /// @param ipOrg_ The address of the selected IP Org. 25 | function getIpOrgLicensorConfig(address ipOrg_) external view returns (Licensing.LicensorConfig); 26 | 27 | /// Gets the value set by an IP org for a parameter of a licensing framework. 28 | /// If no value is set (bytes.length==0), licensors will be able to set their value. 29 | /// @param ipOrg_ address of the IP org 30 | /// @param paramTag_ string tag of the parameter 31 | function getIpOrgValueForParam(address ipOrg_, string calldata paramTag_) external view returns (bytes memory); 32 | } 33 | -------------------------------------------------------------------------------- /contracts/interfaces/modules/registration/IRegistrationModule.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.19; 3 | 4 | import { IModule } from "contracts/interfaces/modules/base/IModule.sol"; 5 | 6 | /// @title IRegistrationModule 7 | interface IRegistrationModule is IModule { 8 | /// @notice Emits when an IPOrg updates metadata associated with its IPA. 9 | /// @param ipOrg The address of the IP Org whose metadata was updated. 10 | /// @param baseURI The base token URI to be used for token metadata. 11 | /// @param contractURI The contract URI to be used for contract metadata. 12 | event MetadataUpdated(address indexed ipOrg, string baseURI, string contractURI); 13 | 14 | /// @notice Emits when a new IP asset is registered. 15 | /// @param ipAssetId The identifier of the newly registered IP asset. 16 | /// @param ipOrg The address of the IP Org of the IP asset. 17 | /// @param ipOrgAssetId The IP Org localized id of the IP asset. 18 | /// @param owner The address of the new IP asset owner. 19 | /// @param name The name of the IP asset being registered. 20 | /// @param ipOrgAssetType The numerical id of the IP asset type. 21 | /// @param hash The content hash of the registered IP asset. 22 | /// @param mediaUrl The media URL of the registered IP asset. 23 | event IPAssetRegistered( 24 | uint256 ipAssetId, 25 | address indexed ipOrg, 26 | uint256 ipOrgAssetId, 27 | address indexed owner, 28 | string name, 29 | uint8 indexed ipOrgAssetType, 30 | bytes32 hash, 31 | string mediaUrl 32 | ); 33 | 34 | /// @notice Emits when an IP asset is transferred to a new owner. 35 | /// @param ipAssetId The identifier of the IP asset being transferred. 36 | /// @param ipOrg The address of the IP Org which administers the IP asset. 37 | /// @param ipOrgAssetId The local id of the wrapped IP within the IP Org. 38 | /// @param prevOwner The address of the previous owner of the IP asset. 39 | /// @param newOwner The address of the new owner of the IP asset. 40 | event IPAssetTransferred( 41 | uint256 indexed ipAssetId, 42 | address indexed ipOrg, 43 | uint256 ipOrgAssetId, 44 | address prevOwner, 45 | address newOwner 46 | ); 47 | 48 | /// @notice Returns the current owner of an IP asset. 49 | /// @param ipAssetId_ The global identifier of the IP asset within the GIPR. 50 | function ownerOf(uint256 ipAssetId_) external view returns (address); 51 | 52 | /// @notice Gets the IP asset id associated with an IP Org asset. 53 | /// @param ipOrg_ The address of the governing IP asset IP Org. 54 | /// @param ipOrgAssetId_ The localized id of the IP asset within the IP Org. 55 | function ipAssetId(address ipOrg_, uint256 ipOrgAssetId_) external view returns (uint256); 56 | 57 | /// @notice Renders metadata of an IP Asset localized for an IP Org. 58 | /// @param ipOrg_ The address of the IP Org of the IP asset. 59 | /// @param ipOrgAssetId_ The local id of the IP asset within the IP Org. 60 | /// @param ipOrgAssetType_ The IP Org asset type. 61 | /// @return The token URI associated with the IP Org. 62 | function tokenURI( 63 | address ipOrg_, 64 | uint256 ipOrgAssetId_, 65 | uint8 ipOrgAssetType_ 66 | ) external view returns (string memory); 67 | 68 | /// @notice Gets the contract URI for an IP Org. 69 | /// @param ipOrg_ The address of the IP Org. 70 | /// @return The contract URI associated with the IP Org. 71 | function contractURI(address ipOrg_) external view returns (string memory); 72 | 73 | /// @notice get the ip Asset types of an ipOrg 74 | function getIpOrgAssetTypes(address ipOrg_) external view returns (string[] memory); 75 | 76 | /// @notice Returns true if the index for an IP Org asset type is supported. 77 | function isValidIpOrgAssetType(address ipOrg_, uint8 index) external view returns (bool); 78 | } 79 | -------------------------------------------------------------------------------- /contracts/interfaces/modules/relationships/IRelationshipModule.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.19; 3 | 4 | import { LibRelationship } from "contracts/lib/modules/LibRelationship.sol"; 5 | import { IModule } from "contracts/interfaces/modules/base/IModule.sol"; 6 | 7 | /// @title IRelationshipModule 8 | /// @notice Interface for the RelationshipModule. 9 | interface IRelationshipModule is IModule { 10 | /// Emitted with a new Relationship Type definitions is created 11 | event RelationshipTypeSet( 12 | // Short string naming the type 13 | string relType, 14 | // Zero for protocol-wide, or address of the IPOrg 15 | address indexed ipOrg, 16 | // Allowed src address, zero address if empty, all F for all addresses are OK 17 | address src, 18 | // Allowed items for src 19 | LibRelationship.Relatables srcRelatable, 20 | // Mask of allowed subtypes for src (see BitMask) 21 | uint256 srcSubtypesMask, 22 | // Allowed dst address, zero address if empty, all F for all addresses are OK 23 | address dst, 24 | // Allowed items for dst 25 | LibRelationship.Relatables dstRelatable, 26 | // Mask of allowed subtypes for dst (see BitMask) 27 | uint256 dstSubtypesMask 28 | ); 29 | 30 | /// Emitted when a Relationship Type definition is removed 31 | event RelationshipTypeUnset( 32 | // Short string naming the type 33 | string relType, 34 | // Zero for protocol-wide, or address of the IPOrg 35 | address ipOrg 36 | ); 37 | 38 | /// Emitted when a Relationship is created, linking 2 elements 39 | event RelationshipCreated( 40 | // Sequential Relationship ID 41 | uint256 indexed relationshipId, 42 | // Short string naming the type 43 | string relType, 44 | // Source contract or EOA 45 | address srcAddress, 46 | // Source item ID 47 | uint256 srcId, 48 | // Destination contract or EOA 49 | address dstAddress, 50 | // Destination item ID 51 | uint256 dstId 52 | ); 53 | 54 | /// Gets relationship type definition for a given relationship type name 55 | /// Will revert if no relationship type is found 56 | /// @param ipOrg_ IP Org address or zero address for protocol level relationships 57 | /// @param relType_ the name of the relationship type 58 | /// @return result the relationship type definition 59 | function getRelationshipType( 60 | address ipOrg_, 61 | string memory relType_ 62 | ) external view returns (LibRelationship.RelationshipType memory); 63 | 64 | /// Gets relationship definition for a given relationship id 65 | function getRelationship(uint256 relationshipId_) external view returns (LibRelationship.Relationship memory); 66 | 67 | /// Gets relationship id for a given relationship 68 | function getRelationshipId(LibRelationship.Relationship calldata rel_) external view returns (uint256); 69 | 70 | /// Checks if a relationship has been set 71 | function relationshipExists(LibRelationship.Relationship calldata rel_) external view returns (bool); 72 | } 73 | -------------------------------------------------------------------------------- /contracts/interfaces/utils/IPolygonTokenClient.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.19; 3 | 4 | /// @notice Interface for processing token requests via clients on Polygon. 5 | interface IPolygonTokenClient { 6 | function sendRequest( 7 | bytes32 requestId, 8 | address requester, 9 | address tokenAddress, 10 | address tokenOwnerAddress, 11 | address callbackAddr, 12 | bytes4 callbackFunctionSignature 13 | ) external; 14 | } 15 | -------------------------------------------------------------------------------- /contracts/lib/AccessControl.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // See https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf 3 | pragma solidity ^0.8.19; 4 | 5 | /// @title Access Control Library 6 | /// @notice Library for access control helpers and protocol role definitions. 7 | /// These roles are used by the AccessControlSingleton, accessed by AccessControlled contracts. 8 | library AccessControl { 9 | // Default admin role as per OZ AccessControl system. All other roles stem from this. 10 | bytes32 public constant PROTOCOL_ADMIN_ROLE = bytes32(0); 11 | 12 | // Role that can upgrade UUPS contracts or Beacon Proxies 13 | bytes32 public constant UPGRADER_ROLE = keccak256("UPGRADER_ROLE"); 14 | 15 | // Role for managing protocol-wide and IP Org localized relationships. 16 | bytes32 public constant RELATIONSHIP_MANAGER_ROLE = keccak256("RELATIONSHIP_MANAGER_ROLE"); 17 | 18 | // Role that can perform admin tasks on the Licensing Module contracts (setNonCommercialLicenseURI) 19 | bytes32 public constant LICENSING_MANAGER_ROLE = keccak256("LICENSING_MANAGER_ROLE"); 20 | 21 | // Role that can add new modules to the Module Registry 22 | bytes32 public constant MODULE_REGISTRAR_ROLE = keccak256("MODULE_REGISTRAR_ROLE"); 23 | 24 | // Role that can execute modules 25 | bytes32 public constant MODULE_EXECUTOR_ROLE = keccak256("MODULE_EXECUTOR_ROLE"); 26 | 27 | // Role that can execute Hooks 28 | bytes32 public constant HOOK_CALLER_ROLE = keccak256("HOOK_CALLER_ROLE"); 29 | 30 | } 31 | -------------------------------------------------------------------------------- /contracts/lib/BitMask.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.19; 3 | 4 | /** 5 | * @notice Based on OpenZeppelin's BitMap, this library is used to encode a set of indexes in a compact way. 6 | * Instead of using a storage type like OZ, where they use a mapping(uint256 => uint256) for large numbers of values, 7 | * this library limts it to a 256 values in a single uint256. 8 | */ 9 | library BitMask { 10 | /// Returns whether the bit at `index` is set. 11 | function isSet(uint256 mask_, uint8 index_) internal pure returns (bool) { 12 | uint256 indexMask = 1 << (index_ & 0xff); 13 | return mask_ & indexMask != 0; 14 | } 15 | 16 | /// Sets the bit at `index` to the boolean `value`. 17 | function setTo(uint256 mask_, uint256 index_, bool value_) internal pure returns (uint256) { 18 | if (value_) { 19 | return set(mask_, index_); 20 | } else { 21 | return unset(mask_, index_); 22 | } 23 | } 24 | 25 | /// Sets the bit at `index`. 26 | function set(uint256 mask_, uint256 index_) internal pure returns (uint256) { 27 | uint256 indexMask = 1 << (index_ & 0xff); 28 | return mask_ |= indexMask; 29 | } 30 | 31 | /// Unsets the bit at `index`. 32 | function unset(uint256 mask_, uint256 index_) internal pure returns (uint256) { 33 | uint256 indexMask = 1 << (index_ & 0xff); 34 | return mask_ &= ~indexMask; 35 | } 36 | 37 | /// Gets the uint8 from the bitmask as an array 38 | function getSetIndexes(uint256 mask_) internal pure returns (uint8[] memory) { 39 | // Count the number of set bits to allocate the array size 40 | uint256 count; 41 | for (uint8 i = 0; i < 255; ++i) { 42 | if (isSet(mask_, i)) { 43 | ++count; 44 | } 45 | } 46 | uint8[] memory setBitIndexes = new uint8[](count); 47 | // Fill the array with indices of set bits 48 | uint256 index = 0; 49 | for (uint8 i = 0; i < 255; ++i) { 50 | if (isSet(mask_, i)) { 51 | setBitIndexes[index] = i; 52 | ++index; 53 | } 54 | } 55 | return setBitIndexes; 56 | } 57 | 58 | /// Converts an array of uint8 to a bit mask 59 | function convertToMask(uint8[] memory indexes_) internal pure returns (uint256) { 60 | uint256 mask = 0; 61 | for (uint256 i = 0; i < indexes_.length; ) { 62 | mask |= 1 << (uint256(indexes_[i]) & 0xff); 63 | unchecked { 64 | i++; 65 | } 66 | } 67 | return mask; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /contracts/lib/IPAsset.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // See https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf 3 | pragma solidity ^0.8.19; 4 | 5 | /// @title IP Asset Library 6 | /// @notice Library for constants, structs, and helper functions for IP assets. 7 | library IPAsset { 8 | /// @notice Core attributes that make up an IP Asset. 9 | struct IPA { 10 | string name; // Human-readable identifier for the IP asset. 11 | address registrant; // Address of the initial registrant of the IP asset. 12 | uint8 status; // Current status of the IP asset (e.g. active, expired, etc.) 13 | address ipOrg; // Address of the governing entity of the IP asset. 14 | bytes32 hash; // A unique content hash of the IP asset for preserving integrity. 15 | uint64 registrationDate; // Timestamp for which the IP asset was first registered. 16 | } 17 | 18 | /// @notice Struct for packing parameters related to IP asset registration. 19 | struct RegisterIpAssetParams { 20 | string name; 21 | uint8 ipOrgAssetType; 22 | address owner; 23 | bytes32 hash; 24 | string mediaUrl; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /contracts/lib/IPOrgParams.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // See https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf 3 | pragma solidity ^0.8.19; 4 | 5 | /// @title IP Org Params Library 6 | /// @notice Library for constants, structs, and helper functions for IP Orgs. 7 | library IPOrgParams { 8 | struct RegisterIPOrgParams { 9 | address registry; 10 | string name; 11 | string symbol; 12 | string description; 13 | string metadataUrl; 14 | } 15 | 16 | struct InitIPOrgParams { 17 | address registry; 18 | address owner; 19 | string name; 20 | string symbol; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /contracts/lib/hooks/Hook.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // See https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf 3 | pragma solidity ^0.8.19; 4 | 5 | import { IHook } from "contracts/interfaces/hooks/base/IHook.sol"; 6 | 7 | /// @title Hooks Library 8 | /// @notice This library defines the ExecutionContext struct used when executing hooks. 9 | /// @dev The ExecutionContext struct contains two fields: config and params, both of type bytes. 10 | library Hook { 11 | uint256 internal constant SYNC_FLAG = 1 << 159; 12 | uint256 internal constant ASYNC_FLAG = 1 << 158; 13 | /// @notice Defines the execution context for a hook. 14 | /// @dev The ExecutionContext struct is used as a parameter when executing hooks. 15 | struct ExecutionContext { 16 | /// @notice The configuration data for the hook, encoded as bytes. 17 | /// @dev This data is used to configure the hook before execution. 18 | /// The configuration is stored in the Module. 19 | bytes config; 20 | /// @notice The parameters for the hook, encoded as bytes. 21 | /// @dev These parameters are passed from the external caller when executing modules. 22 | bytes params; 23 | } 24 | 25 | /// @notice Checks if the hook can support synchronous calls. 26 | /// @dev This function checks if the first bit of the hook address is set to 1, 27 | /// indicating that the hook can support synchronous calls. 28 | /// @param self_ The hook to check. 29 | /// @return A boolean indicating if the hook can support synchronous calls. 30 | function canSupportSyncCall(IHook self_) internal pure returns (bool) { 31 | return uint256(uint160(address(self_))) & SYNC_FLAG != 0; 32 | } 33 | 34 | /// @notice Checks if the hook can support asynchronous calls. 35 | /// @dev This function checks if the second bit of the hook address is set to 1, 36 | /// indicating that the hook can support asynchronous calls. 37 | /// @param self_ The hook to check. 38 | /// @return A boolean indicating if the hook can support asynchronous calls. 39 | function canSupportAsyncCall(IHook self_) internal pure returns (bool) { 40 | return uint256(uint160(address(self_))) & ASYNC_FLAG != 0; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /contracts/lib/hooks/PolygonToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.19; 3 | 4 | /** 5 | * @title PolygonToken 6 | * @dev This library is used for managing Polygon tokens. 7 | */ 8 | library PolygonToken { 9 | /// @notice This is the configuration for the Polygon token. 10 | /// @dev It includes the token address and the balance threshold. 11 | struct Config { 12 | /// @notice The address of the Polygon token. 13 | address tokenAddress; 14 | /// @notice The balance threshold for the Polygon token. 15 | uint256 balanceThreshold; 16 | } 17 | 18 | /// @notice This is the parameters for the Polygon token. 19 | /// @dev It includes the token owner address. 20 | struct Params { 21 | /// @notice The address of the Polygon token owner. 22 | address tokenOwnerAddress; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /contracts/lib/hooks/TokenGated.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // See https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf 3 | pragma solidity ^0.8.19; 4 | 5 | /// @title TokenGated 6 | /// @notice This library defines the Config and Params structs used in the TokenGatedHook. 7 | /// @dev The Config struct contains the tokenAddress field, and the Params struct contains the tokenOwner field. 8 | library TokenGated { 9 | /// @notice Defines the required configuration information for the TokenGatedHook. 10 | /// @dev The Config struct contains a single field: tokenAddress. 11 | struct Config { 12 | /// @notice The address of the ERC721 token contract. 13 | /// @dev This address is used to check if the tokenOwner owns a token of the specified ERC721 token contract. 14 | address tokenAddress; 15 | } 16 | 17 | /// @notice Defines the required parameter information for executing the TokenGatedHook. 18 | /// @dev The Params struct contains a single field: tokenOwner. 19 | struct Params { 20 | /// @notice The address of the token owner. 21 | /// @dev This address is checked against the tokenAddress in the Config struct to ensure the owner has a token. 22 | address tokenOwner; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /contracts/lib/modules/LibRelationship.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // See https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf 3 | pragma solidity ^0.8.19; 4 | 5 | /// @title Relationship Module Library 6 | library LibRelationship { 7 | /// @notice defines the elements that can be related in a relationship type 8 | enum Relatables { 9 | /// Unset value 10 | Undefined, 11 | /// The relationship type can be used to relate IPAs 12 | Ipa, 13 | /// The relationship type can be used to relate IPOrg token ids 14 | IpOrgEntry, 15 | /// The relationship type can be used to relate License token ids 16 | License, 17 | /// The relationship type can be used to relate Addresses 18 | Address, 19 | /// The relationship type can be used to relate External NFTs (e.g. ERC721) 20 | ExternalNft 21 | } 22 | 23 | /// @notice defines the elements that can be related in a relationship type 24 | struct RelatedElements { 25 | /// Source type 26 | Relatables src; 27 | /// Destination type 28 | Relatables dst; 29 | } 30 | 31 | /// @notice defines 2 related elements under a certain type 32 | struct Relationship { 33 | /// Relationship type name id 34 | string relType; 35 | /// Source address 36 | address srcAddress; 37 | /// Destination address 38 | address dstAddress; 39 | /// Source id (or zero if not applicable) 40 | uint256 srcId; 41 | /// Destination id (or zero if not applicable) 42 | uint256 dstId; 43 | } 44 | 45 | /// @notice defines the parameters for adding a relationship type 46 | struct AddRelationshipTypeParams { 47 | /// Relationship type name id 48 | string relType; 49 | /// IP Org address or zero address for protocol level rel types 50 | address ipOrg; 51 | /// Source and destination types 52 | RelatedElements allowedElements; 53 | /// Source ipOrgs types allowed or empty array if not applicable 54 | uint8[] allowedSrcs; 55 | /// Destination ipOrgs types allowed or empty array if not applicable 56 | uint8[] allowedDsts; 57 | } 58 | 59 | /// @notice defines the parameters to define a relationship type 60 | struct RelationshipType { 61 | /// Allowed source address 62 | address src; 63 | /// Allowd source subtypes (bitmask verion of allowedSrcs) 64 | uint256 srcSubtypesMask; 65 | /// Allowed destination address 66 | address dst; 67 | /// Allowed destination subtypes (bitmask verion of allowedDsts) 68 | uint256 dstSubtypesMask; 69 | } 70 | 71 | /// @notice defines the parameters for creating a relationship 72 | struct CreateRelationshipParams { 73 | /// Relationship type name id 74 | string relType; 75 | /// Source address 76 | address srcAddress; 77 | /// Source id or zero if not applicable 78 | uint256 srcId; 79 | /// Destination address 80 | address dstAddress; 81 | /// Destination id or zero if not applicable 82 | uint256 dstId; 83 | } 84 | 85 | /// Constant for protocol level relationship types 86 | address public constant PROTOCOL_LEVEL_RELATIONSHIP = address(0); 87 | /// Any address goes 88 | address public constant NO_ADDRESS_RESTRICTIONS = 0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF; 89 | 90 | /// Action to configure a relationship type in Relationship Module 91 | bytes32 public constant ADD_REL_TYPE_CONFIG = keccak256("ADD_REL_TYPE"); 92 | /// Action to remove a relationship type in Relationship Module 93 | bytes32 public constant REMOVE_REL_TYPE_CONFIG = keccak256("REMOVE_REL_TYPE"); 94 | } 95 | -------------------------------------------------------------------------------- /contracts/lib/modules/Module.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // See https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf 3 | pragma solidity ^0.8.19; 4 | 5 | // This file contains module structures and constants used throughout Story Protocol. 6 | 7 | // A module key is identified by its keccak-256 encoded string identifier. 8 | type ModuleKey is bytes32; 9 | 10 | using { moduleKeyEquals as == } for ModuleKey global; 11 | using { moduleKeyNotEquals as != } for ModuleKey global; 12 | 13 | // A gateway's module dependencies are composed of a list of module keys 14 | // and a list of function selectors dependend on for each of these modules. 15 | struct ModuleDependencies { 16 | ModuleKey[] keys; 17 | bytes4[][] fns; 18 | } 19 | 20 | // Helper function for comparing equality between two keys. 21 | function moduleKeyEquals(ModuleKey k1, ModuleKey k2) pure returns (bool) { 22 | return ModuleKey.unwrap(k1) == ModuleKey.unwrap(k2); 23 | } 24 | 25 | // Helper function for comparing inequality between two keys. 26 | function moduleKeyNotEquals(ModuleKey k1, ModuleKey k2) pure returns (bool) { 27 | return ModuleKey.unwrap(k1) != ModuleKey.unwrap(k2); 28 | } 29 | 30 | // Transforms a string to its designated module key. 31 | function toModuleKey(string calldata moduleKey_) pure returns (ModuleKey) { 32 | return ModuleKey.wrap(keccak256(abi.encodePacked(moduleKey_))); 33 | } 34 | 35 | // String values for core protocol modules. 36 | string constant RELATIONSHIP_MODULE = "RELATIONSHIP_MODULE"; 37 | string constant LICENSING_MODULE = "LICENSING_MODULE"; 38 | string constant REGISTRATION_MODULE = "REGISTRATION_MODULE"; 39 | 40 | // Module key values for core protocol modules. 41 | ModuleKey constant RELATIONSHIP_MODULE_KEY = ModuleKey.wrap(keccak256(abi.encodePacked(RELATIONSHIP_MODULE))); 42 | ModuleKey constant LICENSING_MODULE_KEY = ModuleKey.wrap(keccak256(abi.encodePacked(LICENSING_MODULE))); 43 | ModuleKey constant REGISTRATION_MODULE_KEY = ModuleKey.wrap(keccak256(abi.encodePacked(REGISTRATION_MODULE))); 44 | -------------------------------------------------------------------------------- /contracts/lib/modules/ModuleRegistryKeys.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // See https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf 3 | pragma solidity ^0.8.19; 4 | 5 | library ModuleRegistryKeys { 6 | string public constant RELATIONSHIP_MODULE = "RELATIONSHIP_MODULE"; 7 | string public constant LICENSING_MODULE = "LICENSING_MODULE"; 8 | string public constant REGISTRATION_MODULE = "REGISTRATION_MODULE"; 9 | } 10 | -------------------------------------------------------------------------------- /contracts/lib/modules/Registration.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // See https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf 3 | pragma solidity ^0.8.19; 4 | 5 | /// @title Relationship Module Library 6 | library Registration { 7 | /// @notice IPOrg configuration settings. 8 | struct IPOrgConfig { 9 | string baseURI; 10 | string contractURI; 11 | string[] assetTypes; 12 | } 13 | 14 | /// @notice Struct used for IP asset registration. 15 | struct RegisterIPAssetParams { 16 | address owner; 17 | uint8 ipOrgAssetType; 18 | string name; 19 | bytes32 hash; 20 | string mediaUrl; 21 | } 22 | 23 | // TODO(leeren): Change in favor of granular function-selector based auth. 24 | 25 | // Constants used for determining module configuration logic. 26 | bytes32 public constant SET_IP_ORG_METADATA = keccak256("SET_IP_ORG_METADATA"); 27 | bytes32 public constant SET_IP_ORG_ASSET_TYPES = keccak256("SET_IP_ORG_ASSET_TYPES"); 28 | 29 | // Constants used for determining module execution logic. 30 | bytes32 public constant REGISTER_IP_ASSET = keccak256("REGISTER_IP_ASSET"); 31 | bytes32 public constant TRANSFER_IP_ASSET = keccak256("TRANSFER_IP_ASSET"); 32 | } 33 | -------------------------------------------------------------------------------- /contracts/lib/modules/Royalties.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // See https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf 3 | pragma solidity ^0.8.19; 4 | 5 | /// @title Royalties Module Library 6 | library Royalties { 7 | /// @notice Struct for configuring royalty allocations. 8 | struct ProportionData { 9 | address[] accounts; 10 | uint32[] percentAllocations; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /contracts/modules/Gateway.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // See https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf 3 | pragma solidity ^0.8.19; 4 | 5 | import { Errors } from "contracts/lib/Errors.sol"; 6 | import { IGateway } from "contracts/interfaces/modules/IGateway.sol"; 7 | import { ModuleDependencies } from "contracts/lib/modules/Module.sol"; 8 | import { ModuleRegistry } from "contracts/modules/ModuleRegistry.sol"; 9 | 10 | /// @title Module Gateway 11 | /// @notice This contract serves as the base layer all module "frontends" must 12 | /// extend. Protocol admins enroll gateways through the module registry, 13 | /// which give them access to call all module functions listed as part 14 | /// of their dependency set. 15 | abstract contract Gateway is IGateway { 16 | bool public registered; 17 | 18 | ModuleRegistry public immutable MODULE_REGISTRY; 19 | 20 | /// @notice Modifier that restricts the caller to only the module registry. 21 | modifier onlyModuleRegistry() { 22 | if (msg.sender != address(MODULE_REGISTRY)) { 23 | revert Errors.BaseModule_OnlyModuleRegistry(); 24 | } 25 | _; 26 | } 27 | 28 | constructor(ModuleRegistry moduleRegistry_) { 29 | MODULE_REGISTRY = moduleRegistry_; 30 | } 31 | 32 | /// @notice Synchronizes all downstream dependencies via the module registry. 33 | function updateDependencies() external virtual override returns (ModuleDependencies memory dependencies); 34 | 35 | /// @notice Fetches all module dependencies required by the gateway contract. 36 | function getDependencies() external view virtual override returns (ModuleDependencies memory dependencies); 37 | } 38 | -------------------------------------------------------------------------------- /contracts/utils/ShortStringOps.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // See https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf 3 | pragma solidity ^0.8.19; 4 | 5 | import { ShortString, ShortStrings } from "@openzeppelin/contracts/utils/ShortStrings.sol"; 6 | 7 | /// @notice Library for working with Openzeppelin's ShortString data types. 8 | library ShortStringOps { 9 | using ShortStrings for *; 10 | 11 | /// @dev Compares whether two ShortStrings are equal. 12 | function _equal(ShortString a, ShortString b) internal pure returns (bool) { 13 | return ShortString.unwrap(a) == ShortString.unwrap(b); 14 | } 15 | 16 | /// @dev Checks whether a ShortString and a regular string are equal. 17 | function _equal(ShortString a, string memory b) internal pure returns (bool) { 18 | return _equal(a, b.toShortString()); 19 | } 20 | 21 | /// @dev Checks whether a regular string and a ShortString are equal. 22 | function _equal(string memory a, ShortString b) internal pure returns (bool) { 23 | return _equal(a.toShortString(), b); 24 | } 25 | 26 | /// @dev Checks whether a bytes32 object and ShortString are equal. 27 | function _equal(bytes32 a, ShortString b) internal pure returns (bool) { 28 | return a == ShortString.unwrap(b); 29 | } 30 | 31 | /// @dev Checks whether a string and bytes32 object are equal. 32 | function _equal(string memory a, bytes32 b) internal pure returns (bool) { 33 | return _equal(a, ShortString.wrap(b)); 34 | } 35 | 36 | /// @dev Checks whether a bytes32 object and string are equal. 37 | function _equal(bytes32 a, string memory b) internal pure returns (bool) { 38 | return _equal(ShortString.wrap(a), b); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /deployment-11155111.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": { 3 | "AccessControlSingleton-Impl": "0xC7baFCDBF6d5A9Ad2a30b69cDBeD73BC5F82BA5E", 4 | "AccessControlSingleton-Proxy": "0xd9654A801c046e278DFF4C6E42c5c9c84C672ACD", 5 | "IPAssetRegistry": "0xd6290b1B0434F2c36059fEe5D68641E98eAf6A76", 6 | "IPOrgController-Impl": "0x47bE849d67Ff581547aa9d20D52e106b0c752593", 7 | "IPOrgController-Proxy": "0x13D263F6a9589230f5bC701255De1932FEB208B2", 8 | "LicenseRegistry": "0x18EE5EF9028B4eF991ABBa08cAf0e9B93FEFF3eC", 9 | "LicensingFrameworkRepo": "0x6Ab75A4BC359716F938580Ed65eB915D2D66E5C3", 10 | "LicensingModule": "0xAcc96c5086068d564BfE172836f923EE806ea067", 11 | "MockERC721": "0xE05C8F5C00613ca0b94C08405f70eC0220178708", 12 | "ModuleRegistry": "0x264f08d90BFfaEEDd7Dc64D638202bbe6e1cc6a3", 13 | "PolygonTokenHook": "0x688227771019F809A027f273525A454214506d5b", 14 | "RegistrationModule": "0xE8FE6bc42700a9202eBA4A4A9438B96de3C4F8c7", 15 | "RelationshipModule": "0x52566C3D0D063BCaC8D541D796bcf4e1A887cC46", 16 | "StoryProtocol": "0x22605a79BDFa75f7F1DA97d73F636AB5B5343039", 17 | "TokenGatedHook": "0xAADC8ae4936A911Ac90265852b66B6450cd0e570" 18 | } 19 | } -------------------------------------------------------------------------------- /foundry.toml: -------------------------------------------------------------------------------- 1 | [profile.default] 2 | src = 'contracts' 3 | out = 'out' 4 | libs = ['node_modules', 'lib'] 5 | cache_path = 'forge-cache' 6 | gas_reports = ["*"] 7 | optimizer = true 8 | optimizer_runs = 20000 9 | test = 'test' 10 | fs_permissions = [{ access = "read-write", path = "./"}] 11 | 12 | [rpc_endpoints] 13 | goerli = "${GOERLI_RPC_URL}" 14 | 15 | [etherscan] 16 | goerli = { key = "${ETHERSCAN_API_KEY}" } 17 | 18 | # See more config options https://github.com/foundry-rs/foundry/tree/master/config -------------------------------------------------------------------------------- /hardhat.config.js: -------------------------------------------------------------------------------- 1 | require("dotenv").config(); 2 | require('hardhat-deploy'); 3 | 4 | require("@nomiclabs/hardhat-etherscan"); 5 | require("@nomiclabs/hardhat-waffle"); 6 | require("hardhat-gas-reporter"); 7 | require("solidity-coverage"); 8 | require('@nomiclabs/hardhat-ethers'); 9 | require("@nomiclabs/hardhat-etherscan"); 10 | require('@openzeppelin/hardhat-upgrades'); 11 | require('solidity-docgen'); 12 | 13 | const namespacedStorageKey = require("./script/hardhat/namespacedStorageKey.js"); 14 | const defenderAdminProposal = require("./script/hardhat/defender/adminProposal.js"); 15 | 16 | const { task } = require("hardhat/config"); 17 | 18 | // This is a sample Hardhat task. To learn how to create your own go to 19 | // https://hardhat.org/guides/create-task.html 20 | task("accounts", "Prints the list of accounts", async (taskArgs, hre) => { 21 | const accounts = await hre.ethers.getSigners(); 22 | 23 | for (const account of accounts) { 24 | console.log(account.address); 25 | } 26 | }); 27 | 28 | task('sp:defender:admin-proposal') 29 | .addPositionalParam('proposalFileName', 'Name of the proposal file in scripts/hardhat/proposals, without .js extension') 30 | .setDescription('Creates a proposal on Defender, to be signed by the multisig') 31 | .setAction(defenderAdminProposal); 32 | 33 | task('sp:eip7201-key') 34 | .addPositionalParam('namespace', 'Namespace, for example erc7201:example.main') 35 | .setDescription('Get the namespaced storage key for https://eips.ethereum.org/EIPS/eip-7201') 36 | .setAction(namespacedStorageKey); 37 | 38 | // You need to export an object to set up your config 39 | // Go to https://hardhat.org/config/ to learn more 40 | 41 | /** 42 | * @type import('hardhat/config').HardhatUserConfig 43 | */ 44 | module.exports = { 45 | networks: { 46 | mainnet: { 47 | url: process.env.MAINNET_RPC_URL || "", 48 | chainId: 1, 49 | accounts: [process.env.MAINNET_PRIVATEKEY || "0x1234567890123456789012345678901234567890123456789012345678901234"] 50 | }, 51 | sepolia: { 52 | url: process.env.SEPOLIA_RPC_URL || "", 53 | chainId: 11155111, 54 | accounts: [process.env.SEPOLIA_PRIVATEKEY || "0x1234567890123456789012345678901234567890123456789012345678901234"] 55 | }, 56 | local: { 57 | url: "http://127.0.0.1:8545", 58 | chainId: 31337, 59 | accounts: { 60 | mnemonic: "test test test test test test test test test test test junk", 61 | } 62 | } 63 | }, 64 | gasReporter: { 65 | enabled: process.env.REPORT_GAS !== undefined, 66 | currency: "USD", 67 | }, 68 | solidity: { 69 | version: "0.8.19", 70 | settings: { 71 | optimizer: { 72 | enabled: true, 73 | runs: 2000 74 | } 75 | } 76 | }, 77 | etherscan: { 78 | apiKey: `${process.env.ETHERSCAN_API_KEY}` 79 | }, 80 | paths: { 81 | sources: "./contracts", 82 | tests: "./test", 83 | cache: "./cache", 84 | artifacts: "./artifacts" 85 | }, 86 | mocha: { 87 | timeout: 20000 88 | }, 89 | docgen: { 90 | outputDir: "./docs", 91 | pages: "files" 92 | } 93 | }; 94 | -------------------------------------------------------------------------------- /image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storyprotocol/protocol-contracts/af73b9d39a1d001472828558b0c1ed0a46c326d7/image.png -------------------------------------------------------------------------------- /legacy/contracts/interfaces/ip-accounts/IERC6551Account.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.13; 3 | 4 | /// @dev the ERC-165 identifier for this interface is `0x6faff5f1` 5 | interface IERC6551Account { 6 | 7 | /// @dev Allows the account to receive Ether 8 | /// Accounts MUST implement a `receive` function 9 | /// Accounts MAY perform arbitrary logic to restrict conditions 10 | /// under which Ether can be received 11 | receive() external payable; 12 | 13 | 14 | /// @dev Returns the identifier of the non-fungible token which owns the account 15 | /// The return value of this function MUST be constant - it MUST NOT change over time 16 | /// @return chainId The EIP-155 ID of the chain the token exists on 17 | /// @return tokenContract The contract address of the token 18 | /// @return tokenId The ID of the token 19 | function token() 20 | external 21 | view 22 | returns (uint256 chainId, address tokenContract, uint256 tokenId); 23 | 24 | 25 | /// @dev Returns a value that SHOULD be modified each time the account changes state 26 | /// @return The current account state 27 | function state() external view returns (uint256); 28 | 29 | 30 | /// @dev Returns a magic value indicating whether a given signer is authorized to act on behalf 31 | /// of the account 32 | /// MUST return the bytes4 magic value 0x523e3260 if the given signer is valid 33 | /// By default, the holder of the non-fungible token the account is bound to MUST be considered 34 | /// a valid signer 35 | /// Accounts MAY implement additional authorization logic which invalidates the holder as a 36 | /// signer or grants signing permissions to other non-holder accounts 37 | /// @param signer The address to check signing authorization for 38 | /// @param context Additional data used to determine whether the signer is valid 39 | /// @return magicValue Magic value indicating whether the signer is valid 40 | function isValidSigner( 41 | address signer, 42 | bytes calldata context 43 | ) external view returns (bytes4 magicValue); 44 | } 45 | -------------------------------------------------------------------------------- /legacy/contracts/interfaces/ip-accounts/IIPAccount.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.13; 3 | 4 | import { IERC721Receiver } from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; 5 | import { IERC1155Receiver } from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol"; 6 | import { IERC6551Account } from "./IERC6551Account.sol"; 7 | 8 | interface IIPAccount is IERC6551Account, IERC721Receiver, IERC1155Receiver { 9 | function safeTransferFrom( 10 | address nftContract_, 11 | address from_, 12 | address to_, 13 | uint256 tokenId_ 14 | ) external; 15 | 16 | function sendRoyaltyForDistribution( 17 | address distributor_, 18 | address token_ 19 | ) external; 20 | } 21 | -------------------------------------------------------------------------------- /legacy/contracts/interfaces/ip-accounts/IIPAccountRegistry.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.13; 3 | 4 | interface IIPAccountRegistry { 5 | 6 | event AccountCreated( 7 | address account, 8 | address implementation, 9 | uint256 chainId, 10 | address tokenContract, 11 | uint256 tokenId, 12 | uint256 salt 13 | ); 14 | 15 | function createAccount( 16 | uint256 chainId_, 17 | address tokenContract_, 18 | uint256 tokenId_, 19 | bytes calldata initData 20 | ) external returns (address); 21 | 22 | function account( 23 | uint256 chainId_, 24 | address tokenContract_, 25 | uint256 tokenId_ 26 | ) external view returns (address); 27 | 28 | 29 | /// @notice Returns the IPAccount implementation address. 30 | /// @return address The IPAccount implementation address. 31 | function getIpAccountImpl() external view returns (address); 32 | } 33 | -------------------------------------------------------------------------------- /legacy/contracts/interfaces/modules/collect/ICollectModule.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.18; 3 | 4 | import { Collect } from "contracts/lib/modules/Collect.sol"; 5 | 6 | /// @title Collect Module Interface 7 | /// @notice The collect module enables IP assets to be minted as NFTs mirroring 8 | /// their linking IP assets in a franchise-configurable format. 9 | interface ICollectModule { 10 | 11 | /// @dev Emits when a Collect action is invoked. 12 | /// TODO: Add logging for franchise and ipAssetOrg 13 | event Collected( 14 | uint256 indexed ipAssetId_, 15 | address indexed collector_, 16 | address collectNft_, 17 | uint256 collectNftId_, 18 | bytes collectData_, 19 | bytes collectNftData_ 20 | ); 21 | 22 | /// @dev Emits when a new collect NFT is deployed. 23 | /// TODO: Add logging for franchise and ipAssetOrg 24 | event NewCollectNFT( 25 | uint256 indexed ipAssetId_, 26 | address collectNFT_ 27 | ); 28 | 29 | /// @notice Initializes the collect module for a specific IP asset. 30 | /// @param initCollectParams_ Collect module init data, including IP asset 31 | /// id, collect NFT impl address, and generic unformatted init data. 32 | function initCollect(Collect.InitCollectParams calldata initCollectParams_) external; 33 | 34 | /// @notice Performs a collect on a specific IP asset for a collector. 35 | /// @param collectParams_ Collect module collect data, including IP asset id, 36 | /// collector address, and generic unformatted collect and NFT data. 37 | /// @return collectNft The address of the collected NFT. 38 | /// @return collectNftId The id of the collected collect NFT. 39 | function collect( 40 | Collect.CollectParams calldata collectParams_ 41 | ) external payable returns (address collectNft, uint256 collectNftId); 42 | 43 | /// @notice Returns the collect NFT address associated with an IP asset. 44 | /// @param ipAssetId_ The id of the specified IP asset within the franchise. 45 | /// @return The Collect NFT address if it exists, else the zero address. 46 | function getCollectNFT( 47 | uint256 ipAssetId_ 48 | ) external returns (address); 49 | } 50 | -------------------------------------------------------------------------------- /legacy/contracts/interfaces/modules/collect/ICollectNFT.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.18; 3 | 4 | import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; 5 | 6 | import { Collect } from "contracts/lib/modules/Collect.sol"; 7 | 8 | /// @title Collect NFT Interface 9 | /// @notice Contracts implementing the Collect NFT interface may be collected 10 | /// through a collect module for a bound IP asset collection. 11 | interface ICollectNFT is IERC721 { 12 | /// @notice Initializes a collect NFT for subsequent collection. 13 | /// @param initParams_ Collect NFT init data, including bound franchise IP 14 | /// asset registry, IP asset id, and generic unformatted init data. 15 | function initialize(Collect.InitCollectNFTParams calldata initParams_) external; 16 | 17 | /// @notice Performs a collect, minting the NFT to address `collector`. 18 | /// @param collector_ The address of the target designated for collection. 19 | /// @param data_ Additional unformatted bytes data for optional processing. 20 | /// @return tokenId_ The id of the minted collect NFT. 21 | function collect( 22 | address collector_, 23 | bytes calldata data_ 24 | ) external returns (uint256); 25 | 26 | /// @notice Returns the total # of collect NFTs that exist for an IP asset. 27 | /// @return The total number of collect NFTs in the collection. 28 | function totalSupply() external view returns (uint256); 29 | } 30 | -------------------------------------------------------------------------------- /legacy/contracts/interfaces/modules/collect/ICollectPaymentModule.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.18; 3 | 4 | import { Collect } from "contracts/lib/modules/Collect.sol"; 5 | import { ICollectModule } from "./ICollectModule.sol"; 6 | 7 | /// @title Collect Payment Module Interface 8 | /// @notice The collect payment module enables IP assets to be bound to NFTs 9 | /// that can be minted for a configurable fee. 10 | interface ICollectPaymentModule is ICollectModule { 11 | /// @notice Initializes the collect payment module for a specific IP asset. 12 | /// @param initCollectParams_ Collect module init data, including IP asset 13 | /// id, collect NFT impl address, and payment module init data. 14 | function initCollect( 15 | Collect.InitCollectParams calldata initCollectParams_ 16 | ) external override(ICollectModule); 17 | 18 | /// @notice Performs a collect on a specific IP asset, processing the module 19 | /// configured payment in the process. 20 | /// @param collectParams_ Collect module collect data, including IP asset id, 21 | /// collector address, and collect payment module processing data. 22 | /// @return collectNft The address of the collected NFT. 23 | /// @return collectNftId The id of the collected collect NFT. 24 | function collect( 25 | Collect.CollectParams calldata collectParams_ 26 | ) 27 | external 28 | payable 29 | override(ICollectModule) 30 | returns (address collectNft, uint256 collectNftId); 31 | 32 | /// @notice Returns the collect payment info associated with an IP asset. 33 | /// @param ipAssetId_ The id of the specified IP asset. 34 | /// @return Payment info associated with the configured IP asset collect. 35 | function getPaymentInfo( 36 | uint256 ipAssetId_ 37 | ) external view returns (Collect.CollectPaymentInfo memory); 38 | } 39 | -------------------------------------------------------------------------------- /legacy/contracts/interfaces/modules/royalties/IRoyaltyDistributor.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.19; 3 | 4 | /// @title Royalty Module Interface 5 | /// @notice The Royalty module distribute royalty/revenue across IP graph. 6 | /// It supports customized royalty distributing policies for each individual IP Asset. 7 | interface IRoyaltyDistributor { 8 | 9 | /// @dev Emitted when distribute royalty token. 10 | /// 11 | /// @param sourceAccount The address of account from which distribute royalty to other recipients. 12 | /// @param token The ERC20 royalty token. 13 | /// @param amount The amount of ERC20 royalty token are distributed. 14 | event DistributeRoyalties( 15 | address indexed sourceAccount, 16 | address indexed token, 17 | uint256 amount 18 | ); 19 | 20 | /// @dev Emitted after withdraw royalty to payee's account. 21 | /// 22 | /// @param account The address of account to which withdraw royalty. 23 | /// @param token The ERC20 royalty token. 24 | /// @param amount The amount of ERC20 royalty token are claimed. 25 | event ClaimRoyalties( 26 | address indexed account, 27 | address indexed token, 28 | uint256 amount 29 | ); 30 | 31 | /// @dev Emitted after set RoyaltyPolicy to specified IP Account. 32 | /// 33 | /// @param account The IP Account to which set royalty policy. 34 | /// @param royaltyPolicy the address of royalty policy implements IRoyaltyPolicy interface. 35 | event SetRoyaltyPolicy( 36 | address indexed account, 37 | address indexed royaltyPolicy 38 | ); 39 | 40 | /// @dev Emitted after updated the royalty distribution plan. 41 | /// 42 | /// @param account The IP Account from which distribute the royalty. 43 | event UpdateDistribution(address indexed account); 44 | 45 | /// @notice Set royalty policy to specified IP Asset. 46 | /// @param nftContract_ address of NFT collection contract. 47 | /// @param tokenId_ The NFT token Id of NFT collection contract. 48 | /// @param royaltyPolicy_ The royalty distribution policy. 49 | /// @param data_ The initial data of the royalty distribution policy. 50 | function setRoyaltyPolicy(address nftContract_, uint256 tokenId_, address royaltyPolicy_, bytes calldata data_) external; 51 | 52 | /// @notice update royalty distribution plan for given IP Asset. 53 | /// @param nftContract_ address of NFT collection contract. 54 | /// @param tokenId_ The NFT token Id of NFT collection contract. 55 | /// @param data_ The royalty distribution plan data. 56 | function updateDistribution(address nftContract_, uint256 tokenId_, bytes calldata data_) external; 57 | 58 | /// @notice distribute royalty to each recipient according to royalty distribution plan for given IP Asset. 59 | /// @param nftContract_ address of NFT collection contract. 60 | /// @param tokenId_ The NFT token Id of NFT collection contract. 61 | /// @param token_ The ERC20 token for royalty. 62 | function distribute(address nftContract_, uint256 tokenId_, address token_) external; 63 | 64 | /// @notice claim royalty to account. 65 | /// @param account_ address of the account to which withdraw royalty which distributed before. 66 | function claim(address account_, address token_) external; 67 | 68 | /// @notice pause the royalty distribution. 69 | function pause() external; 70 | 71 | /// @notice unpause the royalty distribution. 72 | function unpause() external; 73 | 74 | /// @notice Get royalty policy for specified IP Asset. 75 | /// @param nftContract_ address of NFT collection contract. 76 | /// @param tokenId_ The NFT token Id of NFT collection contract. 77 | /// @return The address of royalty distribution policy. 78 | function getRoyaltyPolicy(address nftContract_, uint256 tokenId_) external view returns (address); 79 | } 80 | -------------------------------------------------------------------------------- /legacy/contracts/interfaces/modules/royalties/IRoyaltyPolicy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.19; 3 | 4 | /// @title Royalty Policy Interface 5 | /// @notice The Royalty policy perform concrete operations of distributing royalty. 6 | interface IRoyaltyPolicy { 7 | 8 | /// @notice initialize the royalty policy for the specified IP Account 9 | /// @param account_ IP Account associated with the policy. 10 | /// @param data_ The initial data of the royalty distribution policy. 11 | function initPolicy(address account_, bytes calldata data_) external; 12 | 13 | /// @notice update distribute plan for the specified IP Account 14 | /// @param account_ IP Account associated with the policy. 15 | /// @param data_ The distribution plan data. 16 | function updateDistribution(address account_, bytes calldata data_) external; 17 | 18 | /// @notice Distribute royalty to each recipient according to royalty distribution plan for given IP Asset. 19 | /// @param account_ IP Account associated with the policy. 20 | /// @param token_ The ERC20 token for royalty. 21 | function distribute(address account_, address token_) external; 22 | } 23 | -------------------------------------------------------------------------------- /legacy/contracts/interfaces/modules/royalties/ISplitMain.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import { ERC20 } from "solmate/src/tokens/ERC20.sol"; 5 | 6 | interface ISplitMain { 7 | 8 | function createSplit( 9 | address[] calldata accounts, 10 | uint32[] calldata percentAllocations, 11 | uint32 distributorFee, 12 | address controller 13 | ) external returns (address); 14 | 15 | function updateAndDistributeETH( 16 | address split, 17 | address[] calldata accounts, 18 | uint32[] calldata percentAllocations, 19 | uint32 distributorFee, 20 | address distributorAddress 21 | ) external; 22 | 23 | function updateAndDistributeERC20( 24 | address split, 25 | ERC20 token, 26 | address[] calldata accounts, 27 | uint32[] calldata percentAllocations, 28 | uint32 distributorFee, 29 | address distributorAddress 30 | ) external; 31 | 32 | function withdraw(address account, uint256 withdrawETH, ERC20[] calldata tokens) external; 33 | 34 | function getETHBalance(address account) external view returns (uint256); 35 | 36 | function getERC20Balance(address account, ERC20 token) external view returns (uint256); 37 | } 38 | -------------------------------------------------------------------------------- /legacy/contracts/interfaces/modules/royalties/policies/IRoyaltyPolicy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.19; 3 | 4 | /// @title Royalty Policy Interface 5 | /// @notice The Royalty policy perform concrete operations of distributing royalty. 6 | interface IRoyaltyPolicy { 7 | 8 | /// @notice initialize the royalty policy for the specified IP Account 9 | /// @param account_ IP Account associated with the policy. 10 | /// @param data_ The initial data of the royalty distribution policy. 11 | function initPolicy(address account_, bytes calldata data_) external; 12 | 13 | /// @notice update distribute plan for the specified IP Account 14 | /// @param account_ IP Account associated with the policy. 15 | /// @param data_ The distribution plan data. 16 | function updateDistribution(address account_, bytes calldata data_) external; 17 | 18 | /// @notice Distribute royalty to each recipient according to royalty distribution plan for given IP Asset. 19 | /// @param account_ IP Account associated with the policy. 20 | /// @param token_ The ERC20 token for royalty. 21 | function distribute(address account_, address token_) external; 22 | } 23 | -------------------------------------------------------------------------------- /legacy/contracts/modules/collect/SimpleCollectModule.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // See Story Protocol Alpha Agreement: https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf 3 | pragma solidity ^0.8.18; 4 | 5 | import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; 6 | import { AccessControl } from "contracts/lib/AccessControl.sol"; 7 | import { CollectModuleBase } from "contracts/modules/collect/CollectModuleBase.sol"; 8 | 9 | /// @title Simple Collect Module 10 | /// @notice This simple collect module links IP assets to mintable NFTs. 11 | contract SimpleCollectModule is CollectModuleBase { 12 | 13 | /// @notice Initializes a mock collect module. 14 | /// @param franchise_ The protocol-wide franchise registry address. 15 | /// @param defaultCollectNftImpl_ The default collect NFT impl address. 16 | constructor(address franchise_, address defaultCollectNftImpl_) CollectModuleBase(franchise_, defaultCollectNftImpl_) {} 17 | 18 | /// @notice Initializes the collect module via UUPS proxying. 19 | /// @param accessControl_ The address utilized for contract access control. 20 | function initialize(address accessControl_) public initializer { 21 | __UUPSUpgradeable_init(); 22 | __AccessControlledUpgradeable_init(accessControl_); 23 | } 24 | 25 | /// @dev Additional authorization necessitated by UUPS module upgrades. 26 | function _authorizeUpgrade(address newImplementation_) internal override onlyRole(AccessControl.UPGRADER_ROLE) {} 27 | 28 | /// @dev Checks whether the collect action is authorized for an IP asset. 29 | function _isCollectAuthorized(uint256 ipAssetId_) internal view override returns (bool) { 30 | address ipAssetOrg = REGISTRY.ipAssetOrg(ipAssetId_); 31 | return msg.sender == ipAssetOrg; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /legacy/contracts/modules/royalties/RoyaltyDistributor.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // See Story Protocol Alpha Agreement: https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf 3 | pragma solidity ^0.8.19; 4 | 5 | import { IRoyaltyDistributor } from "contracts/interfaces/modules/royalties/IRoyaltyDistributor.sol"; 6 | import { Pausable } from "@openzeppelin/contracts/security/Pausable.sol"; 7 | import { AccessControlledUpgradeable } from "contracts/access-control/AccessControlledUpgradeable.sol"; 8 | import { IIPAccountRegistry } from "contracts/interfaces/ip-accounts/IIPAccountRegistry.sol"; 9 | import { IRoyaltyPolicy } from "contracts/interfaces/modules/royalties/policies/IRoyaltyPolicy.sol"; 10 | import { RoyaltyNFT } from "contracts/modules/royalties/RoyaltyNFT.sol"; 11 | import { AccessControl } from "contracts/lib/AccessControl.sol"; 12 | 13 | contract RoyaltyDistributor is Pausable, IRoyaltyDistributor, AccessControlledUpgradeable { 14 | 15 | IIPAccountRegistry public immutable ipAccountRegistry; 16 | RoyaltyNFT public immutable royaltyNFT; 17 | 18 | // ipAccount => royaltyPolicy 19 | mapping(address => address) private policies; 20 | 21 | constructor(address ipAccountRegistry_, address royaltyNft_) { 22 | ipAccountRegistry = IIPAccountRegistry(ipAccountRegistry_); 23 | royaltyNFT = RoyaltyNFT(royaltyNft_); 24 | } 25 | 26 | function setRoyaltyPolicy( 27 | address nftContract_, 28 | uint256 tokenId_, 29 | address royaltyPolicy_, 30 | bytes calldata data_ 31 | ) external { 32 | address ipAccount = _ipAccount(nftContract_, tokenId_); 33 | policies[ipAccount] = royaltyPolicy_; 34 | IRoyaltyPolicy(royaltyPolicy_).initPolicy(ipAccount, data_); 35 | } 36 | 37 | function updateDistribution( 38 | address nftContract_, 39 | uint256 tokenId_, 40 | bytes calldata data_ 41 | ) external { 42 | address ipAccount = ipAccountRegistry.createAccount(block.chainid, nftContract_, tokenId_, ""); 43 | IRoyaltyPolicy(policies[ipAccount]).updateDistribution(ipAccount, data_); 44 | } 45 | 46 | function distribute(address nftContract_, uint256 tokenId_, address token_) external { 47 | address ipAccount = _ipAccount(nftContract_, tokenId_); 48 | IRoyaltyPolicy(policies[ipAccount]).distribute(ipAccount, token_); 49 | } 50 | 51 | function claim(address account_, address token_) external { 52 | royaltyNFT.claim(account_, token_); 53 | } 54 | 55 | function pause() external onlyRole(AccessControl.PROTOCOL_ADMIN_ROLE) { 56 | _pause(); 57 | } 58 | function unpause() external onlyRole(AccessControl.PROTOCOL_ADMIN_ROLE) { 59 | _unpause(); 60 | } 61 | 62 | function getRoyaltyPolicy( 63 | address nftContract_, 64 | uint256 tokenId_ 65 | ) external view returns (address) { 66 | address ipAccount = _ipAccount(nftContract_, tokenId_); 67 | return policies[ipAccount]; 68 | } 69 | 70 | function _authorizeUpgrade( 71 | address newImplementation_ 72 | ) internal virtual override onlyRole(AccessControl.UPGRADER_ROLE) {} 73 | 74 | function _ipAccount(address nftContract_, uint256 tokenId_) internal view returns(address) { 75 | return ipAccountRegistry.account(block.chainid, nftContract_, tokenId_); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /legacy/contracts/modules/royalties/RoyaltyNFTFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // See Story Protocol Alpha Agreement: https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf 3 | pragma solidity ^0.8.19; 4 | 5 | import { RoyaltyNFT } from "./RoyaltyNFT.sol"; 6 | import { Clones } from "@openzeppelin/contracts/proxy/Clones.sol"; 7 | 8 | contract RoyaltyNFTFactory { 9 | event CreateRoyaltyNFT(RoyaltyNFT indexed royaltyNft); 10 | 11 | address public immutable royaltyNft; 12 | 13 | constructor(address splitMain_) { 14 | royaltyNft = address(new RoyaltyNFT(splitMain_)); 15 | } 16 | 17 | function createRoyaltyNft( 18 | bytes32 salt_ 19 | ) external returns (RoyaltyNFT rn) { 20 | rn = RoyaltyNFT(Clones.cloneDeterministic(royaltyNft, salt_)); 21 | emit CreateRoyaltyNFT(rn); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /legacy/contracts/modules/royalties/policies/MutableRoyaltyProportionPolicy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // See Story Protocol Alpha Agreement: https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf 3 | pragma solidity ^0.8.19; 4 | 5 | import { IRoyaltyPolicy } from "contracts/interfaces/modules/royalties/policies/IRoyaltyPolicy.sol"; 6 | import { RoyaltyNFT } from "contracts/modules/royalties/RoyaltyNFT.sol"; 7 | import { Royalties } from "contracts/lib/modules/Royalties.sol"; 8 | 9 | contract MutableRoyaltyProportionPolicy is IRoyaltyPolicy { 10 | RoyaltyNFT public immutable royaltyNFT; 11 | 12 | constructor(address royaltyNft_) { 13 | royaltyNFT = RoyaltyNFT(royaltyNft_); 14 | } 15 | 16 | function initPolicy(address, bytes calldata) override external {} 17 | 18 | function updateDistribution(address sourceAccount_, bytes calldata data_) override external { 19 | Royalties.ProportionData memory propData = abi.decode(data_, (Royalties.ProportionData)); 20 | uint256 tokenId = royaltyNFT.toTokenId(sourceAccount_); 21 | if (!royaltyNFT.exists(tokenId)) { 22 | royaltyNFT.mint(sourceAccount_, propData.accounts, propData.percentAllocations); 23 | } else { 24 | for (uint256 i = 0; i < propData.accounts.length; ++i) { 25 | royaltyNFT.safeTransferFrom( 26 | sourceAccount_, 27 | propData.accounts[i], 28 | tokenId, 29 | propData.percentAllocations[i], 30 | "" 31 | ); 32 | } 33 | } 34 | } 35 | 36 | function distribute(address account_, address token_) override external { 37 | royaltyNFT.distributeFunds(account_, token_); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /legacy/test/foundry/IPAccountRegistry.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSDL-1.1 2 | pragma solidity ^0.8.13; 3 | 4 | import "forge-std/Test.sol"; 5 | 6 | import { Errors } from "contracts/lib/Errors.sol"; 7 | import "contracts/ip-accounts/IPAccountRegistry.sol"; 8 | import "mvp/test/foundry/mocks/MockIPAccount.sol"; 9 | 10 | contract RegistryTest is Test { 11 | IPAccountRegistry public registry; 12 | MockIPAccount public implementation; 13 | uint256 chainId; 14 | address tokenAddress; 15 | uint256 tokenId; 16 | 17 | error IpAccountInitializationFailed(); 18 | 19 | function setUp() public { 20 | implementation = new MockIPAccount(); 21 | registry = new IPAccountRegistry(address(implementation)); 22 | chainId = 100; 23 | tokenAddress = address(200); 24 | tokenId = 300; 25 | } 26 | 27 | function test_createAccount() public { 28 | address ipAccountAddr; 29 | ipAccountAddr = registry.createAccount( 30 | chainId, 31 | tokenAddress, 32 | tokenId, 33 | abi.encodeWithSignature("foo(bool)", true) 34 | ); 35 | 36 | address registryComputedAddress = registry.account( 37 | chainId, 38 | tokenAddress, 39 | tokenId 40 | ); 41 | assertEq(ipAccountAddr, registryComputedAddress); 42 | 43 | IPAccountImpl ipAccount = IPAccountImpl(payable(ipAccountAddr)); 44 | 45 | (uint256 chainId_, address tokenAddress_, uint256 tokenId_) = ipAccount.token(); 46 | assertEq(chainId_, chainId); 47 | assertEq(tokenAddress_, tokenAddress); 48 | assertEq(tokenId_, tokenId); 49 | } 50 | 51 | function test_revert_createAccount_ifInitFailed() public { 52 | vm.expectRevert(Errors.IPAccountRegistry_InitializationFailed.selector); 53 | registry.createAccount( 54 | chainId, 55 | tokenAddress, 56 | tokenId, 57 | abi.encodeWithSignature("foo(bool)", false) 58 | ); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /legacy/test/foundry/RoyaltyDistributorFork.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSDL-1.1 2 | pragma solidity ^0.8.13; 3 | 4 | import "mvp/test/foundry/RoyaltyDistributor.t.sol"; 5 | 6 | contract RoyaltyDistributorForkTest is RoyaltyDistributorTest { 7 | function _getSplitMain() internal virtual override returns(ISplitMain) { 8 | string memory mainnetRpc; 9 | console.log("fork mainnet"); 10 | try vm.envString("MAINNET_RPC_URL") returns (string memory rpcUrl) { 11 | mainnetRpc = rpcUrl; 12 | console.log("Using mainnet RPC in environment variable"); 13 | } catch { 14 | fail("Missing environment variable 'MAINNET_RPC_URL'"); 15 | } 16 | uint256 mainnetFork = vm.createFork(mainnetRpc); 17 | vm.selectFork(mainnetFork); 18 | assertEq(vm.activeFork(), mainnetFork); 19 | return ISplitMain(0x2ed6c4B5dA6378c7897AC67Ba9e43102Feb694EE); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /legacy/test/foundry/_old_modules/collect/SimpleCollectModule.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // See Story Protocol Alpha Agreement: https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf 3 | pragma solidity ^0.8.18; 4 | 5 | import { SimpleCollectModule } from "contracts/modules/collect/SimpleCollectModule.sol"; 6 | import { BaseCollectModuleTest } from "./BaseCollectModuleTest.sol"; 7 | import { Collect } from "contracts/lib/modules/Collect.sol"; 8 | import { Errors } from "contracts/lib/Errors.sol"; 9 | 10 | /// @title Simple Collect Module Testing Contract 11 | contract SimpleCollectModuleTest is BaseCollectModuleTest { 12 | 13 | function setUp() public virtual override(BaseCollectModuleTest) { 14 | super.setUp(); 15 | } 16 | 17 | /// @notice Tests that unauthorized collects revert. 18 | // function test_CollectModuleCollectUnauthorizedReverts(uint8 ipAssetType) createIpAsset(collector, ipAssetType) public { 19 | // vm.prank(alice); 20 | // vm.expectRevert(Errors.CollectModule_CollectUnauthorized.selector); 21 | // collectModule.collect(Collect.CollectParams({ 22 | // ipAssetId: ipAssetId, 23 | // collector: collector, 24 | // collectData: "", 25 | // collectNftInitData: "", 26 | // collectNftData: "" 27 | // })); 28 | // } 29 | 30 | // /// @notice Tests that upgrades work as expected. 31 | // function test_CollectModuleUpgrade() public { 32 | // address newCollectModuleImpl = address(new SimpleCollectModule(address(registry), defaultCollectNftImpl)); 33 | // vm.prank(upgrader); 34 | 35 | // bytes memory data = abi.encodeWithSelector( 36 | // bytes4(keccak256(bytes("DEFAULT_COLLECT_NFT_IMPL()"))) 37 | // ); 38 | // (bool success, ) = address(collectModule).call( 39 | // abi.encodeWithSignature( 40 | // "upgradeToAndCall(address,bytes)", 41 | // newCollectModuleImpl, 42 | // data 43 | // ) 44 | // ); 45 | // assertTrue(success); 46 | // } 47 | 48 | 49 | // /// @notice Tests whether collect reverts if the IP asset being collected from does not exist. 50 | // function test_CollectModuleCollectNonExistentIPAssetReverts(uint256 nonExistentipAssetId, uint8 ipAssetType) createIpAsset(collector, ipAssetType) public virtual override { 51 | // vm.assume(nonExistentipAssetId != ipAssetId); 52 | // vm.expectRevert(); 53 | // _collect(99); 54 | // } 55 | 56 | /// @notice Changes the base testing collect module deployment to deploy the mock payment collect module instead. 57 | function _deployCollectModule(address collectNftImpl) internal virtual override returns (address) { 58 | collectModuleImpl = address(new SimpleCollectModule(address(registry), collectNftImpl)); 59 | 60 | return _deployUUPSProxy( 61 | collectModuleImpl, 62 | abi.encodeWithSelector( 63 | bytes4(keccak256(bytes("initialize(address)"))), address(accessControl) 64 | ) 65 | ); 66 | 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /legacy/test/foundry/mocks/MockCollectModule.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // See Story Protocol Alpha Agreement: https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf 3 | pragma solidity ^0.8.18; 4 | 5 | import { CollectModuleBase } from "mvp/contracts/modules/collect/CollectModuleBase.sol"; 6 | 7 | /// @title Mock Collect Module 8 | /// @notice This mock contract is used for testing the base collect module. 9 | contract MockCollectModule is CollectModuleBase { 10 | // Whether the collect module is enabled for a specific IP asset. 11 | mapping(uint256 => mapping(uint256 => bool)) collectEnabled; 12 | 13 | /// @notice Initializes a mock collect module. 14 | /// @param registry_ The protocol-wide franchise registry address. 15 | /// @param defaultCollectNftImpl_ The default collect NFT impl address. 16 | constructor( 17 | address registry_, 18 | address defaultCollectNftImpl_ 19 | ) CollectModuleBase(registry_, defaultCollectNftImpl_) {} 20 | 21 | /// @notice Initializes the collect module via UUPS proxying. 22 | /// @param accessControl_ The address utilized for contract access control. 23 | function initialize(address accessControl_) public initializer {} 24 | 25 | /// @dev Checks whether the collect action is authorized for an IP asset. 26 | function _isCollectAuthorized( 27 | uint256 28 | ) internal pure virtual override returns (bool) { 29 | return true; 30 | } 31 | 32 | /// @dev Additional authorization necessitated by UUPS module upgrades. 33 | function _authorizeUpgrade(address newImplementation) internal override {} 34 | } 35 | -------------------------------------------------------------------------------- /legacy/test/foundry/mocks/MockCollectModuleERC721.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSDL-1.1 2 | pragma solidity ^0.8.18; 3 | 4 | import { ERC721 } from "contracts/modules/collect/nft/ERC721.sol"; 5 | 6 | /// @title Mock Collect Module ERC721 7 | /// @notice This mock ERC-721 is used for testing the minimal collect ERC-721. 8 | contract MockCollectModuleERC721 is ERC721 { 9 | 10 | /// @notice Mints NFT `tokenId` to address `to`. 11 | /// @param to The address of the newly minted NFT owner. 12 | /// @param tokenId The id of the NFT being minted. 13 | function mint(address to, uint256 tokenId) external { 14 | _mint(to, tokenId); 15 | } 16 | 17 | /// @notice Burns NFT `tokenId`. 18 | /// @param tokenId The id of the NFT being burned. 19 | function burn(uint256 tokenId) external { 20 | _burn(tokenId); 21 | } 22 | 23 | /// @notice Returns the total supply of the NFT collection. 24 | /// @return The total number of NFTs in the collection. 25 | function totalSupply() external view returns (uint256) { 26 | return _totalSupply; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /legacy/test/foundry/mocks/MockCollectNFT.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // See Story Protocol Alpha Agreement: https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf 3 | pragma solidity ^0.8.18; 4 | 5 | import { CollectNFTBase } from "mvp/contracts/modules/collect/nft/CollectNFTBase.sol"; 6 | 7 | /// @title Mock Collect NFT 8 | /// @notice This contract is used for testing base collect NFT functionality. 9 | contract MockCollectNFT is CollectNFTBase {} 10 | -------------------------------------------------------------------------------- /legacy/test/foundry/mocks/MockCollectPaymentModule.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // See Story Protocol Alpha Agreement: https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf 3 | pragma solidity ^0.8.18; 4 | 5 | import { CollectPaymentModuleBase } from "contracts/modules/collect/CollectPaymentModuleBase.sol"; 6 | 7 | /// @title Mock Payment Collect Module 8 | /// @notice Mock contract used for testing the base payment collect module. 9 | contract MockCollectPaymentModule is CollectPaymentModuleBase { 10 | 11 | // Whether the collect module is enabled for a specific IP asset. 12 | mapping(uint256 => mapping(uint256 => bool)) collectEnabled; 13 | 14 | /// @notice Initializes a mock collect payment module. 15 | /// @param franchise The protocol-wide franchise registry address. 16 | /// @param defaultCollectNftImpl The default collect NFT impl address. 17 | constructor(address franchise, address defaultCollectNftImpl) CollectPaymentModuleBase(franchise, defaultCollectNftImpl) {} 18 | 19 | /// @notice Initializes the collect module via UUPS proxying. 20 | /// @param accessControl The address utilized for contract access control. 21 | function initialize(address accessControl) public initializer {} 22 | 23 | /// @dev Checks whether the collect action is authorized for an IP asset. 24 | function _isCollectAuthorized(uint256) internal pure override returns (bool) { 25 | return true; 26 | } 27 | 28 | /// @dev Additional authorization necessitated by UUPS module upgrades. 29 | function _authorizeUpgrade(address newImplementation) internal override {} 30 | } 31 | -------------------------------------------------------------------------------- /legacy/test/foundry/mocks/MockIPAccount.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSDL-1.1 2 | pragma solidity ^0.8.13; 3 | 4 | import "contracts/ip-accounts/IPAccountImpl.sol"; 5 | 6 | contract MockIPAccount is IPAccountImpl { 7 | constructor() IPAccountImpl() { 8 | } 9 | 10 | function foo(bool val) pure external { 11 | if (!val) { 12 | revert("false"); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /legacy/test/foundry/mocks/MockSplit.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSDL-1.1 2 | pragma solidity ^0.8.13; 3 | 4 | import "contracts/interfaces/modules/royalties/ISplitMain.sol"; 5 | import {ERC20} from "solmate/src/tokens/ERC20.sol"; 6 | 7 | contract MockSplit { 8 | ISplitMain public immutable splitMain; 9 | constructor() { 10 | splitMain = ISplitMain(msg.sender); 11 | } 12 | 13 | function sendERC20ToMain(ERC20 token, uint256 amount) 14 | external 15 | payable 16 | { 17 | token.transfer(address(splitMain), amount); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /legacy/test/foundry/mocks/MockSplitMain.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSDL-1.1 2 | pragma solidity ^0.8.13; 3 | 4 | import "contracts/interfaces/modules/royalties/ISplitMain.sol"; 5 | import "./MockSplit.sol"; 6 | import "test/foundry/mocks/MockERC20.sol"; 7 | 8 | contract MockSplitMain is ISplitMain { 9 | 10 | mapping(ERC20 => mapping(address => uint256)) internal erc20Balances; 11 | uint public TOTAL_SUPPLY = 1e6; 12 | 13 | constructor() {} 14 | 15 | function createSplit( 16 | address[] calldata, 17 | uint32[] calldata, 18 | uint32, 19 | address 20 | ) external override returns (address) { 21 | return address(new MockSplit()); 22 | } 23 | 24 | function updateAndDistributeETH( 25 | address, 26 | address[] calldata, 27 | uint32[] calldata, 28 | uint32, 29 | address 30 | ) external override { 31 | 32 | } 33 | 34 | function updateAndDistributeERC20( 35 | address split, 36 | ERC20 token, 37 | address[] calldata accounts, 38 | uint32[] calldata percentAllocations, 39 | uint32, 40 | address 41 | ) external override { 42 | // simulate SplitMain behavior that SplitMain reserve 1 from split balance 43 | uint balance = token.balanceOf(split) - 1; 44 | MockSplit(split).sendERC20ToMain(token, balance); 45 | for (uint i = 0; i < accounts.length; i++) { 46 | erc20Balances[token][accounts[i]] = percentAllocations[i] * balance / TOTAL_SUPPLY; 47 | } 48 | } 49 | 50 | function getETHBalance(address) external pure override returns (uint256) { 51 | return 100; 52 | } 53 | 54 | function getERC20Balance(address account, ERC20 token) external view override returns (uint256) { 55 | return erc20Balances[token][account]; 56 | } 57 | 58 | function withdraw(address account, uint256 withdrawETH, ERC20[] calldata tokens) external override { 59 | if (withdrawETH != 0) { 60 | revert("Unsupport ETH"); 61 | } 62 | unchecked { 63 | for (uint256 i = 0; i < tokens.length; ++i) { 64 | // Simulate SplitMain behavior that account balance reserve 1 65 | uint256 withdrawn = erc20Balances[tokens[i]][account] - 1; 66 | erc20Balances[tokens[i]][account] = 1; 67 | tokens[i].transfer(account, withdrawn); 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /lib/forge-std/.gitignore: -------------------------------------------------------------------------------- 1 | cache/ 2 | out/ 3 | .vscode 4 | .idea 5 | -------------------------------------------------------------------------------- /lib/forge-std/.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/ds-test"] 2 | path = lib/ds-test 3 | url = https://github.com/dapphub/ds-test 4 | -------------------------------------------------------------------------------- /lib/forge-std/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright Contributors to Forge Standard Library 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE O THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE.R 26 | -------------------------------------------------------------------------------- /lib/forge-std/foundry.toml: -------------------------------------------------------------------------------- 1 | [profile.default] 2 | fs_permissions = [{ access = "read-write", path = "./"}] 3 | 4 | [rpc_endpoints] 5 | # The RPC URLs are modified versions of the default for testing initialization. 6 | mainnet = "https://mainnet.infura.io/v3/16a8be88795540b9b3903d8de0f7baa5" # Different API key. 7 | optimism_goerli = "https://goerli.optimism.io/" # Adds a trailing slash. 8 | arbitrum_one_goerli = "https://goerli-rollup.arbitrum.io/rpc/" # Adds a trailing slash. 9 | needs_undefined_env_var = "${UNDEFINED_RPC_URL_PLACEHOLDER}" 10 | 11 | [fmt] 12 | # These are all the `forge fmt` defaults. 13 | line_length = 120 14 | tab_width = 4 15 | bracket_spacing = false 16 | int_types = 'long' 17 | multiline_func_header = 'attributes_first' 18 | quote_style = 'double' 19 | number_underscore = 'preserve' 20 | single_line_statement_blocks = 'preserve' 21 | ignore = ["src/console.sol", "src/console2.sol"] -------------------------------------------------------------------------------- /lib/forge-std/lib/ds-test/.gitignore: -------------------------------------------------------------------------------- 1 | /.dapple 2 | /build 3 | /out 4 | /cache/ 5 | -------------------------------------------------------------------------------- /lib/forge-std/lib/ds-test/Makefile: -------------------------------------------------------------------------------- 1 | all:; dapp build 2 | 3 | test: 4 | -dapp --use solc:0.4.23 build 5 | -dapp --use solc:0.4.26 build 6 | -dapp --use solc:0.5.17 build 7 | -dapp --use solc:0.6.12 build 8 | -dapp --use solc:0.7.5 build 9 | 10 | demo: 11 | DAPP_SRC=demo dapp --use solc:0.7.5 build 12 | -hevm dapp-test --verbose 3 13 | 14 | .PHONY: test demo 15 | -------------------------------------------------------------------------------- /lib/forge-std/lib/ds-test/default.nix: -------------------------------------------------------------------------------- 1 | { solidityPackage, dappsys }: solidityPackage { 2 | name = "ds-test"; 3 | src = ./src; 4 | } 5 | -------------------------------------------------------------------------------- /lib/forge-std/lib/ds-test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ds-test", 3 | "version": "1.0.0", 4 | "description": "Assertions, equality checks and other test helpers ", 5 | "bugs": "https://github.com/dapphub/ds-test/issues", 6 | "license": "GPL-3.0", 7 | "author": "Contributors to ds-test", 8 | "files": [ 9 | "src/*" 10 | ], 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/dapphub/ds-test.git" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lib/forge-std/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "forge-std", 3 | "version": "1.5.2", 4 | "description": "Forge Standard Library is a collection of helpful contracts and libraries for use with Forge and Foundry.", 5 | "homepage": "https://book.getfoundry.sh/forge/forge-std", 6 | "bugs": "https://github.com/foundry-rs/forge-std/issues", 7 | "license": "(Apache-2.0 OR MIT)", 8 | "author": "Contributors to Forge Standard Library", 9 | "files": [ 10 | "src/**/*" 11 | ], 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/foundry-rs/forge-std.git" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /lib/forge-std/src/Base.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2 <0.9.0; 3 | 4 | import {StdStorage} from "./StdStorage.sol"; 5 | import {Vm, VmSafe} from "./Vm.sol"; 6 | 7 | abstract contract CommonBase { 8 | // Cheat code address, 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D. 9 | address internal constant VM_ADDRESS = address(uint160(uint256(keccak256("hevm cheat code")))); 10 | // console.sol and console2.sol work by executing a staticcall to this address. 11 | address internal constant CONSOLE = 0x000000000000000000636F6e736F6c652e6c6f67; 12 | // Default address for tx.origin and msg.sender, 0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38. 13 | address internal constant DEFAULT_SENDER = address(uint160(uint256(keccak256("foundry default caller")))); 14 | // Address of the test contract, deployed by the DEFAULT_SENDER. 15 | address internal constant DEFAULT_TEST_CONTRACT = 0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f; 16 | // Deterministic deployment address of the Multicall3 contract. 17 | address internal constant MULTICALL3_ADDRESS = 0xcA11bde05977b3631167028862bE2a173976CA11; 18 | 19 | uint256 internal constant UINT256_MAX = 20 | 115792089237316195423570985008687907853269984665640564039457584007913129639935; 21 | 22 | Vm internal constant vm = Vm(VM_ADDRESS); 23 | StdStorage internal stdstore; 24 | } 25 | 26 | abstract contract TestBase is CommonBase {} 27 | 28 | abstract contract ScriptBase is CommonBase { 29 | // Used when deploying with create2, https://github.com/Arachnid/deterministic-deployment-proxy. 30 | address internal constant CREATE2_FACTORY = 0x4e59b44847b379578588920cA78FbF26c0B4956C; 31 | 32 | VmSafe internal constant vmSafe = VmSafe(VM_ADDRESS); 33 | } 34 | -------------------------------------------------------------------------------- /lib/forge-std/src/Script.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2 <0.9.0; 3 | 4 | // 💬 ABOUT 5 | // Standard Library's default Script. 6 | 7 | // 🧩 MODULES 8 | import {ScriptBase} from "./Base.sol"; 9 | import {console} from "./console.sol"; 10 | import {console2} from "./console2.sol"; 11 | import {StdChains} from "./StdChains.sol"; 12 | import {StdCheatsSafe} from "./StdCheats.sol"; 13 | import {stdJson} from "./StdJson.sol"; 14 | import {stdMath} from "./StdMath.sol"; 15 | import {StdStorage, stdStorageSafe} from "./StdStorage.sol"; 16 | import {StdUtils} from "./StdUtils.sol"; 17 | import {VmSafe} from "./Vm.sol"; 18 | 19 | // 📦 BOILERPLATE 20 | import {ScriptBase} from "./Base.sol"; 21 | 22 | // ⭐️ SCRIPT 23 | abstract contract Script is StdChains, StdCheatsSafe, StdUtils, ScriptBase { 24 | // Note: IS_SCRIPT() must return true. 25 | bool public IS_SCRIPT = true; 26 | } 27 | -------------------------------------------------------------------------------- /lib/forge-std/src/StdError.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Panics work for versions >=0.8.0, but we lowered the pragma to make this compatible with Test 3 | pragma solidity >=0.6.2 <0.9.0; 4 | 5 | library stdError { 6 | bytes public constant assertionError = abi.encodeWithSignature("Panic(uint256)", 0x01); 7 | bytes public constant arithmeticError = abi.encodeWithSignature("Panic(uint256)", 0x11); 8 | bytes public constant divisionError = abi.encodeWithSignature("Panic(uint256)", 0x12); 9 | bytes public constant enumConversionError = abi.encodeWithSignature("Panic(uint256)", 0x21); 10 | bytes public constant encodeStorageError = abi.encodeWithSignature("Panic(uint256)", 0x22); 11 | bytes public constant popError = abi.encodeWithSignature("Panic(uint256)", 0x31); 12 | bytes public constant indexOOBError = abi.encodeWithSignature("Panic(uint256)", 0x32); 13 | bytes public constant memOverflowError = abi.encodeWithSignature("Panic(uint256)", 0x41); 14 | bytes public constant zeroVarError = abi.encodeWithSignature("Panic(uint256)", 0x51); 15 | } 16 | -------------------------------------------------------------------------------- /lib/forge-std/src/StdInvariant.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2 <0.9.0; 3 | 4 | pragma experimental ABIEncoderV2; 5 | 6 | contract StdInvariant { 7 | struct FuzzSelector { 8 | address addr; 9 | bytes4[] selectors; 10 | } 11 | 12 | address[] private _excludedContracts; 13 | address[] private _excludedSenders; 14 | address[] private _targetedContracts; 15 | address[] private _targetedSenders; 16 | 17 | string[] private _excludedArtifacts; 18 | string[] private _targetedArtifacts; 19 | 20 | FuzzSelector[] private _targetedArtifactSelectors; 21 | FuzzSelector[] private _targetedSelectors; 22 | 23 | // Functions for users: 24 | // These are intended to be called in tests. 25 | 26 | function excludeContract(address newExcludedContract_) internal { 27 | _excludedContracts.push(newExcludedContract_); 28 | } 29 | 30 | function excludeSender(address newExcludedSender_) internal { 31 | _excludedSenders.push(newExcludedSender_); 32 | } 33 | 34 | function excludeArtifact(string memory newExcludedArtifact_) internal { 35 | _excludedArtifacts.push(newExcludedArtifact_); 36 | } 37 | 38 | function targetArtifact(string memory newTargetedArtifact_) internal { 39 | _targetedArtifacts.push(newTargetedArtifact_); 40 | } 41 | 42 | function targetArtifactSelector(FuzzSelector memory newTargetedArtifactSelector_) internal { 43 | _targetedArtifactSelectors.push(newTargetedArtifactSelector_); 44 | } 45 | 46 | function targetContract(address newTargetedContract_) internal { 47 | _targetedContracts.push(newTargetedContract_); 48 | } 49 | 50 | function targetSelector(FuzzSelector memory newTargetedSelector_) internal { 51 | _targetedSelectors.push(newTargetedSelector_); 52 | } 53 | 54 | function targetSender(address newTargetedSender_) internal { 55 | _targetedSenders.push(newTargetedSender_); 56 | } 57 | 58 | // Functions for forge: 59 | // These are called by forge to run invariant tests and don't need to be called in tests. 60 | 61 | function excludeArtifacts() public view returns (string[] memory excludedArtifacts_) { 62 | excludedArtifacts_ = _excludedArtifacts; 63 | } 64 | 65 | function excludeContracts() public view returns (address[] memory excludedContracts_) { 66 | excludedContracts_ = _excludedContracts; 67 | } 68 | 69 | function excludeSenders() public view returns (address[] memory excludedSenders_) { 70 | excludedSenders_ = _excludedSenders; 71 | } 72 | 73 | function targetArtifacts() public view returns (string[] memory targetedArtifacts_) { 74 | targetedArtifacts_ = _targetedArtifacts; 75 | } 76 | 77 | function targetArtifactSelectors() public view returns (FuzzSelector[] memory targetedArtifactSelectors_) { 78 | targetedArtifactSelectors_ = _targetedArtifactSelectors; 79 | } 80 | 81 | function targetContracts() public view returns (address[] memory targetedContracts_) { 82 | targetedContracts_ = _targetedContracts; 83 | } 84 | 85 | function targetSelectors() public view returns (FuzzSelector[] memory targetedSelectors_) { 86 | targetedSelectors_ = _targetedSelectors; 87 | } 88 | 89 | function targetSenders() public view returns (address[] memory targetedSenders_) { 90 | targetedSenders_ = _targetedSenders; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /lib/forge-std/src/StdMath.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2 <0.9.0; 3 | 4 | library stdMath { 5 | int256 private constant INT256_MIN = -57896044618658097711785492504343953926634992332820282019728792003956564819968; 6 | 7 | function abs(int256 a) internal pure returns (uint256) { 8 | // Required or it will fail when `a = type(int256).min` 9 | if (a == INT256_MIN) { 10 | return 57896044618658097711785492504343953926634992332820282019728792003956564819968; 11 | } 12 | 13 | return uint256(a > 0 ? a : -a); 14 | } 15 | 16 | function delta(uint256 a, uint256 b) internal pure returns (uint256) { 17 | return a > b ? a - b : b - a; 18 | } 19 | 20 | function delta(int256 a, int256 b) internal pure returns (uint256) { 21 | // a and b are of the same sign 22 | // this works thanks to two's complement, the left-most bit is the sign bit 23 | if ((a ^ b) > -1) { 24 | return delta(abs(a), abs(b)); 25 | } 26 | 27 | // a and b are of opposite signs 28 | return abs(a) + abs(b); 29 | } 30 | 31 | function percentDelta(uint256 a, uint256 b) internal pure returns (uint256) { 32 | uint256 absDelta = delta(a, b); 33 | 34 | return absDelta * 1e18 / b; 35 | } 36 | 37 | function percentDelta(int256 a, int256 b) internal pure returns (uint256) { 38 | uint256 absDelta = delta(a, b); 39 | uint256 absB = abs(b); 40 | 41 | return absDelta * 1e18 / absB; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/forge-std/src/Test.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2 <0.9.0; 3 | 4 | pragma experimental ABIEncoderV2; 5 | 6 | // 💬 ABOUT 7 | // Standard Library's default Test 8 | 9 | // 🧩 MODULES 10 | import {console} from "./console.sol"; 11 | import {console2} from "./console2.sol"; 12 | import {StdAssertions} from "./StdAssertions.sol"; 13 | import {StdChains} from "./StdChains.sol"; 14 | import {StdCheats} from "./StdCheats.sol"; 15 | import {stdError} from "./StdError.sol"; 16 | import {StdInvariant} from "./StdInvariant.sol"; 17 | import {stdJson} from "./StdJson.sol"; 18 | import {stdMath} from "./StdMath.sol"; 19 | import {StdStorage, stdStorage} from "./StdStorage.sol"; 20 | import {StdUtils} from "./StdUtils.sol"; 21 | import {Vm} from "./Vm.sol"; 22 | import {StdStyle} from "./StdStyle.sol"; 23 | 24 | // 📦 BOILERPLATE 25 | import {TestBase} from "./Base.sol"; 26 | import {DSTest} from "ds-test/test.sol"; 27 | 28 | // ⭐️ TEST 29 | abstract contract Test is DSTest, StdAssertions, StdChains, StdCheats, StdInvariant, StdUtils, TestBase { 30 | // Note: IS_TEST() must return true. 31 | // Note: Must have failure system, https://github.com/dapphub/ds-test/blob/cd98eff28324bfac652e63a239a60632a761790b/src/test.sol#L39-L76. 32 | } 33 | -------------------------------------------------------------------------------- /lib/forge-std/src/interfaces/IERC165.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2; 3 | 4 | interface IERC165 { 5 | /// @notice Query if a contract implements an interface 6 | /// @param interfaceID The interface identifier, as specified in ERC-165 7 | /// @dev Interface identification is specified in ERC-165. This function 8 | /// uses less than 30,000 gas. 9 | /// @return `true` if the contract implements `interfaceID` and 10 | /// `interfaceID` is not 0xffffffff, `false` otherwise 11 | function supportsInterface(bytes4 interfaceID) external view returns (bool); 12 | } 13 | -------------------------------------------------------------------------------- /lib/forge-std/src/interfaces/IERC20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2; 3 | 4 | /// @dev Interface of the ERC20 standard as defined in the EIP. 5 | /// @dev This includes the optional name, symbol, and decimals metadata. 6 | interface IERC20 { 7 | /// @dev Emitted when `value` tokens are moved from one account (`from`) to another (`to`). 8 | event Transfer(address indexed from, address indexed to, uint256 value); 9 | 10 | /// @dev Emitted when the allowance of a `spender` for an `owner` is set, where `value` 11 | /// is the new allowance. 12 | event Approval(address indexed owner, address indexed spender, uint256 value); 13 | 14 | /// @notice Returns the amount of tokens in existence. 15 | function totalSupply() external view returns (uint256); 16 | 17 | /// @notice Returns the amount of tokens owned by `account`. 18 | function balanceOf(address account) external view returns (uint256); 19 | 20 | /// @notice Moves `amount` tokens from the caller's account to `to`. 21 | function transfer(address to, uint256 amount) external returns (bool); 22 | 23 | /// @notice Returns the remaining number of tokens that `spender` is allowed 24 | /// to spend on behalf of `owner` 25 | function allowance(address owner, address spender) external view returns (uint256); 26 | 27 | /// @notice Sets `amount` as the allowance of `spender` over the caller's tokens. 28 | /// @dev Be aware of front-running risks: https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 29 | function approve(address spender, uint256 amount) external returns (bool); 30 | 31 | /// @notice Moves `amount` tokens from `from` to `to` using the allowance mechanism. 32 | /// `amount` is then deducted from the caller's allowance. 33 | function transferFrom(address from, address to, uint256 amount) external returns (bool); 34 | 35 | /// @notice Returns the name of the token. 36 | function name() external view returns (string memory); 37 | 38 | /// @notice Returns the symbol of the token. 39 | function symbol() external view returns (string memory); 40 | 41 | /// @notice Returns the decimals places of the token. 42 | function decimals() external view returns (uint8); 43 | } 44 | -------------------------------------------------------------------------------- /lib/forge-std/src/interfaces/IMulticall3.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2 <0.9.0; 3 | 4 | pragma experimental ABIEncoderV2; 5 | 6 | interface IMulticall3 { 7 | struct Call { 8 | address target; 9 | bytes callData; 10 | } 11 | 12 | struct Call3 { 13 | address target; 14 | bool allowFailure; 15 | bytes callData; 16 | } 17 | 18 | struct Call3Value { 19 | address target; 20 | bool allowFailure; 21 | uint256 value; 22 | bytes callData; 23 | } 24 | 25 | struct Result { 26 | bool success; 27 | bytes returnData; 28 | } 29 | 30 | function aggregate(Call[] calldata calls) 31 | external 32 | payable 33 | returns (uint256 blockNumber, bytes[] memory returnData); 34 | 35 | function aggregate3(Call3[] calldata calls) external payable returns (Result[] memory returnData); 36 | 37 | function aggregate3Value(Call3Value[] calldata calls) external payable returns (Result[] memory returnData); 38 | 39 | function blockAndAggregate(Call[] calldata calls) 40 | external 41 | payable 42 | returns (uint256 blockNumber, bytes32 blockHash, Result[] memory returnData); 43 | 44 | function getBasefee() external view returns (uint256 basefee); 45 | 46 | function getBlockHash(uint256 blockNumber) external view returns (bytes32 blockHash); 47 | 48 | function getBlockNumber() external view returns (uint256 blockNumber); 49 | 50 | function getChainId() external view returns (uint256 chainid); 51 | 52 | function getCurrentBlockCoinbase() external view returns (address coinbase); 53 | 54 | function getCurrentBlockDifficulty() external view returns (uint256 difficulty); 55 | 56 | function getCurrentBlockGasLimit() external view returns (uint256 gaslimit); 57 | 58 | function getCurrentBlockTimestamp() external view returns (uint256 timestamp); 59 | 60 | function getEthBalance(address addr) external view returns (uint256 balance); 61 | 62 | function getLastBlockHash() external view returns (bytes32 blockHash); 63 | 64 | function tryAggregate(bool requireSuccess, Call[] calldata calls) 65 | external 66 | payable 67 | returns (Result[] memory returnData); 68 | 69 | function tryBlockAndAggregate(bool requireSuccess, Call[] calldata calls) 70 | external 71 | payable 72 | returns (uint256 blockNumber, bytes32 blockHash, Result[] memory returnData); 73 | } 74 | -------------------------------------------------------------------------------- /lib/forge-std/test/StdError.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0 <0.9.0; 3 | 4 | import "../src/StdError.sol"; 5 | import "../src/Test.sol"; 6 | 7 | contract StdErrorsTest is Test { 8 | ErrorsTest test; 9 | 10 | function setUp() public { 11 | test = new ErrorsTest(); 12 | } 13 | 14 | function testExpectAssertion() public { 15 | vm.expectRevert(stdError.assertionError); 16 | test.assertionError(); 17 | } 18 | 19 | function testExpectArithmetic() public { 20 | vm.expectRevert(stdError.arithmeticError); 21 | test.arithmeticError(10); 22 | } 23 | 24 | function testExpectDiv() public { 25 | vm.expectRevert(stdError.divisionError); 26 | test.divError(0); 27 | } 28 | 29 | function testExpectMod() public { 30 | vm.expectRevert(stdError.divisionError); 31 | test.modError(0); 32 | } 33 | 34 | function testExpectEnum() public { 35 | vm.expectRevert(stdError.enumConversionError); 36 | test.enumConversion(1); 37 | } 38 | 39 | function testExpectEncodeStg() public { 40 | vm.expectRevert(stdError.encodeStorageError); 41 | test.encodeStgError(); 42 | } 43 | 44 | function testExpectPop() public { 45 | vm.expectRevert(stdError.popError); 46 | test.pop(); 47 | } 48 | 49 | function testExpectOOB() public { 50 | vm.expectRevert(stdError.indexOOBError); 51 | test.indexOOBError(1); 52 | } 53 | 54 | function testExpectMem() public { 55 | vm.expectRevert(stdError.memOverflowError); 56 | test.mem(); 57 | } 58 | 59 | function testExpectIntern() public { 60 | vm.expectRevert(stdError.zeroVarError); 61 | test.intern(); 62 | } 63 | } 64 | 65 | contract ErrorsTest { 66 | enum T {T1} 67 | 68 | uint256[] public someArr; 69 | bytes someBytes; 70 | 71 | function assertionError() public pure { 72 | assert(false); 73 | } 74 | 75 | function arithmeticError(uint256 a) public pure { 76 | a -= 100; 77 | } 78 | 79 | function divError(uint256 a) public pure { 80 | 100 / a; 81 | } 82 | 83 | function modError(uint256 a) public pure { 84 | 100 % a; 85 | } 86 | 87 | function enumConversion(uint256 a) public pure { 88 | T(a); 89 | } 90 | 91 | function encodeStgError() public { 92 | /// @solidity memory-safe-assembly 93 | assembly { 94 | sstore(someBytes.slot, 1) 95 | } 96 | keccak256(someBytes); 97 | } 98 | 99 | function pop() public { 100 | someArr.pop(); 101 | } 102 | 103 | function indexOOBError(uint256 a) public pure { 104 | uint256[] memory t = new uint256[](0); 105 | t[a]; 106 | } 107 | 108 | function mem() public pure { 109 | uint256 l = 2 ** 256 / 32; 110 | new uint256[](l); 111 | } 112 | 113 | function intern() public returns (uint256) { 114 | function(uint256) internal returns (uint256) x; 115 | x(2); 116 | return 7; 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /lib/forge-std/test/compilation/CompilationScript.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2 <0.9.0; 3 | 4 | pragma experimental ABIEncoderV2; 5 | 6 | import "../../src/Script.sol"; 7 | 8 | // The purpose of this contract is to benchmark compilation time to avoid accidentally introducing 9 | // a change that results in very long compilation times with via-ir. See https://github.com/foundry-rs/forge-std/issues/207 10 | contract CompilationScript is Script {} 11 | -------------------------------------------------------------------------------- /lib/forge-std/test/compilation/CompilationScriptBase.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2 <0.9.0; 3 | 4 | pragma experimental ABIEncoderV2; 5 | 6 | import "../../src/Script.sol"; 7 | 8 | // The purpose of this contract is to benchmark compilation time to avoid accidentally introducing 9 | // a change that results in very long compilation times with via-ir. See https://github.com/foundry-rs/forge-std/issues/207 10 | contract CompilationScriptBase is ScriptBase {} 11 | -------------------------------------------------------------------------------- /lib/forge-std/test/compilation/CompilationTest.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2 <0.9.0; 3 | 4 | pragma experimental ABIEncoderV2; 5 | 6 | import "../../src/Test.sol"; 7 | 8 | // The purpose of this contract is to benchmark compilation time to avoid accidentally introducing 9 | // a change that results in very long compilation times with via-ir. See https://github.com/foundry-rs/forge-std/issues/207 10 | contract CompilationTest is Test {} 11 | -------------------------------------------------------------------------------- /lib/forge-std/test/compilation/CompilationTestBase.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2 <0.9.0; 3 | 4 | pragma experimental ABIEncoderV2; 5 | 6 | import "../../src/Test.sol"; 7 | 8 | // The purpose of this contract is to benchmark compilation time to avoid accidentally introducing 9 | // a change that results in very long compilation times with via-ir. See https://github.com/foundry-rs/forge-std/issues/207 10 | contract CompilationTestBase is TestBase {} 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@story-protocol/contracts", 3 | "version": "0.2.0", 4 | "description": "Story Protocol smart contracts", 5 | "main": "", 6 | "directories": { 7 | "lib": "lib", 8 | "test": "test" 9 | }, 10 | "scripts": { 11 | "test": "echo \"Error: no test specified\" && exit 1" 12 | }, 13 | "author": "StoryProtocol", 14 | "license": "Unlicensed", 15 | "devDependencies": { 16 | "@changesets/cli": "^2.26.2", 17 | "@nomiclabs/hardhat-ethers": "^2.2.3", 18 | "@nomiclabs/hardhat-etherscan": "^3.1.7", 19 | "@nomiclabs/hardhat-waffle": "^2.0.5", 20 | "@openzeppelin/hardhat-upgrades": "^1.22.1", 21 | "base64-sol": "^1.1.0", 22 | "chai": "^4.3.7", 23 | "dotenv": "^16.0.3", 24 | "ethereum-waffle": "^4.0.10", 25 | "hardhat": "^2.13.0", 26 | "hardhat-deploy": "^0.11.25", 27 | "hardhat-gas-reporter": "^1.0.9", 28 | "mocha": "^10.2.0", 29 | "prettier": "^3.1.0", 30 | "prettier-plugin-solidity": "^1.2.0", 31 | "solhint": "^3.6.2", 32 | "solhint-plugin-prettier": "^0.1.0", 33 | "solidity-coverage": "^0.8.2", 34 | "solidity-docgen": "^0.6.0-beta.36" 35 | }, 36 | "dependencies": { 37 | "@openzeppelin/contracts": "^4.9.3", 38 | "@openzeppelin/contracts-upgradeable": "^4.9.3", 39 | "@openzeppelin/defender-sdk": "^1.7.0", 40 | "solmate": "^6.2.0" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /script/foundry/utils/BroadcastManager.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.18; 3 | 4 | import "forge-std/Script.sol"; 5 | import "script/foundry/utils/StringUtil.sol"; 6 | 7 | contract BroadcastManager is Script { 8 | 9 | address public multisig; 10 | address public deployer; 11 | 12 | function _beginBroadcast() internal { 13 | uint256 deployerPrivateKey; 14 | if (block.chainid == 1) { 15 | deployerPrivateKey = vm.envUint("MAINNET_PRIVATEKEY"); 16 | deployer = vm.envAddress("MAINNET_DEPLOYER_ADDRESS"); 17 | multisig = vm.envAddress("MAINNET_MULTISIG_ADDRESS"); 18 | vm.startBroadcast(deployerPrivateKey); 19 | } else if (block.chainid == 11155111) { 20 | deployerPrivateKey = vm.envUint("SEPOLIA_PRIVATEKEY"); 21 | deployer = vm.envAddress("SEPOLIA_DEPLOYER_ADDRESS"); 22 | multisig = vm.envAddress("SEPOLIA_MULTISIG_ADDRESS"); 23 | vm.startBroadcast(deployerPrivateKey); 24 | 25 | } else if (block.chainid == 31337) { 26 | multisig = address(0x456); 27 | deployer = address(0x999); 28 | vm.startPrank(deployer); 29 | } else { 30 | revert("Unsupported chain"); 31 | } 32 | 33 | } 34 | 35 | function _endBroadcast() internal { 36 | if (block.chainid == 31337) { 37 | vm.stopPrank(); 38 | } else { 39 | vm.stopBroadcast(); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /script/foundry/utils/HooksFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.19; 3 | 4 | import { Create2 } from "@openzeppelin/contracts/utils/Create2.sol"; 5 | import { Hook } from "contracts/lib/hooks/Hook.sol"; 6 | 7 | contract HooksFactory { 8 | function deploy(bytes memory code_, uint256 hookTypeFlag_, uint256 seed_) external returns (address hookAddr) { 9 | uint256 randomNumber = uint256(keccak256(abi.encodePacked(seed_))); 10 | for (uint256 i = 0; i < 1500; i++) { 11 | bytes32 salt = bytes32(randomNumber + i); 12 | bytes32 bytecodeHash = keccak256(code_); 13 | address expectedAddress = Create2.computeAddress(salt, bytecodeHash); 14 | uint160 prefix = hookTypeFlag_ == Hook.SYNC_FLAG ? 0x02 : 0x01; 15 | if (_doesAddressStartWith(expectedAddress, prefix)) { 16 | hookAddr = Create2.deploy(0, salt, code_); 17 | return hookAddr; 18 | } 19 | } 20 | } 21 | 22 | function _doesAddressStartWith(address address_,uint160 prefix_) private pure returns (bool) { 23 | return uint160(address_) >> (160 - 2) == prefix_; 24 | } 25 | } -------------------------------------------------------------------------------- /script/foundry/utils/JsonDeploymentHandler.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.18; 3 | 4 | import "forge-std/Script.sol"; 5 | import "script/foundry/utils/StringUtil.sol"; 6 | 7 | 8 | contract JsonDeploymentHandler is Script { 9 | 10 | using StringUtil for uint256; 11 | using stdJson for string; 12 | 13 | string output; 14 | string readJson; 15 | string chainId; 16 | string key; 17 | string internalKey = "key"; 18 | 19 | constructor(string memory _key) { 20 | chainId = (block.chainid).toString(); 21 | key = _key; 22 | } 23 | 24 | function _readAddress(string memory readPath) internal returns(address) { 25 | try vm.parseJsonAddress(readJson, readPath) returns (address addr) { 26 | return addr; 27 | } catch { 28 | return address(0); 29 | } 30 | } 31 | 32 | function _readDeployment() internal { 33 | string memory root = vm.projectRoot(); 34 | string memory filePath = string.concat("/deployment-", (block.chainid).toString(), ".json"); 35 | string memory path = string.concat(root, filePath); 36 | readJson = vm.readFile(path); 37 | } 38 | 39 | function _writeAddress(string memory contractKey, address newAddress) internal { 40 | output = vm.serializeAddress(internalKey, contractKey, newAddress); 41 | } 42 | 43 | function _writeToJson(string memory contractKey, string memory value) internal { 44 | vm.writeJson(value, string.concat("./deployment-", chainId, ".json"), contractKey); 45 | } 46 | 47 | function _writeDeployment() internal { 48 | vm.writeJson(output, string.concat("./deployment-", chainId, ".json"), string.concat(".", key)); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /script/foundry/utils/RolesPrinter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.18; 3 | 4 | import "forge-std/Script.sol"; 5 | import { AccessControl } from "contracts/lib/AccessControl.sol"; 6 | import { ModuleKey, RELATIONSHIP_MODULE_KEY, LICENSING_MODULE_KEY, REGISTRATION_MODULE_KEY } from "contracts/lib/modules/Module.sol"; 7 | 8 | contract RolesPrinter is Script { 9 | function run() view public { 10 | console.log("PROTOCOL_ADMIN_ROLE"); 11 | console.logBytes32(bytes32(0)); 12 | console.log("UPGRADER_ROLE"); 13 | console.logBytes32(keccak256("UPGRADER_ROLE")); 14 | 15 | console.log("RELATIONSHIP_MANAGER_ROLE"); 16 | console.logBytes32(keccak256("RELATIONSHIP_MANAGER_ROLE")); 17 | 18 | console.log("LICENSING_MANAGER_ROLE"); 19 | console.logBytes32(keccak256("LICENSING_MANAGER_ROLE")); 20 | 21 | console.log("MODULE_REGISTRAR_ROLE"); 22 | console.logBytes32(keccak256("MODULE_REGISTRAR_ROLE")); 23 | 24 | console.log("MODULE_EXECUTOR_ROLE"); 25 | console.logBytes32(keccak256("MODULE_EXECUTOR_ROLE")); 26 | 27 | console.log("HOOK_CALLER_ROLE"); 28 | console.logBytes32(keccak256("HOOK_CALLER_ROLE")); 29 | 30 | console.log("RELATIONSHIP_MODULE_KEY"); 31 | console.logBytes32(ModuleKey.unwrap(RELATIONSHIP_MODULE_KEY)); 32 | 33 | console.log("LICENSING_MODULE_KEY"); 34 | console.logBytes32(ModuleKey.unwrap(LICENSING_MODULE_KEY)); 35 | 36 | console.log("REGISTRATION_MODULE_KEY"); 37 | console.logBytes32(ModuleKey.unwrap(REGISTRATION_MODULE_KEY)); 38 | 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /script/foundry/utils/ShortStringPrinter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.18; 3 | 4 | import "forge-std/Script.sol"; 5 | import { ShortString, ShortStrings } from "@openzeppelin/contracts/utils/ShortStrings.sol"; 6 | 7 | contract ShortStringPrinter is Script { 8 | using ShortStrings for *; 9 | 10 | string public input = "Hello World"; 11 | 12 | function run() view public { 13 | console.log(input); 14 | console.logBytes32(ShortString.unwrap(input.toShortString())); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /script/foundry/utils/StringUtil.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.18; 3 | 4 | /// @title String Utility Library 5 | library StringUtil { 6 | 7 | /// @dev Converts a uint256 into a string. 8 | function toString(uint256 value) internal pure returns (string memory) { 9 | if (value == 0) { 10 | return "0"; 11 | } 12 | uint256 temp = value; 13 | uint256 digits; 14 | while (temp != 0) { 15 | digits++; 16 | temp /= 10; 17 | } 18 | bytes memory buffer = new bytes(digits); 19 | while (value != 0) { 20 | digits -= 1; 21 | buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); 22 | value /= 10; 23 | } 24 | return string(buffer); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /script/hardhat/createFranchise.js: -------------------------------------------------------------------------------- 1 | const loadDeployment = require('./loadDeployment.js'); 2 | const { getContractAddress } = require('@ethersproject/address'); 3 | 4 | function findIdAndAddress(events) { 5 | const event = events.find((e) => e.event === "FranchiseRegistered"); 6 | return { 7 | id: event.args.id.toString(), 8 | address: event.args.ipAssetRegistryForId, 9 | }; 10 | } 11 | 12 | async function main(args, hre) { 13 | const { ethers } = hre; 14 | const { chainId, contracts } = await loadDeployment(hre); 15 | const { name, symbol, description, tokenURI, events } = args; 16 | console.log("Creating franchise: ", name, symbol); 17 | const params = { name, symbol, description, tokenURI }; 18 | const tx = await contracts.franchiseRegistry.registerFranchise(params); 19 | console.log("Franchise created in tx: ", tx.hash); 20 | console.log("Waiting for tx to be mined..."); 21 | const receipt = await tx.wait(); 22 | if (events) { 23 | console.log("Events: "); 24 | console.log(receipt.events); 25 | } 26 | console.log("Franchise created"); 27 | const { id, address } = findIdAndAddress(receipt.events); 28 | console.log("id: ", id); 29 | console.log("address: ", address); 30 | return { id, address }; 31 | } 32 | 33 | module.exports = main; 34 | -------------------------------------------------------------------------------- /script/hardhat/defender/proposals/initialGrantRoles.js: -------------------------------------------------------------------------------- 1 | const roles = require('../../utils/roles.json'); 2 | const deployment = require('../../../../deployment-11155111.json'); 3 | const safeAddress = process.env.SEPOLIA_MULTISIG_ADDRESS; 4 | 5 | module.exports = { 6 | title: 'Grant Role for deployment contract', 7 | description: 'Grants the roles needed for a StoryProtocol deployment, and admin access to the multisig', 8 | network: 'sepolia', 9 | steps: [ 10 | { 11 | contractName: 'AccessControlSingleton', 12 | functionName: 'grantRole', 13 | functionInputs: [roles["RELATIONSHIP_MANAGER_ROLE"], safeAddress], 14 | }, 15 | { 16 | contractName: 'AccessControlSingleton', 17 | functionName: 'grantRole', 18 | functionInputs: [roles["LICENSING_MANAGER_ROLE"], safeAddress], 19 | }, 20 | { 21 | contractName: 'AccessControlSingleton', 22 | functionName: 'grantRole', 23 | functionInputs: [roles["MODULE_REGISTRAR_ROLE"], safeAddress], 24 | } 25 | // TODO: add contract roles 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /script/hardhat/loadDeployment.js: -------------------------------------------------------------------------------- 1 | const { readFileSync } = require("fs"); 2 | 3 | const DEBUG = false; 4 | 5 | async function main(hre) { 6 | const { ethers } = hre; 7 | const chainId = await ethers.provider.getNetwork().then((n) => n.chainId); 8 | console.log("ChainId:", `${chainId}`); 9 | const contracts = {}; 10 | 11 | const filePath = `./deployment-${chainId}.json`; 12 | const deployment = JSON.parse(readFileSync(filePath))['main']; 13 | for (let contractName in deployment) { 14 | if (DEBUG) { 15 | console.log(`${contractName}: ${deployment[contractName]}`); 16 | } 17 | const contractAddress = deployment[contractName]; 18 | if (contractName.endsWith('-Proxy')) { 19 | contractName = contractName.replace('-Proxy', ''); 20 | } else if (contractName.endsWith('-Impl') || contractName === 'MockERC721') { 21 | continue; 22 | } 23 | 24 | contracts[contractName] = await ethers.getContractAt(contractName, contractAddress); 25 | } 26 | 27 | return { chainId, contracts, deployment }; 28 | } 29 | 30 | module.exports = main; 31 | -------------------------------------------------------------------------------- /script/hardhat/namespacedStorageKey.js: -------------------------------------------------------------------------------- 1 | 2 | async function main(args, hre) { 3 | const { ethers } = hre; 4 | const { id, keccak256, solidityPack } = ethers.utils; 5 | const BigNumber = ethers.BigNumber; 6 | const { namespace } = args; 7 | // See https://eips.ethereum.org/EIPS/eip-7201 8 | const result = keccak256( 9 | solidityPack( 10 | ["bytes32"], 11 | [ 12 | BigNumber.from(id(namespace)).sub("1") 13 | ] 14 | ) 15 | ); 16 | console.log(result); 17 | return result; 18 | } 19 | 20 | module.exports = main; 21 | -------------------------------------------------------------------------------- /script/hardhat/utils/moduleKeys.json: -------------------------------------------------------------------------------- 1 | { 2 | "RELATIONSHIP_MODULE_KEY": "0x9bfc53275443313ccf9690a922eff8f27043682fe678ea5eab9045fc8f00ce33", 3 | "LICENSING_MODULE_KEY": "0xfb7d3935d1a1e0767e0484ea8ee192d9e24df803a28df4d54afff92c847dbfe4", 4 | "REGISTRATION_MODULE_KEY": "0x0e0494ad156d9b40ca20d29bf47a48d8f9aafc6d15e92e1752645f1fcd2b790c" 5 | } 6 | -------------------------------------------------------------------------------- /script/hardhat/utils/roles.json: -------------------------------------------------------------------------------- 1 | { 2 | "PROTOCOL_ADMIN_ROLE": "0x0000000000000000000000000000000000000000000000000000000000000000", 3 | "UPGRADER_ROLE": "0x189ab7a9244df0848122154315af71fe140f3db0fe014031783b0946b8c9d2e3", 4 | "RELATIONSHIP_MANAGER_ROLE": "0x637821dcee84aabec9c8cf4f6f643013a24ad27fd41511ffd937aea80d8619a9", 5 | "LICENSING_MANAGER_ROLE": "0xa8b84e71bc3552b48fea3678097679554a90f0e2c12c536a8bd697c05041e279", 6 | "IPORG_CREATOR_ROLE": "0x02f7fd2cb4d8e8ad75bab0a56a6f1ed7091336a83e11ccacc4e51f915215ab4c", 7 | "MODULE_REGISTRAR_ROLE": "0xc9a6ce5d86bdc06f8d57f20091bf09bf611e57d0626b65aa9ebb3c9179ba650f", 8 | "MODULE_EXECUTOR_ROLE": "0xe4a10d29decbd34c09a62a511d4fd993a7d8cbbf58d7d4278711c29c9148b8f7", 9 | "HOOK_CALLER_ROLE": "0x33dd54660937884a707404066945db647918933f71cc471efc6d6d0c3665d8db", 10 | "LICENSING_MANAGER": "0x256895704541e78004f0fd55953a8bcc54c628755b14fbf53afdc556473f9bd0" 11 | } -------------------------------------------------------------------------------- /slither.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "filter_paths": "lib", 3 | "solc_remaps": [ 4 | "ds-test/=lib/ds-test/src/", 5 | "forge-std/=lib/forge-std/src/", 6 | "@chainlink/=node_modules/@chainlink/", 7 | "@openzeppelin/=node_modules/@openzeppelin/", 8 | "base64-sol/=node_modules/base64-sol/" 9 | ] 10 | } -------------------------------------------------------------------------------- /test/foundry/access-control/AccessControlSingleton.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSDL-1.1 2 | pragma solidity ^0.8.13; 3 | 4 | import "forge-std/Test.sol"; 5 | import { AccessControlHelper } from "test/foundry/utils/AccessControlHelper.sol"; 6 | import { Errors } from "contracts/lib/Errors.sol"; 7 | import { AccessControl } from "contracts/lib/AccessControl.sol"; 8 | import { AccessControlSingleton } from "contracts/access-control/AccessControlSingleton.sol"; 9 | import { AccessControlUpgradeable } from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; 10 | 11 | contract AccessControlSingletonTest is Test, AccessControlHelper { 12 | 13 | error TestError(); 14 | function setUp() public { 15 | _setupAccessControl(); 16 | } 17 | 18 | function test_AccessControlSingleton_setup() public { 19 | assertTrue( 20 | accessControl.hasRole(AccessControl.PROTOCOL_ADMIN_ROLE, admin), 21 | "Admin role not set correctly" 22 | ); 23 | } 24 | 25 | function test_AccessControlSingleton_revert_reinitialize() public { 26 | vm.expectRevert("Initializable: contract is already initialized"); 27 | accessControl.initialize(admin); 28 | } 29 | 30 | function test_AccessControlSingleton_revert_zeroAdmin() public { 31 | AccessControlSingleton ac2 = new AccessControlSingleton(); 32 | vm.expectRevert(Errors.ZeroAddress.selector); 33 | ac2.initialize(address(0)); 34 | } 35 | 36 | function test_AccessControlSingleton_setRoleAdmin() public { 37 | bytes32 role = keccak256("TEST_ROLE"); 38 | bytes32 roleAdmin = keccak256("TEST_ROLE_ADMIN"); 39 | vm.prank(admin); 40 | accessControl.setRoleAdmin(role, roleAdmin); 41 | assertTrue( 42 | accessControl.getRoleAdmin(role) == roleAdmin, 43 | "Role admin not set correctly" 44 | ); 45 | } 46 | 47 | function test_AccessControlSingleton_revert_setRoleAdminNotProtocolAdmin() public { 48 | bytes32 role = keccak256("TEST_ROLE"); 49 | vm.expectRevert(_getRoleErrorMessage(address(this), AccessControl.PROTOCOL_ADMIN_ROLE)); 50 | accessControl.setRoleAdmin(role, AccessControl.PROTOCOL_ADMIN_ROLE); 51 | } 52 | 53 | function test_AccessControlSingleton_revert_UpgradeNotAuthorized() public { 54 | vm.expectRevert(_getRoleErrorMessage(address(this), AccessControl.UPGRADER_ROLE)); 55 | accessControl.upgradeTo(address(0)); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /test/foundry/access-control/AccessControlled.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSDL-1.1 2 | pragma solidity ^0.8.13; 3 | 4 | import "forge-std/Test.sol"; 5 | import { AccessControlHelper } from "test/foundry/utils/AccessControlHelper.sol"; 6 | import { Errors } from "contracts/lib/Errors.sol"; 7 | import { AccessControl } from "contracts/lib/AccessControl.sol"; 8 | import { AccessControlled } from "contracts/access-control/AccessControlled.sol"; 9 | import { AccessControlSingleton } from "contracts/access-control/AccessControlSingleton.sol"; 10 | import { MockAccessControlled } from "test/foundry/mocks/MockAccessControlled.sol"; 11 | import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; 12 | 13 | contract AccessControlledTest is Test, AccessControlHelper { 14 | 15 | event AccessControlUpdated(address indexed accessControl); 16 | 17 | MockAccessControlled accessControlled; 18 | 19 | function setUp() public { 20 | _setupAccessControl(); 21 | accessControlled = new MockAccessControlled(address(accessControl)); 22 | } 23 | 24 | function test_AccessControlled_onlyRole() public { 25 | bytes32 role = keccak256("TEST_ROLE"); 26 | _grantRole(vm, role, address(this)); 27 | accessControlled.exposeOnlyRole(role); 28 | } 29 | 30 | function test_AccessControlled_revert_onlyRole() public { 31 | bytes32 role = keccak256("TEST_ROLE"); 32 | vm.expectRevert( 33 | abi.encodeWithSelector( 34 | Errors.MissingRole.selector, 35 | role, 36 | address(this) 37 | ) 38 | ); 39 | accessControlled.exposeOnlyRole(role); 40 | } 41 | 42 | function test_AccessControlled_setAccessControl() public { 43 | AccessControlSingleton ac2 = new AccessControlSingleton(); 44 | vm.expectEmit(true, true, true, true); 45 | emit AccessControlUpdated(address(ac2)); 46 | vm.prank(admin); 47 | accessControlled.setAccessControl(address(ac2)); 48 | } 49 | 50 | function test_AccessControlled_revert_setAccessControlNotProtocolAdmin() public { 51 | AccessControlSingleton ac2 = new AccessControlSingleton(); 52 | vm.expectRevert( 53 | abi.encodeWithSelector( 54 | Errors.MissingRole.selector, 55 | AccessControl.PROTOCOL_ADMIN_ROLE, 56 | address(this) 57 | ) 58 | ); 59 | accessControlled.setAccessControl(address(ac2)); 60 | } 61 | 62 | function test_AccessControlled_revert_setAccessControlUnsupportedInterface() public { 63 | vm.expectRevert( 64 | abi.encodeWithSelector( 65 | Errors.UnsupportedInterface.selector, 66 | "IAccessControl" 67 | ) 68 | ); 69 | vm.prank(admin); 70 | accessControlled.setAccessControl(address(this)); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /test/foundry/hooks/TestBaseHook.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // See Story Protocol Alpha Agreement: https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf 3 | pragma solidity ^0.8.19; 4 | 5 | import "forge-std/Test.sol"; 6 | 7 | import { BaseTest } from "test/foundry/utils/BaseTest.sol"; 8 | import { HookResult } from "contracts/interfaces/hooks/base/IHook.sol"; 9 | import { MockBaseHook } from "test/foundry/mocks/MockBaseHook.sol"; 10 | import { Errors } from "contracts/lib/Errors.sol"; 11 | import { AccessControl } from "contracts/lib/AccessControl.sol"; 12 | 13 | 14 | contract TestBaseHook is BaseTest { 15 | MockBaseHook hook; 16 | 17 | function setUp() public override { 18 | super.setUp(); 19 | 20 | vm.prank(admin); 21 | accessControl.grantRole(AccessControl.HOOK_CALLER_ROLE, address(this)); 22 | 23 | hook = new MockBaseHook(address(accessControl)); 24 | } 25 | 26 | function test_baseHook_validateGoodConfig() public view { 27 | hook.validateConfig(abi.encode("GoodConfig")); 28 | } 29 | 30 | function test_baseHook_revert_NotQualifiedSyncHookCaller() public { 31 | bytes memory hooksParams = "0x1234"; 32 | vm.startPrank(address(0x7777)); 33 | // Try to handle the callback with an invalid request ID 34 | vm.expectRevert( 35 | abi.encodeWithSelector( 36 | Errors.MissingRole.selector, 37 | AccessControl.HOOK_CALLER_ROLE, 38 | address(0x7777) 39 | ) 40 | ); 41 | hook.executeSync(hooksParams); 42 | vm.stopPrank(); 43 | } 44 | 45 | function test_baseHook_revert_NotQualifiedAsyncHookCaller() public { 46 | bytes memory hooksParams = "0x1234"; 47 | vm.startPrank(address(0x7777)); 48 | // Try to handle the callback with an invalid request ID 49 | vm.expectRevert( 50 | abi.encodeWithSelector( 51 | Errors.MissingRole.selector, 52 | AccessControl.HOOK_CALLER_ROLE, 53 | address(0x7777) 54 | ) 55 | ); 56 | hook.executeAsync(hooksParams, address(this)); 57 | vm.stopPrank(); 58 | } 59 | 60 | function test_baseHook_revert_executeSyncCall() public { 61 | bytes memory hooksParams = "0x1234"; 62 | 63 | vm.expectRevert(Errors.Hook_UnsupportedSyncOperation.selector); 64 | hook.executeSync(hooksParams); 65 | } 66 | 67 | function test_baseHook_revert_executeAsyncCall() public { 68 | bytes memory hooksParams = "0x1234"; 69 | 70 | vm.expectRevert(Errors.Hook_UnsupportedAsyncOperation.selector); 71 | hook.executeAsync(hooksParams, address(this)); 72 | } 73 | 74 | function test_baseHook_revert_invalidConfig() public { 75 | vm.expectRevert(Errors.ZeroAddress.selector); 76 | hook.validateConfig(abi.encode("ERROR")); 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /test/foundry/hooks/TestSyncBaseHook.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // See Story Protocol Alpha Agreement: https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf 3 | pragma solidity ^0.8.19; 4 | 5 | import "forge-std/Test.sol"; 6 | 7 | import { BaseTest } from "test/foundry/utils/BaseTest.sol"; 8 | import { SyncBaseHook } from "contracts/hooks/base/SyncBaseHook.sol"; 9 | import { HookResult } from "contracts/interfaces/hooks/base/IHook.sol"; 10 | import { MockSyncHook } from "test/foundry/mocks/MockSyncHook.sol"; 11 | import { Errors } from "contracts/lib/Errors.sol"; 12 | import { AccessControl } from "contracts/lib/AccessControl.sol"; 13 | import { Hook } from "contracts/lib/hooks/Hook.sol"; 14 | 15 | contract TestSyncBaseHook is BaseTest { 16 | MockSyncHook hook; 17 | 18 | event SyncHookExecuted( 19 | address indexed hookAddress, 20 | HookResult indexed result, 21 | bytes contextData, 22 | bytes returnData 23 | ); 24 | 25 | function setUp() public override { 26 | super.setUp(); 27 | 28 | vm.prank(admin); 29 | accessControl.grantRole(AccessControl.HOOK_CALLER_ROLE, address(this)); 30 | 31 | hook = new MockSyncHook(address(accessControl)); 32 | } 33 | 34 | function test_syncBaseHook_executeSyncValidParams() public { 35 | bytes memory hookParams = "0x1234"; 36 | bytes memory hookConfig = "0x5678"; 37 | bytes memory context = _getExecutionContext(hookConfig, hookParams); 38 | bytes memory expectedHookData = _getExpectedReturnData(hookConfig, hookParams); 39 | HookResult result; 40 | bytes memory hookData; 41 | 42 | // Execute the sync hook 43 | (result, hookData) = hook.executeSync(context); 44 | 45 | // Check the result 46 | assertEq(uint(result), uint(HookResult.Completed)); 47 | 48 | // Check the hook data 49 | assertEq0(hookData, expectedHookData); 50 | } 51 | 52 | function test_syncBaseHook_executeSyncVerifyEvent() public { 53 | bytes memory hookParams = "0x1234"; 54 | bytes memory hookConfig = "0x5678"; 55 | bytes memory context = _getExecutionContext(hookConfig, hookParams); 56 | bytes memory expectedHookData = _getExpectedReturnData(hookConfig, hookParams); 57 | 58 | vm.expectEmit(address(hook)); 59 | emit SyncHookExecuted( 60 | address(hook), 61 | HookResult.Completed, 62 | context, 63 | expectedHookData 64 | ); 65 | // Execute the sync hook 66 | hook.executeSync(context); 67 | } 68 | 69 | function test_syncBaseHook_revert_NotQualifiedHookCaller() public { 70 | bytes memory hookParams = "0x1234"; 71 | vm.startPrank(address(0x7777)); 72 | // Try to handle the callback with an invalid request ID 73 | vm.expectRevert( 74 | abi.encodeWithSelector( 75 | Errors.MissingRole.selector, 76 | AccessControl.HOOK_CALLER_ROLE, 77 | address(0x7777) 78 | ) 79 | ); 80 | hook.executeSync(hookParams); 81 | vm.stopPrank(); 82 | } 83 | 84 | function test_syncBaseHook_revert_executeAsyncCall() public { 85 | bytes memory hookParams = "0x1234"; 86 | 87 | vm.expectRevert(Errors.Hook_UnsupportedAsyncOperation.selector); 88 | hook.executeAsync(hookParams, address(this)); 89 | } 90 | 91 | function _getExecutionContext(bytes memory hookConfig_, bytes memory hookParams_) internal pure returns (bytes memory) { 92 | Hook.ExecutionContext memory context = Hook.ExecutionContext({ 93 | config: hookConfig_, 94 | params: hookParams_ 95 | }); 96 | return abi.encode(context); 97 | } 98 | 99 | function _getExpectedReturnData(bytes memory hookConfig_, bytes memory hookParams_) internal pure returns(bytes memory) { 100 | return abi.encode(hookConfig_, hookParams_); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /test/foundry/interfaces/IERC721Events.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // See Story Protocol Alpha Agreement: https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf 3 | pragma solidity ^0.8.18; 4 | 5 | /// @title ERC-721 Events Interface 6 | interface IERC721Events { 7 | /// @notice Emits when `tokenId` is transferred from address `from` to `to`. 8 | /// @param from The address of the original NFT owner. 9 | /// @param to The address of the new NFT owner. 10 | /// @param tokenId The id of the NFT being transferred. 11 | event Transfer( 12 | address indexed from, 13 | address indexed to, 14 | uint256 indexed tokenId 15 | ); 16 | 17 | /// @notice Emits when `owner` approves `approved` to operate on `tokenId`. 18 | /// @param owner The address of the current NFT owner. 19 | /// @param approved The address approved to operate on `tokenId`. 20 | /// @param tokenId The id of the NFT being approved. 21 | event Approval( 22 | address indexed owner, 23 | address indexed approved, 24 | uint256 indexed tokenId 25 | ); 26 | 27 | /// @notice Emits when `owner` approves `operator` to operate on their NFTs. 28 | /// @param owner The address of the current NFT owner. 29 | /// @param operator The address of the new NFT operator. 30 | /// @param approved Whether operator can operate on NFTs of owner. 31 | event ApprovalForAll( 32 | address indexed owner, 33 | address indexed operator, 34 | bool approved 35 | ); 36 | } 37 | -------------------------------------------------------------------------------- /test/foundry/lib/BitMask.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSDL-1.1 2 | pragma solidity ^0.8.13; 3 | 4 | import "forge-std/Test.sol"; 5 | 6 | import { Errors } from "contracts/lib/Errors.sol"; 7 | import { BitMask } from "contracts/lib/BitMask.sol"; 8 | import { IPOrgController } from "contracts/ip-org/IPOrgController.sol"; 9 | import { ERC721 } from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; 10 | import { MockERC721 } from "test/foundry/mocks/MockERC721.sol"; 11 | 12 | contract BitMaskHarness { 13 | 14 | function convertToMask(uint8[] calldata assetTypes) pure external returns (uint256) { 15 | return BitMask.convertToMask(assetTypes); 16 | } 17 | 18 | function isSet(uint256 mask, uint8 assetType) pure external returns (bool) { 19 | return BitMask.isSet(mask, assetType); 20 | } 21 | 22 | } 23 | 24 | contract BitMaskTest is Test { 25 | 26 | BitMaskHarness public checker; 27 | 28 | function setUp() public { 29 | checker = new BitMaskHarness(); 30 | } 31 | 32 | function test_BitMask_convertToMask() public { 33 | for (uint8 i = 1; i <= 254; i++) { 34 | uint8[] memory assetTypes = new uint8[](i); 35 | uint256 resultMask; 36 | for (uint8 j = 1; j <= i; j++) { 37 | assetTypes[j-1] = uint8(j); 38 | resultMask |= 1 << (uint256(j) & 0xff); 39 | } 40 | uint256 mask = checker.convertToMask(assetTypes); 41 | assertEq(mask, resultMask); 42 | } 43 | } 44 | 45 | function test_BitMask_isSetOnMaskTrue() public { 46 | uint256 mask = 0; 47 | for (uint256 i = 0; i < 256; i++) { 48 | mask |= 1 << (i & 0xff); 49 | } 50 | for (uint256 i = 1; i < 256; i++) { 51 | assertTrue(checker.isSet(mask, uint8(i))); 52 | } 53 | } 54 | 55 | function test_BitMask_isSetOnMaskFalse() public { 56 | for (uint8 i = 1; i <= uint8(254); i++) { 57 | uint256 zeroMask; 58 | assertFalse(checker.isSet(zeroMask, i)); 59 | } 60 | } 61 | 62 | } -------------------------------------------------------------------------------- /test/foundry/lib/modules/SPUMLParams.t.sol: -------------------------------------------------------------------------------- 1 | /* solhint-disable contract-name-camelcase, func-name-mixedcase, var-name-mixedcase */ 2 | // SPDX-License-Identifier: UNLICENSED 3 | // See Story Protocol Alpha Agreement: https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf 4 | pragma solidity ^0.8.19; 5 | 6 | import "forge-std/Test.sol"; 7 | import { Errors } from "contracts/lib/Errors.sol"; 8 | import { Licensing } from "contracts/lib/modules/Licensing.sol"; 9 | import { SPUMLParams } from "contracts/lib/modules/SPUMLParams.sol"; 10 | import { ShortString, ShortStrings } from "@openzeppelin/contracts/utils/ShortStrings.sol"; 11 | 12 | contract SPUMLParamsHarness { 13 | function exposedgetDerivativeChoices() external pure returns (ShortString[] memory) { 14 | return SPUMLParams.getDerivativeChoices(); 15 | } 16 | 17 | function exposedgetParamDefs() external pure returns (Licensing.ParamDefinition[] memory paramDefs) { 18 | return SPUMLParams.getParamDefs(); 19 | } 20 | } 21 | 22 | contract SPUMLParamsTest is Test { 23 | using ShortStrings for *; 24 | 25 | SPUMLParamsHarness public checker; 26 | 27 | function setUp() public { 28 | checker = new SPUMLParamsHarness(); 29 | } 30 | 31 | function test_SPUMLParamsgetDerivativeChoices() public { 32 | ShortString[] memory choices = checker.exposedgetDerivativeChoices(); 33 | assertEq(choices.length, 3); 34 | assertEq(choices[0].toString(), SPUMLParams.ALLOWED_WITH_APPROVAL); 35 | assertEq(choices[1].toString(), SPUMLParams.ALLOWED_WITH_RECIPROCAL_LICENSE); 36 | assertEq(choices[2].toString(), SPUMLParams.ALLOWED_WITH_ATTRIBUTION); 37 | } 38 | 39 | function test_SPUMLParamsgetParamDefs() 40 | public 41 | { 42 | Licensing.ParamDefinition[] memory paramDefs = new Licensing.ParamDefinition[](4); 43 | Licensing.ParamDefinition[] memory actual = checker.exposedgetParamDefs(); 44 | 45 | paramDefs[0] = Licensing.ParamDefinition( 46 | SPUMLParams.CHANNELS_OF_DISTRIBUTION.toShortString(), 47 | Licensing.ParameterType.ShortStringArray, 48 | "", 49 | "" 50 | ); 51 | paramDefs[1] = Licensing.ParamDefinition( 52 | SPUMLParams.ATTRIBUTION.toShortString(), 53 | Licensing.ParameterType.Bool, 54 | abi.encode(false), 55 | "" 56 | ); 57 | paramDefs[2] = Licensing.ParamDefinition( 58 | SPUMLParams.DERIVATIVES_ALLOWED.toShortString(), 59 | Licensing.ParameterType.Bool, 60 | abi.encode(false), 61 | "" 62 | ); 63 | paramDefs[3] = Licensing.ParamDefinition( 64 | SPUMLParams.DERIVATIVES_ALLOWED_OPTIONS.toShortString(), 65 | Licensing.ParameterType.MultipleChoice, 66 | "", 67 | abi.encode(checker.exposedgetDerivativeChoices()) 68 | ); 69 | 70 | assertEq(actual.length, paramDefs.length); 71 | for (uint256 i = 0; i < actual.length; ++i) { 72 | assertEq(actual[i].tag.toString(), paramDefs[i].tag.toString()); 73 | assertEq(uint8(actual[i].paramType), uint8(paramDefs[i].paramType)); 74 | assertEq(actual[i].defaultValue, paramDefs[i].defaultValue); 75 | assertEq(actual[i].availableChoices, paramDefs[i].availableChoices); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /test/foundry/mocks/MockAccessControlled.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSDL-1.1 2 | pragma solidity ^0.8.13; 3 | 4 | import { AccessControlled } from "contracts/access-control/AccessControlled.sol"; 5 | 6 | contract MockAccessControlled is AccessControlled { 7 | constructor(address accessControl) AccessControlled(accessControl) {} 8 | 9 | function exposeOnlyRole(bytes32 role) public onlyRole(role) {} 10 | } 11 | 12 | -------------------------------------------------------------------------------- /test/foundry/mocks/MockAccessControlledUpgradeable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSDL-1.1 2 | pragma solidity ^0.8.13; 3 | 4 | import { AccessControlledUpgradeable } from "contracts/access-control/AccessControlledUpgradeable.sol"; 5 | import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol"; 6 | 7 | 8 | contract MockAccessControlledUpgradeable is AccessControlledUpgradeable { 9 | 10 | bool isInterfaceValid; 11 | 12 | function initialize(address accessControl) public initializer { 13 | __AccessControlledUpgradeable_init(accessControl); 14 | } 15 | 16 | function setIsInterfaceValid(bool isValid) public { 17 | isInterfaceValid = isValid; 18 | } 19 | 20 | function exposeOnlyRole(bytes32 role) public onlyRole(role) {} 21 | 22 | function _authorizeUpgrade(address newImplementation) internal virtual override {} 23 | 24 | function supportsInterface(bytes4 interfaceId) public view returns (bool) { 25 | if (isInterfaceValid) { 26 | return false; 27 | } 28 | return interfaceId == type(IAccessControl).interfaceId; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /test/foundry/mocks/MockAsyncHook.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // See Story Protocol Alpha Agreement: https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf 3 | pragma solidity ^0.8.19; 4 | 5 | import { AsyncBaseHook } from "contracts/hooks/base/AsyncBaseHook.sol"; 6 | 7 | /// @title MockAsyncHook 8 | /// @notice This contract is a mock for testing the AsyncBaseHook contract. 9 | /// @dev It overrides the _requestAsyncCall and handleCallback functions for testing purposes. 10 | contract MockAsyncHook is AsyncBaseHook { 11 | address immutable CALLBACK_CALLER; 12 | 13 | /// @notice Constructs the MockAsyncHook contract. 14 | /// @param accessControl_ The address of the access control contract. 15 | /// @param callbackCaller_ The address of the callback caller contract. 16 | /// @dev The constructor sets the access control and callback caller addresses. 17 | constructor( 18 | address accessControl_, 19 | address callbackCaller_ 20 | ) AsyncBaseHook(accessControl_) { 21 | CALLBACK_CALLER = callbackCaller_; 22 | } 23 | 24 | /// @notice Requests an asynchronous call. 25 | /// @dev This function is overridden for testing purposes. 26 | /// It simply returns the input parameters. 27 | /// @param hookConfig_ The configuration of the hook. 28 | /// @param hookParams_ The parameters for the hook. 29 | /// @return hookData The data for the hook. 30 | /// @return requestId The ID of the request. 31 | function _requestAsyncCall( 32 | bytes memory hookConfig_, 33 | bytes memory hookParams_ 34 | ) 35 | internal 36 | virtual 37 | override 38 | returns (bytes memory hookData, bytes32 requestId) 39 | { 40 | // Simply return the input parameters 41 | return ( 42 | abi.encode(hookConfig_, hookParams_), 43 | getRequestId(hookParams_) 44 | ); 45 | } 46 | 47 | /// @notice Handles a callback. 48 | /// @dev This function is overridden for testing purposes. 49 | /// It simply calls the _handleCallback function with the input parameters. 50 | /// @param requestId_ The ID of the request. 51 | /// @param callbackData_ The data for the callback. 52 | function handleCallback( 53 | bytes32 requestId_, 54 | bytes calldata callbackData_ 55 | ) external { 56 | _handleCallback(requestId_, getProcessedCallbackData(callbackData_)); 57 | } 58 | 59 | function getRequestId( 60 | bytes memory hookParams_ 61 | ) public pure returns (bytes32) { 62 | return bytes32(uint256(keccak256(hookParams_))); 63 | } 64 | 65 | function getProcessedCallbackData(bytes calldata callbackData_) public pure returns (bytes memory result) { 66 | string memory callbackData = abi.decode(callbackData_, (string)); 67 | if (keccak256(abi.encodePacked("PASS")) == keccak256(abi.encodePacked(callbackData))) { 68 | result = abi.encode(true, callbackData); 69 | } else { 70 | result = abi.encode(false, callbackData); 71 | } 72 | } 73 | 74 | function _validateConfig(bytes memory) internal view override {} 75 | 76 | function _callbackCaller( 77 | bytes32 78 | ) internal view virtual override returns (address) { 79 | return CALLBACK_CALLER; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /test/foundry/mocks/MockBaseHook.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // See Story Protocol Alpha Agreement: https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf 3 | pragma solidity ^0.8.19; 4 | import { BaseHook } from "contracts/hooks/base/BaseHook.sol"; 5 | import { Errors } from "contracts/lib/Errors.sol"; 6 | 7 | /// @title MockBaseHook 8 | /// @notice This contract is a mock for testing the BaseHook contract. 9 | /// @dev It extends the BaseHook contract and implements its constructor. 10 | contract MockBaseHook is BaseHook { 11 | 12 | /// @notice Constructs the MockBaseHook contract. 13 | /// @param accessControl_ The address of the access control contract. 14 | /// @dev The constructor sets the access control address. 15 | constructor( 16 | address accessControl_ 17 | ) BaseHook(accessControl_) {} 18 | 19 | /// @notice Mock validation function for testing the hook configuration. 20 | /// @dev This function is used for testing purposes in the MockBaseHook contract. 21 | /// It simulates the validation of the hook configuration by reverting with an error if the configuration equals "ERROR". 22 | /// If the validation passes (i.e., the configuration does not equal "ERROR"), nothing happens. 23 | /// @param hookConfig_ The mock configuration data for the hook, encoded as bytes. 24 | function _validateConfig(bytes memory hookConfig_) internal pure override { 25 | if (keccak256(hookConfig_) == keccak256(abi.encode("ERROR"))) { 26 | revert Errors.ZeroAddress(); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test/foundry/mocks/MockBaseModule.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // See Story Protocol Alpha Agreement: https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf 3 | pragma solidity ^0.8.18; 4 | 5 | import { BaseModule } from "contracts/modules/base/BaseModule.sol"; 6 | import { IIPOrg } from "contracts/interfaces/ip-org/IIPOrg.sol"; 7 | import { ModuleKey } from "contracts/lib/modules/Module.sol"; 8 | 9 | /// @title Mock BaseModule 10 | /// @notice This mock contract is used for testing the base module flow 11 | contract MockBaseModule is BaseModule { 12 | address private _admin; 13 | 14 | struct ModuleExecutionParams { 15 | uint256 paramA; 16 | uint256 paramC; 17 | string someHookRegisteringRelatedInfo; 18 | } 19 | 20 | struct BaseModuleCall { 21 | address ipOrg; 22 | address caller; 23 | bytes params; 24 | } 25 | 26 | BaseModuleCall[] private _callStack; 27 | 28 | constructor( 29 | address admin_, 30 | ModuleConstruction memory params_ 31 | ) BaseModule(params_) { 32 | _admin = admin_; 33 | } 34 | 35 | // Stub for testing authorization via the module registry. 36 | function test() external onlyAuthorized() {} 37 | 38 | function moduleKey() public pure override returns (ModuleKey) { 39 | return ModuleKey.wrap(keccak256(abi.encodePacked("test"))); 40 | } 41 | 42 | function callStackAt( 43 | uint256 index_ 44 | ) external view returns (BaseModuleCall memory) { 45 | return _callStack[index_]; 46 | } 47 | 48 | function _configure( 49 | IIPOrg ipOrg_, 50 | address caller_, 51 | bytes calldata params_ 52 | ) internal virtual override returns (bytes memory) { 53 | _callStack.push(BaseModuleCall(address(ipOrg_), caller_, params_)); 54 | } 55 | 56 | function _verifyExecution( 57 | IIPOrg ipOrg_, 58 | address caller_, 59 | bytes calldata params_ 60 | ) internal virtual override { 61 | _callStack.push(BaseModuleCall(address(ipOrg_), caller_, params_)); 62 | } 63 | 64 | function _performAction( 65 | IIPOrg ipOrg_, 66 | address caller_, 67 | bytes memory params_ 68 | ) internal virtual override returns (bytes memory) { 69 | _callStack.push(BaseModuleCall(address(ipOrg_), caller_, params_)); 70 | return ""; 71 | } 72 | 73 | function registerHooks( 74 | HookType hType_, 75 | IIPOrg ipOrg_, 76 | string memory hookRegistrationInfo_, 77 | address[] calldata hooks_, 78 | bytes[] calldata hooksConfig_ 79 | ) external onlyIpOrgOwner(ipOrg_) { 80 | bytes32 registryKey = _generateRegistryKey(address(ipOrg_), hookRegistrationInfo_); 81 | registerHooks(hType_, ipOrg_, registryKey, hooks_, hooksConfig_); 82 | } 83 | 84 | function hookRegistryKey( 85 | address ipOrg_, 86 | string calldata hookRegistrationInfo_ 87 | ) external pure returns(bytes32) { 88 | return _generateRegistryKey(ipOrg_, hookRegistrationInfo_); 89 | } 90 | 91 | function _hookRegistryKey( 92 | IIPOrg ipOrg_, 93 | address, 94 | bytes calldata params_ 95 | ) internal view virtual override returns(bytes32) { 96 | ModuleExecutionParams memory moduleParams = abi.decode(params_, (ModuleExecutionParams)); 97 | return _generateRegistryKey(address(ipOrg_), moduleParams.someHookRegisteringRelatedInfo); 98 | } 99 | 100 | function _generateRegistryKey( 101 | address ipOrg_, 102 | string memory hookRegistrationInfo_ 103 | ) private pure returns(bytes32) { 104 | return keccak256(abi.encode(ipOrg_, hookRegistrationInfo_)); 105 | } 106 | 107 | 108 | } 109 | -------------------------------------------------------------------------------- /test/foundry/mocks/MockCallbackHandler.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // See Story Protocol Alpha Agreement: https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf 3 | pragma solidity ^0.8.19; 4 | import { ICallbackHandler } from "contracts/interfaces/hooks/base/ICallbackHandler.sol"; 5 | import { ERC165 } from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; 6 | import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; 7 | 8 | /// @title MockCallbackHandler 9 | /// @notice This contract is a mock for testing the ICallbackHandler interface. 10 | /// @dev It extends the ERC165 contract and implements the ICallbackHandler interface. 11 | contract MockCallbackHandler is ERC165, ICallbackHandler { 12 | bytes32 public lastHandledRequestId; 13 | bytes public lastHandledCallbackData; 14 | 15 | /// @notice Handles a hook callback. 16 | /// @dev This function stores the input parameters for later inspection. 17 | /// @param requestId The ID of the request. 18 | /// @param callbackData The data for the callback. 19 | function handleHookCallback(bytes32 requestId, bytes calldata callbackData) external override { 20 | // Store the parameters for later inspection 21 | lastHandledRequestId = requestId; 22 | lastHandledCallbackData = callbackData; 23 | } 24 | 25 | /// @notice Checks if the contract supports an interface. 26 | /// @dev This function returns true if the interface ID is for the ICallbackHandler interface. 27 | /// @param interfaceId The ID of the interface. 28 | /// @return true if the contract supports the interface, false otherwise. 29 | function supportsInterface(bytes4 interfaceId) public view override(ERC165, IERC165) returns (bool) { 30 | // Check if the interface ID is for the ICallbackHandler interface 31 | return interfaceId == type(ICallbackHandler).interfaceId || super.supportsInterface(interfaceId); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /test/foundry/mocks/MockERC20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSDL-1.1 2 | pragma solidity ^0.8.13; 3 | import {ERC20} from "solmate/src/tokens/ERC20.sol"; 4 | 5 | contract MockERC20 is ERC20 { 6 | constructor(string memory name, string memory symbol, uint8 decimals) ERC20(name, symbol, decimals) {} 7 | 8 | function mint(uint256 amount) external { 9 | _mint(msg.sender, amount); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /test/foundry/mocks/MockERC721.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSDL-1.1 2 | pragma solidity ^0.8.13; 3 | 4 | import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; 5 | 6 | contract MockERC721 is ERC721 { 7 | constructor() ERC721("MockERC721", "M721") {} 8 | 9 | function mint(address to, uint256 tokenId) external { 10 | _safeMint(to, tokenId); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /test/foundry/mocks/MockERC721Receiver.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // See Story Protocol Alpha Agreement: https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf 3 | pragma solidity ^0.8.18; 4 | 5 | import '@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol'; 6 | 7 | import { IERC721Events } from "../interfaces/IERC721Events.sol"; 8 | import { Errors } from "contracts/lib/Errors.sol"; 9 | 10 | /// @title Mock ERC-721 Receiver 11 | /// @notice This contract is used to test ERC-721 safe transfers. 12 | contract MockERC721Receiver is IERC721Receiver, IERC721Events { 13 | 14 | /// @notice Custom event used to vet whether a receive was successful. 15 | event ERC721Received(address operator, address from, uint256 tokenId, bytes data); 16 | 17 | // Programmable return value to mock for `onERC721Received` call returns. 18 | bytes4 private immutable _retval; 19 | 20 | // Whether the contract should throw on `onERC721Received` calls. 21 | bool private immutable _throws; 22 | 23 | /// @notice Initializes a mock ERC-721 receiver contract. 24 | /// @param retval The return value to send on `onERC721Received` calls. 25 | /// @param throws Whether to revert on `onERC721Received` calls. 26 | constructor(bytes4 retval, bool throws) { 27 | _retval = retval; 28 | _throws = throws; 29 | } 30 | 31 | /// @notice Handles the receiving of an ERC-721 NFT during safe transfers. 32 | /// @param operator The address approved for operating on the specified NFT. 33 | /// @param from The address of the current NFT owner. 34 | /// @param tokenId The id of the NFT being received. 35 | /// @param data Additional data sent with no specified format. 36 | function onERC721Received( 37 | address operator, 38 | address from, 39 | uint256 tokenId, 40 | bytes memory data 41 | ) public override returns (bytes4) { 42 | if (_throws) { 43 | revert Errors.ERC721_SafeTransferUnsupported(); 44 | } 45 | emit ERC721Received(operator, from, tokenId, data); 46 | return _retval; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /test/foundry/mocks/MockGateway.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // See https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf 3 | pragma solidity ^0.8.18; 4 | 5 | import { MockBaseModule } from "./MockBaseModule.sol"; 6 | import { Gateway } from "contracts/modules/Gateway.sol"; 7 | import { ModuleKey, ModuleDependencies } from "contracts/lib/modules/Module.sol"; 8 | import { ModuleRegistry } from "contracts/modules/ModuleRegistry.sol"; 9 | 10 | /// @title Mock Gateway 11 | /// @notice This mock contract is used for testing the gateway. 12 | contract MockGateway is Gateway { 13 | 14 | ModuleKey constant TEST_MODULE_KEY = ModuleKey.wrap(keccak256(abi.encodePacked("test"))); 15 | 16 | bool isValid; 17 | MockBaseModule module; 18 | 19 | constructor(bool isValid_, ModuleRegistry moduleRegistry_) Gateway(moduleRegistry_) { 20 | isValid = isValid_; 21 | } 22 | 23 | function updateDependencies() 24 | external 25 | override 26 | onlyModuleRegistry 27 | returns (ModuleDependencies memory dependencies) 28 | { 29 | // Synchronize relevant modules with the registry. 30 | module = MockBaseModule(MODULE_REGISTRY.protocolModule(TEST_MODULE_KEY)); 31 | return getDependencies(); 32 | } 33 | 34 | function getDependencies() public view override returns (ModuleDependencies memory dependencies) { 35 | ModuleKey[] memory keys; 36 | bytes4[][] memory fns; 37 | if (!isValid) { 38 | keys = new ModuleKey[](1); 39 | fns = new bytes4[][](2); 40 | 41 | keys[0] = TEST_MODULE_KEY; 42 | bytes4[] memory moduleFns = new bytes4[](2); 43 | fns[0] = moduleFns; 44 | 45 | } else { 46 | keys = new ModuleKey[](1); 47 | fns = new bytes4[][](1); 48 | keys[0] = TEST_MODULE_KEY; 49 | bytes4[] memory moduleFns = new bytes4[](1); 50 | moduleFns[0] = MockBaseModule.test.selector; 51 | fns[0] = moduleFns; 52 | } 53 | return ModuleDependencies(keys, fns); 54 | } 55 | 56 | function setIsValid(bool isValid_) public { 57 | isValid = isValid_; 58 | } 59 | 60 | function callModule() external { 61 | return module.test(); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /test/foundry/mocks/MockHookRegistry.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // See Story Protocol Alpha Agreement: https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf 3 | pragma solidity ^0.8.18; 4 | 5 | import { HookRegistry } from "contracts/modules/base/HookRegistry.sol"; 6 | import { IIPOrg } from "contracts/interfaces/ip-org/IIPOrg.sol"; 7 | import { ModuleRegistry } from "contracts/modules/ModuleRegistry.sol"; 8 | 9 | /// @title Mock Hook Registry 10 | /// @notice This mock contract is used for testing the base hook registry. 11 | contract MockHookRegistry is HookRegistry { 12 | constructor(ModuleRegistry moduleRegistry_) HookRegistry(moduleRegistry_) {} 13 | 14 | function hookRegistryKey( 15 | address ipOrg_, 16 | string calldata someHookRegisteringRelatedInfo_ 17 | ) public pure returns (bytes32) { 18 | return _generateRegistryKey(ipOrg_, someHookRegisteringRelatedInfo_); 19 | } 20 | 21 | function _generateRegistryKey( 22 | address ipOrg_, 23 | string memory someHookRegisteringRelatedInfo_ 24 | ) private pure returns (bytes32) { 25 | return keccak256(abi.encode(ipOrg_, someHookRegisteringRelatedInfo_)); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /test/foundry/mocks/MockIPAssetOrgFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // See Story Protocol Alpha Agreement: https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf 3 | pragma solidity ^0.8.13; 4 | 5 | contract MockIPOrgController { 6 | 7 | address ipAssetOrgAddress; 8 | 9 | function setIpAssetRegistryAddress(address _ipAssetOrgAddress) external { 10 | ipAssetOrgAddress = _ipAssetOrgAddress; 11 | } 12 | 13 | function ipAssetOrgForId( 14 | uint256 franchiseId 15 | ) public view returns (address) { 16 | if (franchiseId == 1) { 17 | return ipAssetOrgAddress; 18 | } 19 | return address(0); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /test/foundry/mocks/MockIPOrg.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // See Story Protocol Alpha Agreement: https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf 3 | pragma solidity ^0.8.19; 4 | 5 | import { IIPOrg } from "contracts/interfaces/ip-org/IIPOrg.sol"; 6 | 7 | contract MockIPOrg is IIPOrg { 8 | 9 | address private _owner; 10 | 11 | constructor(address owner_) { 12 | _owner = owner_; 13 | } 14 | 15 | function ownerOf(uint256 id) external view returns (address) { 16 | return _owner; 17 | } 18 | 19 | function burn(uint256 id) external override(IIPOrg) {} 20 | 21 | function contractURI() external pure returns (string memory) { 22 | return ""; 23 | } 24 | 25 | function transferFrom(address from, address to, uint256 id) external {} 26 | 27 | function mint(address owner_, uint8 type_) external override(IIPOrg) returns (uint256 id) {} 28 | 29 | function owner() external view override(IIPOrg) returns (address) { 30 | return _owner; 31 | } 32 | 33 | function ipOrgAssetType(uint256 id_) external pure override(IIPOrg) returns (uint8) { 34 | return 0; 35 | } 36 | 37 | function ipAssetId(uint256 id_) external returns (uint256) { 38 | return 0; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /test/foundry/mocks/MockIPOrgController.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // See Story Protocol Alpha Agreement: https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf 3 | pragma solidity ^0.8.13; 4 | 5 | contract MockIPOrgController { 6 | 7 | address ipAssetOrgAddress; 8 | 9 | function setIpAssetRegistryAddress(address _ipAssetOrgAddress) external { 10 | ipAssetOrgAddress = _ipAssetOrgAddress; 11 | } 12 | 13 | function ipAssetOrgForId( 14 | uint256 franchiseId 15 | ) public view returns (address) { 16 | if (franchiseId == 1) { 17 | return ipAssetOrgAddress; 18 | } 19 | return address(0); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /test/foundry/mocks/MockNativeTokenNonReceiver.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // See Story Protocol Alpha Agreement: https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf 3 | pragma solidity ^0.8.18; 4 | 5 | error Revert(); 6 | 7 | contract MockNativeTokenNonReceiver { 8 | 9 | receive() external payable { 10 | revert Revert(); 11 | } 12 | } 13 | 14 | -------------------------------------------------------------------------------- /test/foundry/mocks/MockPolygonTokenClient.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.19; 3 | 4 | /// @title MockPolygonTokenClient 5 | /// @notice This contract client of Polygon Token Oracle 6 | contract MockPolygonTokenClient { 7 | /// @notice Emits an event for a Polygon token balance request. 8 | /// @param requestId The unique ID of the request. 9 | /// @param requester The address of the requester. 10 | /// @param tokenAddress The address of the token. 11 | /// @param tokenOwnerAddress The address of the token owner. 12 | /// @param callbackAddr The address of the callback. 13 | /// @param callbackFunctionSignature The signature of the callback function. 14 | event PolygonTokenBalanceRequest( 15 | bytes32 indexed requestId, 16 | address indexed requester, 17 | address tokenAddress, 18 | address tokenOwnerAddress, 19 | address callbackAddr, 20 | bytes4 callbackFunctionSignature 21 | ); 22 | 23 | function sendRequest( 24 | bytes32 requestId, 25 | address requester, 26 | address tokenAddress, 27 | address tokenOwnerAddress, 28 | address callbackAddr, 29 | bytes4 callbackFunctionSignature 30 | ) external { 31 | emit PolygonTokenBalanceRequest( 32 | requestId, 33 | requester, 34 | tokenAddress, 35 | tokenOwnerAddress, 36 | callbackAddr, 37 | callbackFunctionSignature 38 | ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /test/foundry/mocks/MockSyncHook.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // See Story Protocol Alpha Agreement: https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf 3 | pragma solidity ^0.8.19; 4 | import { SyncBaseHook } from "contracts/hooks/base/SyncBaseHook.sol"; 5 | 6 | /// @title MockSyncHook 7 | /// @notice This contract is a mock for testing the SyncBaseHook contract. 8 | /// @dev It extends the SyncBaseHook contract and overrides its _executeSyncCall function. 9 | contract MockSyncHook is SyncBaseHook { 10 | bool public shouldExecuteSuccess; 11 | /// @notice Constructs the MockSyncHook contract. 12 | /// @param accessControl_ The address of the access control contract. 13 | /// @dev The constructor sets the access control address. 14 | constructor( 15 | address accessControl_ 16 | ) SyncBaseHook(accessControl_) { 17 | shouldExecuteSuccess = true; 18 | } 19 | 20 | function setShouldExecuteSuccess(bool shouldExecuteSuccess_) public { 21 | shouldExecuteSuccess = shouldExecuteSuccess_; 22 | } 23 | 24 | /// @notice Executes a synchronous call. 25 | /// @dev This function is overridden for testing purposes. 26 | /// It simply returns the input parameters. 27 | /// @param hookConfig_ The configuration of the hook. 28 | /// @param hookParams_ The parameters for the hook. 29 | /// @return The parameters for the hook. 30 | function _executeSyncCall( 31 | bytes memory hookConfig_, 32 | bytes memory hookParams_ 33 | ) 34 | internal 35 | virtual 36 | override 37 | returns (bytes memory) 38 | { 39 | if (!shouldExecuteSuccess) { 40 | revert("EXPECTED_FAILURE"); 41 | } 42 | // Simply return the input parameters 43 | return abi.encode(hookConfig_, hookParams_); 44 | } 45 | 46 | function _validateConfig(bytes memory) internal view override {} 47 | } 48 | -------------------------------------------------------------------------------- /test/foundry/mocks/MockThrowingERC20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSDL-1.1 2 | pragma solidity ^0.8.13; 3 | import {ERC20} from "solmate/src/tokens/ERC20.sol"; 4 | 5 | contract MockThrowingERC20 is ERC20 { 6 | 7 | enum TransferBehavior { 8 | Fail, 9 | ReturnInvalidABI, 10 | ReturnFalse 11 | } 12 | 13 | TransferBehavior public behavior; 14 | 15 | constructor(string memory name, string memory symbol, uint8 decimals, TransferBehavior behavior_) ERC20(name, symbol, decimals) { 16 | behavior = behavior_; 17 | } 18 | 19 | function mint(uint256 amount) external { 20 | _mint(msg.sender, amount); 21 | } 22 | 23 | function transferFrom(address sender, address recipient, uint256 amount) public override returns (bool) { 24 | if (behavior == TransferBehavior.Fail) { 25 | revert(); 26 | } 27 | 28 | if (behavior == TransferBehavior.ReturnInvalidABI) { 29 | assembly { 30 | mstore(0x0, 0x1) 31 | return(0x0, 1) 32 | } 33 | } 34 | 35 | if (behavior == TransferBehavior.ReturnFalse) { 36 | return false; 37 | } 38 | 39 | return super.transferFrom(sender, recipient, amount); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /test/foundry/mocks/MockWETH.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSDL-1.1 2 | pragma solidity ^0.8.18; 3 | 4 | import {WETH} from "solmate/src/tokens/WETH.sol"; 5 | 6 | contract MockWETH is WETH { 7 | 8 | function mint(uint256 amount) external { 9 | _mint(msg.sender, amount); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /test/foundry/utils/AccessControlHelper.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSDL-1.1 2 | pragma solidity ^0.8.19; 3 | 4 | import "test/foundry/utils/ProxyHelper.sol"; 5 | import { AccessControl } from "contracts/lib/AccessControl.sol"; 6 | import { AccessControlSingleton } from "contracts/access-control/AccessControlSingleton.sol"; 7 | import { Vm } from "forge-std/Test.sol"; 8 | import "forge-std/console.sol"; 9 | import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; 10 | 11 | /// @title AccessControlHelper 12 | /// @notice Helper contract to setup AccessControlSingleton and grant roles 13 | contract AccessControlHelper is ProxyHelper { 14 | AccessControlSingleton accessControl; 15 | address admin = address(123); 16 | 17 | constructor() {} 18 | 19 | function _setupAccessControl() internal { 20 | // Create Access Control 21 | address accessControlSingletonImpl = address( 22 | new AccessControlSingleton() 23 | ); 24 | accessControl = AccessControlSingleton( 25 | _deployUUPSProxy( 26 | accessControlSingletonImpl, 27 | abi.encodeWithSelector( 28 | bytes4(keccak256(bytes("initialize(address)"))), 29 | admin 30 | ) 31 | ) 32 | ); 33 | } 34 | 35 | function _grantRole(Vm vm, bytes32 role, address account) internal { 36 | vm.prank(admin); 37 | accessControl.grantRole(role, account); 38 | } 39 | 40 | function _getRoleErrorMessage( 41 | address sender, 42 | bytes32 role 43 | ) internal pure returns (bytes memory) { 44 | return 45 | abi.encodePacked( 46 | "AccessControl: account ", 47 | Strings.toHexString(uint160(sender), 20), 48 | " is missing role ", 49 | Strings.toHexString(uint256(role), 32) 50 | ); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /test/foundry/utils/BaseTestUtils.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // See Story Protocol Alpha Agreement: https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf 3 | pragma solidity ^0.8.18; 4 | 5 | import "forge-std/Test.sol"; 6 | 7 | contract BaseTestUtils is Test { 8 | 9 | // Test public keys EOAs that may be reused for testing and deriving EOAs. 10 | uint256 internal alicePk = 0xa11ce; 11 | uint256 internal bobPk = 0xb0b; 12 | uint256 internal calPk = 0xca1; 13 | 14 | // Test EOA addresses that may be reused for testing. 15 | address payable internal alice = payable(vm.addr(alicePk)); 16 | address payable internal bob = payable(vm.addr(bobPk)); 17 | address payable internal cal = payable(vm.addr(calPk)); 18 | 19 | /// @notice Modifier that ensures that a receiver address does not happen 20 | /// to be a built-in foundry contract that breaks EOA assumptions. 21 | modifier isValidReceiver(address receiver) { 22 | vm.assume(receiver != 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); // HEVM Address 23 | vm.assume(receiver != 0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496); // Foundry Test Contract 24 | vm.assume(receiver != 0x4e59b44847b379578588920cA78FbF26c0B4956C); // CREATE2 Deployer 25 | vm.assume(receiver != 0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f); 26 | vm.assume(receiver != 0x104fBc016F4bb334D775a19E8A6510109AC63E00); 27 | _; 28 | } 29 | 30 | /// @notice Helper function that allows running "subtests" that revert back 31 | /// to a snapshotted state after being run. 32 | modifier stateless() { 33 | uint256 snapshot = vm.snapshot(); 34 | _; 35 | vm.revertTo(snapshot); 36 | } 37 | 38 | function setUp() public virtual { 39 | vm.label(alice, "alice"); 40 | vm.label(bob, "bob"); 41 | vm.label(cal, "cal"); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /test/foundry/utils/ProxyHelper.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.19; 3 | import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; 4 | 5 | contract ProxyHelper { 6 | 7 | function _deployUUPSProxy(address _logic, bytes memory _data) internal returns (address) { 8 | ERC1967Proxy proxy = new ERC1967Proxy(_logic, _data); 9 | return address(proxy); 10 | } 11 | } 12 | --------------------------------------------------------------------------------