diff --git a/.gitmodules b/.gitmodules
index a8ba7c7a..f747d275 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,9 +1,9 @@
-[submodule "moda-contracts/lib/forge-std"]
- path = moda-contracts/lib/forge-std
+[submodule "contracts/lib/forge-std"]
+ path = contracts/lib/forge-std
url = https://github.com/foundry-rs/forge-std
-[submodule "moda-contracts/lib/openzeppelin-contracts-upgradeable"]
- path = moda-contracts/lib/openzeppelin-contracts-upgradeable
+[submodule "contracts/lib/openzeppelin-contracts-upgradeable"]
+ path = contracts/lib/openzeppelin-contracts-upgradeable
url = https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable
-[submodule "moda-contracts/lib/openzeppelin-foundry-upgrades"]
- path = moda-contracts/lib/openzeppelin-foundry-upgrades
+[submodule "contracts/lib/openzeppelin-foundry-upgrades"]
+ path = contracts/lib/openzeppelin-foundry-upgrades
url = https://github.com/OpenZeppelin/openzeppelin-foundry-upgrades
diff --git a/LICENSE b/LICENSE
index c8df6bf2..b81b15c7 100644
--- a/LICENSE
+++ b/LICENSE
@@ -187,7 +187,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
- Copyright 2023 MODA Foundation
+ Copyright 2024 DROPcmdk
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/README.md b/README.md
index 8bfdce00..1b5531b9 100644
--- a/README.md
+++ b/README.md
@@ -1,18 +1,4 @@
-
-

-
-
-
-
-
-
-
A web3 protocol for distributing music on EVM chains.
-
- [![k1]][k2]
-
- [k1]: https://img.shields.io/twitter/follow/moda_dao?style=flat
- [k2]: https://x.com/MODA_DAO
-
+
## Getting Started
@@ -23,8 +9,8 @@ pnpm i
2. Set up environment variables
```bash
-cp ./moda-contracts/.env-local ./moda-contracts/.env
-# Add your values to ./moda-contracts/.env
+cp ./contracts/.env-local ./contracts/.env
+# Add your values to ./contracts/.env
```
## Generate Smart Contract Documentation
@@ -32,44 +18,26 @@ cp ./moda-contracts/.env-local ./moda-contracts/.env
1. `pnpm c:docs`
2. Visit [localhost:8080](localhost:8080)
-### Deploy Membership
+### Membership //TODO
-Membership is a way to control who has access to your Catalog. The default implementation defines any address as a "member", which means anyone can register tracks in the Catalog. You can extend this logic to define your own rules such as NFT-gates, minimal ERC-20 balance amounts, or a generic whitelist. See Membership.sol in `./moda-contracts/src/Membership.sol`
+Membership is a way to control who has access to your Registry. The default implementation defines any address as a "member", which means anyone can register tracks in the Catalog. You can extend this logic to define your own rules such as NFT-gates, minimal ERC-20 balance amounts, or a generic whitelist. See Membership.sol in `./contracts/src/Membership.sol`
1. Deploy contract `pnpm 1_deploy_Membership --chain mumbai`
-### Deploy Catalog
-
-2. Deploy contract `pnpm 2_deploy_Catalog --chain mumbai --sender `
-
-Then look in the console for your Catalog address:
-```
- $ Your Catalog was deployed to:
- $
-```
+### Registry
-## (Optional, but not recommended) Deploy Common-Good Contracts
+#### Technical Overview
-These contracts have already been deployed on Mumbai. It is recommended that you use the ones already deployed so that the creators can benefit, but if you need to change certain features you are free to redeploy these. If you would like these contracts on a particular chain please reach out to the team and we will help facilitate.
+The Registry is an upgradeable contract and the only one in the protocol. It follows an Upgradeable Beacon pattern in which a Beacon contract containing the the implementation address is deployed and controlled by an Authorized account. Any proxy that is deployed will refer to this Beacon contract for the implementation address.
-### Deploy Registry
-`pnpm c:deploy_Registry --chain mumbai`
+The Registry uses a Namespaced Storage Layout defined in [ERC-7201](https://eips.ethereum.org/EIPS/eip-7201)
-### Deploy Profile
-`pnpm c:deploy_Profile --chain mumbai`
+The [Foundry Upgrades](https://docs.openzeppelin.com/upgrades-plugins/1.x/api-foundry-upgrades) library is used for ease of upgradeable contract deployment and customizable upgrade safety validations.
-### Deploy Management
-`pnpm c:deploy_Management --chain mumbai`
-### Deploy Splits Factory
-`pnpm c:deploy_SplitsFactory --chain mumbai`
-### Deploy CatalogFactory
-`pnpm c:deploy_CatalogFactory --chain mumbai`
-### Register Contracts with Registry
-`pnpm c:set_OfficialContracts --chain mumbai --sender `
## Automatic track verification
@@ -88,7 +56,7 @@ All registered tracks have a status that is used to represent their authenticity
## Smart Contract Testing
-Navigate to the moda-contracts directory and run
+Navigate to the contracts directory and run
```bash
pnpm test
@@ -102,7 +70,7 @@ pnpm coverage
## License
-`moda-protocol` is [Apache licensed](LICENSE).
+`drop-protocol` is [Apache licensed](LICENSE).
## Contributing
diff --git a/apps/dapp/next.config.js b/apps/dapp/next.config.js
index cb14c4ce..cdc347cb 100644
--- a/apps/dapp/next.config.js
+++ b/apps/dapp/next.config.js
@@ -3,14 +3,14 @@ const nextConfig = {
images: {
remotePatterns: [
{
- protocol: 'https',
- hostname: 'drops.moda.audio',
- pathname: '/ipfs/*',
+ protocol: "https",
+ hostname: "", //TODO
+ pathname: "/ipfs/*",
},
{
- protocol: 'https',
- hostname: 'gateway.pinata.cloud',
- pathname: '/ipfs/*',
+ protocol: "https",
+ hostname: "gateway.pinata.cloud",
+ pathname: "/ipfs/*",
},
],
},
diff --git a/moda-contracts/.env.example b/contracts/.env.example
similarity index 79%
rename from moda-contracts/.env.example
rename to contracts/.env.example
index c513a622..cc08c05e 100644
--- a/moda-contracts/.env.example
+++ b/contracts/.env.example
@@ -2,15 +2,17 @@
# DEVELOPER CONFIG - Change these to match your organization values
# =================================================================
DEPLOYER_PRIVATE_KEY=SECRET
-EVM_RPC_URL=https://polygon-mumbai.g.alchemy.com/v2/
+EVM_RPC_URL=
+TESTNET_RPC_URL=
# Etherscan or Polygonscan API Key
ETHERSCAN_API_KEY=SECRET
-# Change the catalog to your organization name.
-CATALOG_NAME="DRIP"
-# Once deployed add your Catalog address here.
-CATALOG_ADDRESS=<0xCatalogAddress...>
+# USDC Address
+USDC_ADDRESS=0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238 # Sepolia
+
+# Once deployed add your Registry address here.
+REGISTRY_ADDRESS=<0xRegistryAddress...>
# OpenReleases Contract (Optional & Recommended) - Organizations can deploy a common good Releases contract for artists and
# labels to release tracks without the need of deploying their own Releases contract.
@@ -26,7 +28,7 @@ TREASURY_ADDRESS=0x01...
# Change this to the address on your chain if you are deploying common good contracts.
# See: https://docs.splits.org/core/split#addresses
-SPLIT_MAIN_ADDRESS=0x2ed6c4B5dA6378c7897AC67Ba9e43102Feb694EE # Mumbai
+SPLIT_MAIN_ADDRESS=0x54E4a6014D36c381fC43b7E24A1492F556139a6F # Sepolia
# =================================================================
=======
diff --git a/contracts/.gas-snapshot b/contracts/.gas-snapshot
new file mode 100644
index 00000000..0d66eac0
--- /dev/null
+++ b/contracts/.gas-snapshot
@@ -0,0 +1,163 @@
+MarketplaceTest:test_constructor() (gas: 10761)
+MarketplaceTest:test_createSale() (gas: 432806)
+MarketplaceTest:test_createSale_RevertIf_Releases_is_not_registered() (gas: 49023)
+MarketplaceTest:test_createSale_RevertIf_amountTotal_is_zero() (gas: 49057)
+MarketplaceTest:test_createSale_RevertIf_beneficiary_address_is_zero() (gas: 49090)
+MarketplaceTest:test_createSale_RevertIf_maxCountPerWallet_is_zero() (gas: 49213)
+MarketplaceTest:test_createSale_RevertIf_startAt_is_after_endAt() (gas: 47607)
+MarketplaceTest:test_createSale_emits_event() (gas: 426522)
+MarketplaceTest:test_purchase() (gas: 600636)
+MarketplaceTest:test_purchase_RevertIf_Sale_has_ended() (gas: 432092)
+MarketplaceTest:test_purchase_RevertIf_Sale_has_not_started() (gas: 433422)
+MarketplaceTest:test_purchase_RevertIf_maxCountPerWallet_is_exceeded() (gas: 594112)
+MarketplaceTest:test_purchase_RevertIf_tokenAmount_is_greater_than_amountRemaining() (gas: 435268)
+MarketplaceTest:test_purchase_RevertIf_tokenAmount_is_zero() (gas: 434655)
+MarketplaceTest:test_purchase_emits_event() (gas: 588888)
+MarketplaceTest:test_withdraw() (gas: 450701)
+OpenReleasesFactoryTest:test_constructor() (gas: 14921)
+OpenReleasesFactoryTest:test_create() (gas: 289818)
+OpenReleasesTest:test_burn_emits_event() (gas: 990909)
+OpenReleasesTest:test_constructor() (gas: 29107)
+OpenReleasesTest:test_create_RevertIf_caller_is_not_track_owner() (gas: 588877)
+OpenReleasesTest:test_create_RevertIf_royalty_amount_is_over_2000() (gas: 595969)
+OpenReleasesTest:test_create_emits_event() (gas: 1076025)
+OpenReleasesTest:test_create_release() (gas: 1100692)
+OpenReleasesTest:test_removeRelease() (gas: 993576)
+OpenReleasesTest:test_royaltyInfo() (gas: 1079364)
+OpenReleasesTest:test_setUri() (gas: 1083522)
+OpenReleasesTest:test_setUri_RevertIf_caller_is_not_release_owner() (gas: 1077540)
+OpenReleasesTest:test_setUri_RevertIf_tokenId_is_invalid() (gas: 1077380)
+OpenReleasesTest:test_setUri_emits_event() (gas: 1082455)
+OpenReleasesTest:test_supportsInterface() (gas: 8539)
+ProfileTest:test_accountUri() (gas: 109271)
+ProfileTest:test_accountUri_reverts_with_nonexistent_profile() (gas: 17082)
+ProfileTest:test_approve_reverts() (gas: 10759)
+ProfileTest:test_balanceOf_with_profile() (gas: 106209)
+ProfileTest:test_balanceOf_without_profile() (gas: 10017)
+ProfileTest:test_canMintFor_as_IAccessControl_DEFAULT_ADMIN_ROLE() (gas: 278295)
+ProfileTest:test_canMintFor_as_owner() (gas: 92572)
+ProfileTest:test_canMintFor_without_DEFAULT_ADMIN_ROLE() (gas: 282304)
+ProfileTest:test_canMintFor_without_ownership() (gas: 95031)
+ProfileTest:test_constructor() (gas: 19907)
+ProfileTest:test_getApproved_reverts() (gas: 8611)
+ProfileTest:test_isApprovedForAll() (gas: 10196)
+ProfileTest:test_mintFor_as_IAccessControl_DEFAULT_ADMIN_ROLE() (gas: 387396)
+ProfileTest:test_mintFor_as_owner() (gas: 201768)
+ProfileTest:test_mintFor_emits_events() (gas: 195491)
+ProfileTest:test_mintFor_increments_total_supply() (gas: 266038)
+ProfileTest:test_mintFor_reverts_for_duplicates() (gas: 198767)
+ProfileTest:test_mintFor_without_DEFAULT_ADMIN_ROLE() (gas: 288527)
+ProfileTest:test_mintFor_without_ownership() (gas: 101186)
+ProfileTest:test_mint_creates_new_token() (gas: 115503)
+ProfileTest:test_mint_emits_event() (gas: 107567)
+ProfileTest:test_mint_increments_total_supply() (gas: 182489)
+ProfileTest:test_mint_reverts_for_duplicates() (gas: 104163)
+ProfileTest:test_name() (gas: 12348)
+ProfileTest:test_ownerOf() (gas: 106412)
+ProfileTest:test_ownerOf_reverts_with_nonexistent_token() (gas: 10708)
+ProfileTest:test_returns_false_for_unsupported_interfaces() (gas: 5988)
+ProfileTest:test_safeTransferFrom_reverts() (gas: 12946)
+ProfileTest:test_safeTransferFrom_with_data_arg_reverts() (gas: 13585)
+ProfileTest:test_setApprovalForAll_reverts() (gas: 10805)
+ProfileTest:test_supports_ERC165() (gas: 5969)
+ProfileTest:test_supports_IERC165() (gas: 5971)
+ProfileTest:test_supports_IERC4906() (gas: 5957)
+ProfileTest:test_supports_IERC721() (gas: 5880)
+ProfileTest:test_supports_IERC721Metadata() (gas: 5895)
+ProfileTest:test_supports_IProfile() (gas: 5891)
+ProfileTest:test_symbol() (gas: 12390)
+ProfileTest:test_tokenUri() (gas: 108898)
+ProfileTest:test_tokenUri_reverts_with_nonexistent_token() (gas: 12764)
+ProfileTest:test_totalSupply_returns_the_token_count() (gas: 101291)
+ProfileTest:test_transferFrom_reverts() (gas: 12954)
+ProfileTest:test_updateProfileFor_emits_events() (gas: 391007)
+ProfileTest:test_updateProfileFor_reverts_for_nonexisting_profile() (gas: 286741)
+ProfileTest:test_updateProfileFor_reverts_when_unauthorized() (gas: 391805)
+ProfileTest:test_updateProfileFor_with_AccessControl_DEFAULT_ADMIN_ROLE() (gas: 401956)
+ProfileTest:test_updateProfileFor_with_ownership_sets_new_uri() (gas: 215621)
+ProfileTest:test_updateProfile_emits_MetadataUpdate() (gas: 108851)
+ProfileTest:test_updateProfile_reverts_for_nonexisting_profile() (gas: 13255)
+ProfileTest:test_updateProfile_sets_new_uri() (gas: 126727)
+RegisterReleaseTest:test_getReleaseTracks() (gas: 426539)
+RegisterReleaseTest:test_registerRelease_RevertIf_release_duplicate() (gas: 715756)
+RegisterReleaseTest:test_registerRelease_RevertIf_releases_contract_has_no_approval() (gas: 410240)
+RegisterReleaseTest:test_registerRelease_RevertIf_releases_contract_not_registered() (gas: 397157)
+RegisterReleaseTest:test_registerRelease_RevertIf_track_not_validated() (gas: 471437)
+RegisterReleaseTest:test_registerRelease_RevertIf_tracks_unregistered() (gas: 410833)
+RegisterReleaseTest:test_registerRelease_emits_event() (gas: 693316)
+RegisterReleaseTest:test_registerRelease_with_setReleasesApproval() (gas: 707534)
+RegisterReleaseTest:test_registerRelease_with_setReleasesApprovalForAll() (gas: 668860)
+RegisterReleaseTest:test_unregisterRelease() (gas: 571904)
+RegisterReleaseTest:test_unregisterRelease_emits_event() (gas: 569784)
+RegisterReleasesContractTest:test_registerReleasesContract() (gas: 20266)
+RegisterReleasesContractTest:test_registerReleasesContract_emits_event() (gas: 134132)
+RegisterReleasesContractTest:test_registerReleasesContract_reverts_if_no_releases_registrar_role() (gas: 52701)
+RegisterReleasesContractTest:test_registerReleasesContract_reverts_if_releases_contract_already_registered() (gas: 38370)
+RegisterReleasesContractTest:test_revert_if_unregistering_unregistered_releases_contract() (gas: 24122)
+RegisterReleasesContractTest:test_unregisterReleasesContract() (gas: 28370)
+RegisterReleasesContractTest:test_unregisterReleasesContract_emits_event() (gas: 30129)
+RegisterTrackTest:test_registerTrack() (gas: 200654)
+RegisterTrackTest:test_registerTrack_RevertIf_track_already_registered() (gas: 193313)
+RegisterTrackTest:test_registerTrack_RevertIf_user_not_member() (gas: 32188)
+RegisterTrackTest:test_registerTrack_emits_event() (gas: 165876)
+RegisterTrackTest:test_registerTrack_with_AUTO_VERIFIED_ROLE() (gas: 209434)
+RegistryInitializeTest:test_initialize_RevertIf_already_initialized() (gas: 6791331)
+RegistryUpgradesTest:test_beacon() (gas: 7489)
+RegistryUpgradesTest:test_implementation_address_updated() (gas: 3315989)
+RegistryUpgradesTest:test_proxy_can_call_updated_Registry() (gas: 3323900)
+ReleasesApprovalTest:test_remove_releases_approval() (gas: 216979)
+ReleasesApprovalTest:test_setReleasesApproval() (gas: 227866)
+ReleasesApprovalTest:test_setReleasesApprovalForAll() (gas: 223322)
+ReleasesApprovalTest:test_setReleasesApprovalForAll_RevertIf_setting_approval_not_artist() (gas: 199177)
+ReleasesApprovalTest:test_setReleasesApprovalForAll_RevertIf_setting_approval_with_unregistered_releases_contract() (gas: 194695)
+ReleasesApprovalTest:test_setReleasesApprovalForAll_emits_event() (gas: 223173)
+ReleasesApprovalTest:test_setReleasesApproval_RevertIf_setting_approval_not_artist() (gas: 200150)
+ReleasesApprovalTest:test_setReleasesApproval_RevertIf_setting_approval_with_unregistered_releases_contract() (gas: 198018)
+ReleasesApprovalTest:test_setReleasesApproval_emits_event() (gas: 227824)
+ReleasesFactoryTest:test_constructor() (gas: 14921)
+ReleasesFactoryTest:test_create() (gas: 323542)
+ReleasesTest:test_create_RevertIf_caller_is_not_release_admin() (gas: 609395)
+ReleasesTest:test_create_RevertIf_royalty_amount_is_over_2000() (gas: 607354)
+ReleasesTest:test_create_emits_event() (gas: 1063670)
+ReleasesTest:test_create_release_curated() (gas: 1088448)
+ReleasesTest:test_initialize() (gas: 26153)
+ReleasesTest:test_initialize_RevertIf_already_initialized() (gas: 30475)
+ReleasesTest:test_royaltyInfo() (gas: 1067164)
+ReleasesTest:test_setUri() (gas: 1073351)
+ReleasesTest:test_setUri_RevertIf_tokenId_is_invalid() (gas: 1067464)
+ReleasesTest:test_setUri_emits_event() (gas: 1072239)
+ReleasesTest:test_supportsInterface() (gas: 8555)
+ReleasesTest:test_withdrawRelease() (gas: 1076129)
+ReleasesTest:test_withdrawRelease_RevertIf_tokenId_is_invalid() (gas: 1066600)
+ReleasesTest:test_withdrawRelease_emits_event() (gas: 1076856)
+SetTrackDataTest:test_setTrackBeneficiary() (gas: 212827)
+SetTrackDataTest:test_setTrackBeneficiary_RevertIf_not_track_owner() (gas: 200770)
+SetTrackDataTest:test_setTrackBeneficiary_RevertIf_track_not_registered() (gas: 24712)
+SetTrackDataTest:test_setTrackBeneficiary_emits_event() (gas: 209071)
+SetTrackDataTest:test_setTrackFingerprintHash() (gas: 235072)
+SetTrackDataTest:test_setTrackFingerprintHash_RevertIf_not_track_owner() (gas: 201252)
+SetTrackDataTest:test_setTrackFingerprintHash_RevertIf_track_not_registered() (gas: 25151)
+SetTrackDataTest:test_setTrackFingerprintHash_emits_event() (gas: 227870)
+SetTrackDataTest:test_setTrackMetadata() (gas: 214610)
+SetTrackDataTest:test_setTrackMetadata_RevertIf_not_track_owner() (gas: 201210)
+SetTrackDataTest:test_setTrackMetadata_RevertIf_track_not_registered() (gas: 25092)
+SetTrackDataTest:test_setTrackMetadata_emits_event() (gas: 206940)
+SetTrackDataTest:test_setTrackStatus() (gas: 264320)
+SetTrackDataTest:test_setTrackStatus_RevertIf_no_verifier_role() (gas: 195144)
+SetTrackDataTest:test_setTrackStatus_RevertIf_track_not_registered() (gas: 56876)
+SetTrackDataTest:test_setTrackStatus_emits_event() (gas: 260516)
+SetTrackDataTest:test_setTrackValidationHash() (gas: 235202)
+SetTrackDataTest:test_setTrackValidationHash_RevertIf_not_track_owner() (gas: 201274)
+SetTrackDataTest:test_setTrackValidationHash_RevertIf_track_not_registered() (gas: 25260)
+SetTrackDataTest:test_setTrackValidationHash_emits_event() (gas: 227936)
+SplitsFactoryTest:test_PERCENTAGE_SCALE() (gas: 5489)
+SplitsFactoryTest:test_constructor_sets_sender_as_owner() (gas: 18025)
+SplitsFactoryTest:test_create_adds_all_beneficiaries() (gas: 344101)
+SplitsFactoryTest:test_create_adds_dust_allocations_to_treasury() (gas: 744589)
+SplitsFactoryTest:test_create_emits_SplitCreated() (gas: 342899)
+SplitsFactoryTest:test_create_gives_more_shares_to_duplicate_accounts() (gas: 396780)
+SplitsFactoryTest:test_create_returns_the_split_address() (gas: 335946)
+SplitsFactoryTest:test_create_sets_one_percent_distributor_fee() (gas: 335920)
+SplitsFactoryTest:test_create_sorts_unordered_accounts() (gas: 402547)
+SplitsFactoryWithForkTest:test_distributeETH_from_SplitMain() (gas: 457027)
+SplitsFactoryWithForkTest:test_withdraw_from_splitMain() (gas: 591375)
\ No newline at end of file
diff --git a/moda-contracts/.github/workflows/test.yml b/contracts/.github/workflows/test.yml
similarity index 100%
rename from moda-contracts/.github/workflows/test.yml
rename to contracts/.github/workflows/test.yml
diff --git a/moda-contracts/.gitignore b/contracts/.gitignore
similarity index 100%
rename from moda-contracts/.gitignore
rename to contracts/.gitignore
diff --git a/moda-contracts/.gitkeep b/contracts/.gitkeep
similarity index 100%
rename from moda-contracts/.gitkeep
rename to contracts/.gitkeep
diff --git a/moda-contracts/README.md b/contracts/README.md
similarity index 100%
rename from moda-contracts/README.md
rename to contracts/README.md
diff --git a/moda-contracts/foundry.toml b/contracts/foundry.toml
similarity index 92%
rename from moda-contracts/foundry.toml
rename to contracts/foundry.toml
index b78cbf09..d89cc578 100644
--- a/moda-contracts/foundry.toml
+++ b/contracts/foundry.toml
@@ -1,4 +1,6 @@
[profile.default]
+ffi = true
+ast = true
src = "src"
out = "out"
libs = ["lib"]
@@ -10,10 +12,12 @@ remappings = [
'@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/'
]
+
fs_permissions = [{ access = "read-write", path = "./"}]
[rpc_endpoints]
-rpcUrl = "${EVM_RPC_URL}"
+rpcUrl = "${TESTNET_RPC_URL}"
+
[etherscan]
mumbai = { key = "${ETHERSCAN_API_KEY}" }
diff --git a/contracts/lib/forge-std b/contracts/lib/forge-std
new file mode 160000
index 00000000..bb4ceea9
--- /dev/null
+++ b/contracts/lib/forge-std
@@ -0,0 +1 @@
+Subproject commit bb4ceea94d6f10eeb5b41dc2391c6c8bf8e734ef
diff --git a/contracts/lib/openzeppelin-contracts-upgradeable b/contracts/lib/openzeppelin-contracts-upgradeable
new file mode 160000
index 00000000..723f8cab
--- /dev/null
+++ b/contracts/lib/openzeppelin-contracts-upgradeable
@@ -0,0 +1 @@
+Subproject commit 723f8cab09cdae1aca9ec9cc1cfa040c2d4b06c1
diff --git a/contracts/lib/openzeppelin-foundry-upgrades b/contracts/lib/openzeppelin-foundry-upgrades
new file mode 160000
index 00000000..35958936
--- /dev/null
+++ b/contracts/lib/openzeppelin-foundry-upgrades
@@ -0,0 +1 @@
+Subproject commit 359589365aeba6cf41d39bae69867446b194e582
diff --git a/contracts/package-lock.json b/contracts/package-lock.json
new file mode 100644
index 00000000..26b43900
--- /dev/null
+++ b/contracts/package-lock.json
@@ -0,0 +1,1557 @@
+{
+ "name": "contracts",
+ "version": "1.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "contracts",
+ "version": "1.0.0",
+ "license": "ISC",
+ "dependencies": {
+ "@openzeppelin/upgrades-core": "^1.33.1"
+ }
+ },
+ "node_modules/@openzeppelin/upgrades-core": {
+ "version": "1.33.1",
+ "resolved": "https://registry.npmjs.org/@openzeppelin/upgrades-core/-/upgrades-core-1.33.1.tgz",
+ "integrity": "sha512-YRxIRhTY1b+j7+NUUu8Uuem5ugxKexEMVd8dBRWNgWeoN1gS1OCrhgUg0ytL+54vzQ+SGWZDfNnzjVuI1Cj1Zw==",
+ "dependencies": {
+ "cbor": "^9.0.0",
+ "chalk": "^4.1.0",
+ "compare-versions": "^6.0.0",
+ "debug": "^4.1.1",
+ "ethereumjs-util": "^7.0.3",
+ "minimist": "^1.2.7",
+ "proper-lockfile": "^4.1.1",
+ "solidity-ast": "^0.4.51"
+ },
+ "bin": {
+ "openzeppelin-upgrades-core": "dist/cli/cli.js"
+ }
+ },
+ "node_modules/@types/bn.js": {
+ "version": "5.1.5",
+ "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.5.tgz",
+ "integrity": "sha512-V46N0zwKRF5Q00AZ6hWtN0T8gGmDUaUzLWQvHFo5yThtVwK/VCenFY3wXVbOvNfajEpsTfQM4IN9k/d6gUVX3A==",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/node": {
+ "version": "20.12.12",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.12.tgz",
+ "integrity": "sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==",
+ "dependencies": {
+ "undici-types": "~5.26.4"
+ }
+ },
+ "node_modules/@types/pbkdf2": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@types/pbkdf2/-/pbkdf2-3.1.2.tgz",
+ "integrity": "sha512-uRwJqmiXmh9++aSu1VNEn3iIxWOhd8AHXNSdlaLfdAAdSTY9jYVeGWnzejM3dvrkbqE3/hyQkQQ29IFATEGlew==",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/secp256k1": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/@types/secp256k1/-/secp256k1-4.0.6.tgz",
+ "integrity": "sha512-hHxJU6PAEUn0TP4S/ZOzuTUvJWuZ6eIKeNKb5RBpODvSl6hp1Wrw4s7ATY50rklRCScUDpHzVA/DQdSjJ3UoYQ==",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/array-buffer-byte-length": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz",
+ "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==",
+ "dependencies": {
+ "call-bind": "^1.0.5",
+ "is-array-buffer": "^3.0.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.findlast": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz",
+ "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0",
+ "es-shim-unscopables": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/arraybuffer.prototype.slice": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz",
+ "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==",
+ "dependencies": {
+ "array-buffer-byte-length": "^1.0.1",
+ "call-bind": "^1.0.5",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.22.3",
+ "es-errors": "^1.2.1",
+ "get-intrinsic": "^1.2.3",
+ "is-array-buffer": "^3.0.4",
+ "is-shared-array-buffer": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/available-typed-arrays": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
+ "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
+ "dependencies": {
+ "possible-typed-array-names": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/base-x": {
+ "version": "3.0.9",
+ "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz",
+ "integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==",
+ "dependencies": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/blakejs": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.2.1.tgz",
+ "integrity": "sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ=="
+ },
+ "node_modules/bn.js": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz",
+ "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ=="
+ },
+ "node_modules/brorand": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
+ "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w=="
+ },
+ "node_modules/browserify-aes": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
+ "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==",
+ "dependencies": {
+ "buffer-xor": "^1.0.3",
+ "cipher-base": "^1.0.0",
+ "create-hash": "^1.1.0",
+ "evp_bytestokey": "^1.0.3",
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/bs58": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz",
+ "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==",
+ "dependencies": {
+ "base-x": "^3.0.2"
+ }
+ },
+ "node_modules/bs58check": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz",
+ "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==",
+ "dependencies": {
+ "bs58": "^4.0.0",
+ "create-hash": "^1.1.0",
+ "safe-buffer": "^5.1.2"
+ }
+ },
+ "node_modules/buffer-xor": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz",
+ "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ=="
+ },
+ "node_modules/call-bind": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
+ "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
+ "dependencies": {
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.4",
+ "set-function-length": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/cbor": {
+ "version": "9.0.2",
+ "resolved": "https://registry.npmjs.org/cbor/-/cbor-9.0.2.tgz",
+ "integrity": "sha512-JPypkxsB10s9QOWwa6zwPzqE1Md3vqpPc+cai4sAecuCsRyAtAl/pMyhPlMbT/xtPnm2dznJZYRLui57qiRhaQ==",
+ "dependencies": {
+ "nofilter": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/cipher-base": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
+ "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==",
+ "dependencies": {
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ },
+ "node_modules/compare-versions": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-6.1.0.tgz",
+ "integrity": "sha512-LNZQXhqUvqUTotpZ00qLSaify3b4VFD588aRr8MKFw4CMUr98ytzCW5wDH5qx/DEY5kCDXcbcRuCqL0szEf2tg=="
+ },
+ "node_modules/create-hash": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
+ "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==",
+ "dependencies": {
+ "cipher-base": "^1.0.1",
+ "inherits": "^2.0.1",
+ "md5.js": "^1.3.4",
+ "ripemd160": "^2.0.1",
+ "sha.js": "^2.4.0"
+ }
+ },
+ "node_modules/create-hmac": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
+ "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==",
+ "dependencies": {
+ "cipher-base": "^1.0.3",
+ "create-hash": "^1.1.0",
+ "inherits": "^2.0.1",
+ "ripemd160": "^2.0.0",
+ "safe-buffer": "^5.0.1",
+ "sha.js": "^2.4.8"
+ }
+ },
+ "node_modules/data-view-buffer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz",
+ "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==",
+ "dependencies": {
+ "call-bind": "^1.0.6",
+ "es-errors": "^1.3.0",
+ "is-data-view": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/data-view-byte-length": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz",
+ "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "es-errors": "^1.3.0",
+ "is-data-view": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/data-view-byte-offset": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz",
+ "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==",
+ "dependencies": {
+ "call-bind": "^1.0.6",
+ "es-errors": "^1.3.0",
+ "is-data-view": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/define-data-property": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
+ "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
+ "dependencies": {
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/define-properties": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz",
+ "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
+ "dependencies": {
+ "define-data-property": "^1.0.1",
+ "has-property-descriptors": "^1.0.0",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/elliptic": {
+ "version": "6.5.5",
+ "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.5.tgz",
+ "integrity": "sha512-7EjbcmUm17NQFu4Pmgmq2olYMj8nwMnpcddByChSUjArp8F5DQWcIcpriwO4ZToLNAJig0yiyjswfyGNje/ixw==",
+ "dependencies": {
+ "bn.js": "^4.11.9",
+ "brorand": "^1.1.0",
+ "hash.js": "^1.0.0",
+ "hmac-drbg": "^1.0.1",
+ "inherits": "^2.0.4",
+ "minimalistic-assert": "^1.0.1",
+ "minimalistic-crypto-utils": "^1.0.1"
+ }
+ },
+ "node_modules/elliptic/node_modules/bn.js": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
+ "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA=="
+ },
+ "node_modules/es-abstract": {
+ "version": "1.23.3",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz",
+ "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==",
+ "dependencies": {
+ "array-buffer-byte-length": "^1.0.1",
+ "arraybuffer.prototype.slice": "^1.0.3",
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.7",
+ "data-view-buffer": "^1.0.1",
+ "data-view-byte-length": "^1.0.1",
+ "data-view-byte-offset": "^1.0.0",
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0",
+ "es-set-tostringtag": "^2.0.3",
+ "es-to-primitive": "^1.2.1",
+ "function.prototype.name": "^1.1.6",
+ "get-intrinsic": "^1.2.4",
+ "get-symbol-description": "^1.0.2",
+ "globalthis": "^1.0.3",
+ "gopd": "^1.0.1",
+ "has-property-descriptors": "^1.0.2",
+ "has-proto": "^1.0.3",
+ "has-symbols": "^1.0.3",
+ "hasown": "^2.0.2",
+ "internal-slot": "^1.0.7",
+ "is-array-buffer": "^3.0.4",
+ "is-callable": "^1.2.7",
+ "is-data-view": "^1.0.1",
+ "is-negative-zero": "^2.0.3",
+ "is-regex": "^1.1.4",
+ "is-shared-array-buffer": "^1.0.3",
+ "is-string": "^1.0.7",
+ "is-typed-array": "^1.1.13",
+ "is-weakref": "^1.0.2",
+ "object-inspect": "^1.13.1",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.5",
+ "regexp.prototype.flags": "^1.5.2",
+ "safe-array-concat": "^1.1.2",
+ "safe-regex-test": "^1.0.3",
+ "string.prototype.trim": "^1.2.9",
+ "string.prototype.trimend": "^1.0.8",
+ "string.prototype.trimstart": "^1.0.8",
+ "typed-array-buffer": "^1.0.2",
+ "typed-array-byte-length": "^1.0.1",
+ "typed-array-byte-offset": "^1.0.2",
+ "typed-array-length": "^1.0.6",
+ "unbox-primitive": "^1.0.2",
+ "which-typed-array": "^1.1.15"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
+ "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
+ "dependencies": {
+ "get-intrinsic": "^1.2.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-object-atoms": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz",
+ "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-set-tostringtag": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz",
+ "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==",
+ "dependencies": {
+ "get-intrinsic": "^1.2.4",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-shim-unscopables": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz",
+ "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==",
+ "dependencies": {
+ "hasown": "^2.0.0"
+ }
+ },
+ "node_modules/es-to-primitive": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
+ "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
+ "dependencies": {
+ "is-callable": "^1.1.4",
+ "is-date-object": "^1.0.1",
+ "is-symbol": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/ethereum-cryptography": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz",
+ "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==",
+ "dependencies": {
+ "@types/pbkdf2": "^3.0.0",
+ "@types/secp256k1": "^4.0.1",
+ "blakejs": "^1.1.0",
+ "browserify-aes": "^1.2.0",
+ "bs58check": "^2.1.2",
+ "create-hash": "^1.2.0",
+ "create-hmac": "^1.1.7",
+ "hash.js": "^1.1.7",
+ "keccak": "^3.0.0",
+ "pbkdf2": "^3.0.17",
+ "randombytes": "^2.1.0",
+ "safe-buffer": "^5.1.2",
+ "scrypt-js": "^3.0.0",
+ "secp256k1": "^4.0.1",
+ "setimmediate": "^1.0.5"
+ }
+ },
+ "node_modules/ethereumjs-util": {
+ "version": "7.1.5",
+ "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz",
+ "integrity": "sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg==",
+ "dependencies": {
+ "@types/bn.js": "^5.1.0",
+ "bn.js": "^5.1.2",
+ "create-hash": "^1.1.2",
+ "ethereum-cryptography": "^0.1.3",
+ "rlp": "^2.2.4"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/evp_bytestokey": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz",
+ "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==",
+ "dependencies": {
+ "md5.js": "^1.3.4",
+ "safe-buffer": "^5.1.1"
+ }
+ },
+ "node_modules/for-each": {
+ "version": "0.3.3",
+ "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
+ "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==",
+ "dependencies": {
+ "is-callable": "^1.1.3"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/function.prototype.name": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz",
+ "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==",
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.2.0",
+ "es-abstract": "^1.22.1",
+ "functions-have-names": "^1.2.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/functions-have-names": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
+ "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
+ "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "has-proto": "^1.0.1",
+ "has-symbols": "^1.0.3",
+ "hasown": "^2.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-symbol-description": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz",
+ "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==",
+ "dependencies": {
+ "call-bind": "^1.0.5",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/globalthis": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz",
+ "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==",
+ "dependencies": {
+ "define-properties": "^1.2.1",
+ "gopd": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/gopd": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
+ "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
+ "dependencies": {
+ "get-intrinsic": "^1.1.3"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="
+ },
+ "node_modules/has-bigints": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz",
+ "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/has-property-descriptors": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
+ "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
+ "dependencies": {
+ "es-define-property": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-proto": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz",
+ "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
+ "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-tostringtag": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+ "dependencies": {
+ "has-symbols": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hash-base": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz",
+ "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==",
+ "dependencies": {
+ "inherits": "^2.0.4",
+ "readable-stream": "^3.6.0",
+ "safe-buffer": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/hash.js": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
+ "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "minimalistic-assert": "^1.0.1"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/hmac-drbg": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
+ "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==",
+ "dependencies": {
+ "hash.js": "^1.0.3",
+ "minimalistic-assert": "^1.0.0",
+ "minimalistic-crypto-utils": "^1.0.1"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+ },
+ "node_modules/internal-slot": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz",
+ "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "hasown": "^2.0.0",
+ "side-channel": "^1.0.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/is-array-buffer": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz",
+ "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==",
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "get-intrinsic": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-bigint": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz",
+ "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==",
+ "dependencies": {
+ "has-bigints": "^1.0.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-boolean-object": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz",
+ "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==",
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-callable": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
+ "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-data-view": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz",
+ "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==",
+ "dependencies": {
+ "is-typed-array": "^1.1.13"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-date-object": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz",
+ "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==",
+ "dependencies": {
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-negative-zero": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz",
+ "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-number-object": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz",
+ "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==",
+ "dependencies": {
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-regex": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz",
+ "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==",
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-shared-array-buffer": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz",
+ "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==",
+ "dependencies": {
+ "call-bind": "^1.0.7"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-string": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz",
+ "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==",
+ "dependencies": {
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-symbol": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz",
+ "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==",
+ "dependencies": {
+ "has-symbols": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-typed-array": {
+ "version": "1.1.13",
+ "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz",
+ "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==",
+ "dependencies": {
+ "which-typed-array": "^1.1.14"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-weakref": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz",
+ "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==",
+ "dependencies": {
+ "call-bind": "^1.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/isarray": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
+ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="
+ },
+ "node_modules/keccak": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.4.tgz",
+ "integrity": "sha512-3vKuW0jV8J3XNTzvfyicFR5qvxrSAGl7KIhvgOu5cmWwM7tZRj3fMbj/pfIf4be7aznbc+prBWGjywox/g2Y6Q==",
+ "hasInstallScript": true,
+ "dependencies": {
+ "node-addon-api": "^2.0.0",
+ "node-gyp-build": "^4.2.0",
+ "readable-stream": "^3.6.0"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/md5.js": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
+ "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==",
+ "dependencies": {
+ "hash-base": "^3.0.0",
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.1.2"
+ }
+ },
+ "node_modules/minimalistic-assert": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
+ "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="
+ },
+ "node_modules/minimalistic-crypto-utils": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
+ "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg=="
+ },
+ "node_modules/minimist": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+ },
+ "node_modules/node-addon-api": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz",
+ "integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA=="
+ },
+ "node_modules/node-gyp-build": {
+ "version": "4.8.1",
+ "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.1.tgz",
+ "integrity": "sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw==",
+ "bin": {
+ "node-gyp-build": "bin.js",
+ "node-gyp-build-optional": "optional.js",
+ "node-gyp-build-test": "build-test.js"
+ }
+ },
+ "node_modules/nofilter": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/nofilter/-/nofilter-3.1.0.tgz",
+ "integrity": "sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g==",
+ "engines": {
+ "node": ">=12.19"
+ }
+ },
+ "node_modules/object-inspect": {
+ "version": "1.13.1",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz",
+ "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object-keys": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.assign": {
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz",
+ "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==",
+ "dependencies": {
+ "call-bind": "^1.0.5",
+ "define-properties": "^1.2.1",
+ "has-symbols": "^1.0.3",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/pbkdf2": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz",
+ "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==",
+ "dependencies": {
+ "create-hash": "^1.1.2",
+ "create-hmac": "^1.1.4",
+ "ripemd160": "^2.0.1",
+ "safe-buffer": "^5.0.1",
+ "sha.js": "^2.4.8"
+ },
+ "engines": {
+ "node": ">=0.12"
+ }
+ },
+ "node_modules/possible-typed-array-names": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz",
+ "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/proper-lockfile": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz",
+ "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==",
+ "dependencies": {
+ "graceful-fs": "^4.2.4",
+ "retry": "^0.12.0",
+ "signal-exit": "^3.0.2"
+ }
+ },
+ "node_modules/randombytes": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
+ "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
+ "dependencies": {
+ "safe-buffer": "^5.1.0"
+ }
+ },
+ "node_modules/readable-stream": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/regexp.prototype.flags": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz",
+ "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==",
+ "dependencies": {
+ "call-bind": "^1.0.6",
+ "define-properties": "^1.2.1",
+ "es-errors": "^1.3.0",
+ "set-function-name": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/retry": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
+ "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/ripemd160": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz",
+ "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==",
+ "dependencies": {
+ "hash-base": "^3.0.0",
+ "inherits": "^2.0.1"
+ }
+ },
+ "node_modules/rlp": {
+ "version": "2.2.7",
+ "resolved": "https://registry.npmjs.org/rlp/-/rlp-2.2.7.tgz",
+ "integrity": "sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ==",
+ "dependencies": {
+ "bn.js": "^5.2.0"
+ },
+ "bin": {
+ "rlp": "bin/rlp"
+ }
+ },
+ "node_modules/safe-array-concat": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz",
+ "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "get-intrinsic": "^1.2.4",
+ "has-symbols": "^1.0.3",
+ "isarray": "^2.0.5"
+ },
+ "engines": {
+ "node": ">=0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/safe-regex-test": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz",
+ "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==",
+ "dependencies": {
+ "call-bind": "^1.0.6",
+ "es-errors": "^1.3.0",
+ "is-regex": "^1.1.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/scrypt-js": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz",
+ "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA=="
+ },
+ "node_modules/secp256k1": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.3.tgz",
+ "integrity": "sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA==",
+ "hasInstallScript": true,
+ "dependencies": {
+ "elliptic": "^6.5.4",
+ "node-addon-api": "^2.0.0",
+ "node-gyp-build": "^4.2.0"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/set-function-length": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
+ "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.4",
+ "gopd": "^1.0.1",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/set-function-name": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz",
+ "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==",
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-errors": "^1.3.0",
+ "functions-have-names": "^1.2.3",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/setimmediate": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
+ "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA=="
+ },
+ "node_modules/sha.js": {
+ "version": "2.4.11",
+ "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
+ "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
+ "dependencies": {
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ },
+ "bin": {
+ "sha.js": "bin.js"
+ }
+ },
+ "node_modules/side-channel": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz",
+ "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.4",
+ "object-inspect": "^1.13.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/signal-exit": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
+ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="
+ },
+ "node_modules/solidity-ast": {
+ "version": "0.4.56",
+ "resolved": "https://registry.npmjs.org/solidity-ast/-/solidity-ast-0.4.56.tgz",
+ "integrity": "sha512-HgmsA/Gfklm/M8GFbCX/J1qkVH0spXHgALCNZ8fA8x5X+MFdn/8CP2gr5OVyXjXw6RZTPC/Sxl2RUDQOXyNMeA==",
+ "dependencies": {
+ "array.prototype.findlast": "^1.2.2"
+ }
+ },
+ "node_modules/string_decoder": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+ "dependencies": {
+ "safe-buffer": "~5.2.0"
+ }
+ },
+ "node_modules/string.prototype.trim": {
+ "version": "1.2.9",
+ "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz",
+ "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.0",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/string.prototype.trimend": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz",
+ "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/string.prototype.trimstart": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz",
+ "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/typed-array-buffer": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz",
+ "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "es-errors": "^1.3.0",
+ "is-typed-array": "^1.1.13"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/typed-array-byte-length": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz",
+ "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "for-each": "^0.3.3",
+ "gopd": "^1.0.1",
+ "has-proto": "^1.0.3",
+ "is-typed-array": "^1.1.13"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/typed-array-byte-offset": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz",
+ "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==",
+ "dependencies": {
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.7",
+ "for-each": "^0.3.3",
+ "gopd": "^1.0.1",
+ "has-proto": "^1.0.3",
+ "is-typed-array": "^1.1.13"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/typed-array-length": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz",
+ "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "for-each": "^0.3.3",
+ "gopd": "^1.0.1",
+ "has-proto": "^1.0.3",
+ "is-typed-array": "^1.1.13",
+ "possible-typed-array-names": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/unbox-primitive": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz",
+ "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==",
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "has-bigints": "^1.0.2",
+ "has-symbols": "^1.0.3",
+ "which-boxed-primitive": "^1.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "5.26.5",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
+ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
+ },
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
+ },
+ "node_modules/which-boxed-primitive": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz",
+ "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==",
+ "dependencies": {
+ "is-bigint": "^1.0.1",
+ "is-boolean-object": "^1.1.0",
+ "is-number-object": "^1.0.4",
+ "is-string": "^1.0.5",
+ "is-symbol": "^1.0.3"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/which-typed-array": {
+ "version": "1.1.15",
+ "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz",
+ "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==",
+ "dependencies": {
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.7",
+ "for-each": "^0.3.3",
+ "gopd": "^1.0.1",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ }
+ }
+}
diff --git a/contracts/package.json b/contracts/package.json
new file mode 100644
index 00000000..ffe832b7
--- /dev/null
+++ b/contracts/package.json
@@ -0,0 +1,27 @@
+{
+ "name": "contracts",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.js",
+ "scripts": {
+ "test": "forge clean && forge test -vv",
+ "coverage": "forge coverage --report lcov --ffi && genhtml lcov.info --branch-coverage --output-dir coverage ",
+ "size": "forge build --sizes",
+ "contracts:docs": "forge doc -s -p 8080",
+ "contracts:install": "forge install",
+ "_deploy_Profile": "forge script script/DeployProfile.s.sol:DeployProfile --rpc-url rpcUrl --broadcast --verify -vvvv",
+ "1_deploy_Membership": "forge script script/DeployMembership.s.sol:DeployMembership --rpc-url rpcUrl --broadcast --verify -vvvv",
+ "2_deploy_Registry": "forge clean && forge script script/DeployRegistry.s.sol:DeployRegistry --rpc-url rpcUrl --broadcast --verify -vvvv",
+ "3_deploy_SplitsFactory": "forge script script/DeploySplitsFactory.s.sol:DeploySplitsFactory --rpc-url rpcUrl --broadcast --verify -vvvv",
+ "4_deploy_ReleasesFactory": "forge script script/DeployReleasesFactory.s.sol:DeployReleasesFactory --rpc-url rpcUrl --broadcast --verify -vvvv --ffi",
+ "5_deploy_OpenReleasesFactory": "forge script script/DeployOpenReleasesFactory.s.sol:DeployOpenReleasesFactory --rpc-url rpcUrl --broadcast --verify -vvvv --ffi",
+ "6_deploy_Marketplace": "forge script script/DeployMarketplace.s.sol:DeployMarketplace --rpc-url rpcUrl --broadcast --verify -vvvv",
+ "add_verified_roles": "node script/utils/add_verified_role/addAddressesToVerify.js"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "ISC",
+ "dependencies": {
+ "@openzeppelin/upgrades-core": "^1.33.1"
+ }
+}
diff --git a/moda-contracts/script/AddVerifiedRoles.s.sol b/contracts/script/AddVerifiedRoles.s.sol
similarity index 87%
rename from moda-contracts/script/AddVerifiedRoles.s.sol
rename to contracts/script/AddVerifiedRoles.s.sol
index 8348af37..9fcab8a5 100644
--- a/moda-contracts/script/AddVerifiedRoles.s.sol
+++ b/contracts/script/AddVerifiedRoles.s.sol
@@ -9,7 +9,7 @@ import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
contract AddVerifiedRoles is Script {
function run() public {
uint256 privateKey = vm.envUint("DEPLOYER_PRIVATE_KEY");
- address catalog = vm.envAddress("REGISTRY");
+ address registry = vm.envAddress("REGISTRY_ADDRESS");
vm.startBroadcast(privateKey);
@@ -21,7 +21,7 @@ contract AddVerifiedRoles is Script {
string(abi.encodePacked("$.addresses[", Strings.toString(i), "]"));
bytes memory addressBytes = vm.parseJson(content, addressPath);
address addressToVerify = abi.decode(addressBytes, (address));
- IAccessControl(catalog).grantRole(keccak256("AUTO_VERIFIED_ROLE"), addressToVerify);
+ IAccessControl(registry).grantRole(keccak256("AUTO_VERIFIED_ROLE"), addressToVerify);
console2.log("address", addressToVerify, "has been granted the AUTO_VERIFIED_ROLE");
}
}
diff --git a/contracts/script/DeployMarketplace.s.sol b/contracts/script/DeployMarketplace.s.sol
new file mode 100644
index 00000000..9f87ff0c
--- /dev/null
+++ b/contracts/script/DeployMarketplace.s.sol
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: UNLICENSED
+pragma solidity ^0.8.21;
+
+import {Script, console2} from "forge-std/Script.sol";
+import {Marketplace} from "../src/Marketplace.sol";
+import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
+import {DeployedContracts} from "./utils/DeployedContracts.sol";
+
+contract DeployMarketplace is Script {
+ function run() public {
+ uint256 privateKey = vm.envUint("DEPLOYER_PRIVATE_KEY");
+ address usdc = vm.envAddress("USDC_ADDRESS");
+ vm.startBroadcast(privateKey);
+
+ new Marketplace(IERC20(usdc));
+
+ vm.stopBroadcast();
+ }
+}
diff --git a/moda-contracts/script/DeployMembership.s.sol b/contracts/script/DeployMembership.s.sol
similarity index 100%
rename from moda-contracts/script/DeployMembership.s.sol
rename to contracts/script/DeployMembership.s.sol
diff --git a/moda-contracts/script/DeployProfile.s.sol b/contracts/script/DeployProfile.s.sol
similarity index 100%
rename from moda-contracts/script/DeployProfile.s.sol
rename to contracts/script/DeployProfile.s.sol
diff --git a/contracts/script/DeployRegistry.s.sol b/contracts/script/DeployRegistry.s.sol
new file mode 100644
index 00000000..6aa946ab
--- /dev/null
+++ b/contracts/script/DeployRegistry.s.sol
@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: UNLICENSED
+pragma solidity ^0.8.21;
+
+import {Script, console2} from "forge-std/Script.sol";
+import {DeployedContracts} from "./utils/DeployedContracts.sol";
+import "../src/Registry.sol";
+import {Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol";
+
+contract DeployRegistry is Script {
+ function run() public {
+ uint256 privateKey = vm.envUint("DEPLOYER_PRIVATE_KEY");
+ address admin = vm.addr(privateKey);
+
+ console2.log("Deploying Registry with admin:", admin);
+
+ IMembership membership =
+ IMembership(DeployedContracts.get("DeployMembership.s.sol", block.chainid));
+ ISplitsFactory splitsFactory =
+ ISplitsFactory(DeployedContracts.get("DeploySplitsFactory.s.sol", block.chainid));
+
+ vm.startBroadcast(privateKey);
+ address beacon = Upgrades.deployBeacon("Registry.sol:Registry", admin);
+
+ Registry registry = Registry(
+ Upgrades.deployBeaconProxy(
+ beacon,
+ abi.encodeCall(Registry.initialize, (admin, "DropRegistry", membership, splitsFactory))
+ )
+ );
+
+ console2.log("Registry deployed at:", address(registry));
+
+ vm.stopBroadcast();
+ }
+}
diff --git a/contracts/script/DeployReleases.s.sol b/contracts/script/DeployReleases.s.sol
new file mode 100644
index 00000000..02761635
--- /dev/null
+++ b/contracts/script/DeployReleases.s.sol
@@ -0,0 +1,28 @@
+// SPDX-License-Identifier: UNLICENSED
+pragma solidity ^0.8.21;
+
+import {Script, console2} from "forge-std/Script.sol";
+import {IReleasesFactory} from "../src/interfaces/Releases/IReleasesFactory.sol";
+import {IReleasesInitialize} from "../src/interfaces/Releases/IReleasesInitialize.sol";
+import {DeployedContracts} from "./utils/DeployedContracts.sol";
+
+contract DeployReleases is Script {
+ function run() public {
+ uint256 privateKey = vm.envUint("DEPLOYER_PRIVATE_KEY");
+ address releaseAdmin = vm.envAddress("RELEASE_ADMIN");
+ string memory releaseName = vm.envString("RELEASE_NAME");
+ string memory releaseSymbol = vm.envString("RELEASE_SYMBOL");
+ address[] memory releaseAdmins = new address[](1);
+ releaseAdmins[0] = releaseAdmin;
+
+ vm.startBroadcast(privateKey);
+
+ IReleasesFactory releasesFactory =
+ IReleasesFactory(DeployedContracts.getAt("DeployReleasesFactory.s.sol", block.chainid, 2));
+ console2.log("ReleasesFactory address", address(releasesFactory));
+
+ releasesFactory.create(releaseName, releaseSymbol);
+
+ vm.stopBroadcast();
+ }
+}
diff --git a/moda-contracts/script/DeployReleasesFactory.s.sol b/contracts/script/DeployReleasesFactory.s.sol
similarity index 63%
rename from moda-contracts/script/DeployReleasesFactory.s.sol
rename to contracts/script/DeployReleasesFactory.s.sol
index 400bf76b..f847c954 100644
--- a/moda-contracts/script/DeployReleasesFactory.s.sol
+++ b/contracts/script/DeployReleasesFactory.s.sol
@@ -1,22 +1,20 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.21;
-import {Script} from "forge-std/Script.sol";
+import {Script, console2} from "forge-std/Script.sol";
import {ReleasesFactory} from "../src/ReleasesFactory.sol";
import {Registry} from "../src/Registry.sol";
import {Releases} from "../src/Releases.sol";
import {DeployedContracts} from "./utils/DeployedContracts.sol";
+import {ISplitsFactory} from "../src/interfaces/ISplitsFactory.sol";
contract DeployReleasesFactory is Script {
function run() public {
uint256 privateKey = vm.envUint("DEPLOYER_PRIVATE_KEY");
vm.startBroadcast(privateKey);
- Registry registry = Registry(DeployedContracts.get("DeployRegistry.s.sol", block.chainid));
Releases releases = new Releases();
- ReleasesFactory factory = new ReleasesFactory(registry, address(releases));
-
- registry.grantRole(registry.RELEASES_REGISTRAR_ROLE(), address(factory));
+ ReleasesFactory factory = new ReleasesFactory(address(releases));
vm.stopBroadcast();
}
diff --git a/moda-contracts/script/DeploySplitsFactory.s.sol b/contracts/script/DeploySplitsFactory.s.sol
similarity index 65%
rename from moda-contracts/script/DeploySplitsFactory.s.sol
rename to contracts/script/DeploySplitsFactory.s.sol
index 0999c46a..2046dce4 100644
--- a/moda-contracts/script/DeploySplitsFactory.s.sol
+++ b/contracts/script/DeploySplitsFactory.s.sol
@@ -4,19 +4,20 @@ pragma solidity ^0.8.21;
import {Script, console2} from "forge-std/Script.sol";
import "../src/SplitsFactory.sol";
import "../src/interfaces/0xSplits/ISplitMain.sol";
-import "../src/interfaces/Registry/IOfficialContracts.sol";
import {DeployedContracts} from "./utils/DeployedContracts.sol";
contract DeploySplitsFactory is Script {
function run() public {
uint256 privateKey = vm.envUint("DEPLOYER_PRIVATE_KEY");
+ address treasury = vm.envAddress("TREASURY_ADDRESS");
+ uint32 treasuryFee = uint32(vm.envUint("TREASURY_FEE"));
ISplitMain splitMain = ISplitMain(vm.envAddress("SPLIT_MAIN_ADDRESS"));
vm.startBroadcast(privateKey);
- IOfficialContracts registry =
- IOfficialContracts(DeployedContracts.get("DeployRegistry.s.sol", block.chainid));
- new SplitsFactory(splitMain, registry);
+ SplitsFactory splitsFactory = new SplitsFactory(splitMain, treasury, treasuryFee);
+
+ console2.log("SplitsFactory deployed at:", address(splitsFactory));
vm.stopBroadcast();
}
diff --git a/moda-contracts/script/utils/DeployedContracts.sol b/contracts/script/utils/DeployedContracts.sol
similarity index 100%
rename from moda-contracts/script/utils/DeployedContracts.sol
rename to contracts/script/utils/DeployedContracts.sol
diff --git a/moda-contracts/script/utils/add_verified_role/addAddressesToVerify.js b/contracts/script/utils/add_verified_role/addAddressesToVerify.js
similarity index 100%
rename from moda-contracts/script/utils/add_verified_role/addAddressesToVerify.js
rename to contracts/script/utils/add_verified_role/addAddressesToVerify.js
diff --git a/contracts/src/Marketplace.sol b/contracts/src/Marketplace.sol
new file mode 100644
index 00000000..a825ae94
--- /dev/null
+++ b/contracts/src/Marketplace.sol
@@ -0,0 +1,219 @@
+// SPDX-License-Identifier: MIT
+pragma solidity 0.8.21;
+
+import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
+import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
+import "@openzeppelin/contracts/access/AccessControl.sol";
+import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
+import "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
+import "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol";
+import {IMarketplace} from "./interfaces/IMarketplace.sol";
+import {IReleases} from "./interfaces/Releases/IReleases.sol";
+
+/**
+ * @title Marketplace
+ * @dev This contract allows buying and selling of Releases and charges a fee on each sale.
+ */
+contract Marketplace is IMarketplace, ERC1155Holder, ReentrancyGuard, AccessControl {
+ using SafeERC20 for IERC20;
+
+ // State Variables
+
+ IERC20 _token;
+
+ /// @dev releaseOwner => saleId => Sale
+ mapping(address => Sale[]) private sales;
+
+ // Errors
+ error CannotBeZeroAddress();
+ error TreasuryFeeCannotBeZero();
+ error TokenAmountCannotBeZero();
+ error MaxCountCannotBeZero();
+ error StartCannotBeAfterEnd(uint256 startTime, uint256 endTime);
+ error InsufficientSupply(uint256 remainingSupply);
+ error MaxSupplyReached(uint256 maxSupplyPerWallet);
+ error SaleNotStarted(uint256 startTime);
+ error SaleHasEnded(uint256 endTime, uint256 currentTime);
+ error ReleasesIsNotRegistered();
+ error OnlySellerCanWithdraw();
+
+ /**
+ * @dev Constructor
+ * @param token - A token that implements an IERC20 interface that will be used for payments
+ */
+ constructor(IERC20 token) {
+ _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
+ if (address(token) == address(0)) revert CannotBeZeroAddress();
+ _token = token;
+ }
+
+ // External Functions
+
+ /**
+ * @inheritdoc IMarketplace
+ */
+ function createSale(
+ address releaseOwner,
+ address payable beneficiary,
+ address payable treasury,
+ uint256 treasuryFee,
+ IReleases releases,
+ uint256 tokenId,
+ uint256 amountTotal,
+ uint256 pricePerToken,
+ uint256 startAt,
+ uint256 endAt,
+ uint256 maxCountPerWallet
+ ) external {
+ if (treasuryFee == 0) revert TreasuryFeeCannotBeZero();
+ if (treasury == address(0)) revert CannotBeZeroAddress();
+ if (beneficiary == address(0)) revert CannotBeZeroAddress();
+ if (amountTotal == 0) revert TokenAmountCannotBeZero();
+ if (endAt != 0 && startAt > endAt) {
+ revert StartCannotBeAfterEnd(startAt, endAt);
+ }
+ if (maxCountPerWallet == 0) revert MaxCountCannotBeZero();
+ IERC1155(address(releases)).safeTransferFrom(
+ _msgSender(), address(this), tokenId, amountTotal, ""
+ );
+
+ sales[releaseOwner].push(
+ Sale({
+ seller: _msgSender(),
+ releaseOwner: releaseOwner,
+ beneficiary: beneficiary,
+ treasury: treasury,
+ treasuryFee: treasuryFee,
+ releases: address(releases),
+ tokenId: tokenId,
+ amountRemaining: amountTotal,
+ amountTotal: amountTotal,
+ pricePerToken: pricePerToken,
+ startAt: startAt,
+ endAt: endAt,
+ maxCountPerWallet: maxCountPerWallet
+ })
+ );
+
+ emit SaleCreated(releaseOwner, sales[releaseOwner].length - 1);
+ }
+
+ /**
+ * @inheritdoc IMarketplace
+ */
+ function purchase(
+ address releaseOwner,
+ uint256 saleId,
+ uint256 tokenAmount,
+ address recipient
+ ) external nonReentrant {
+ Sale storage sale = _getSaleForPurchase(releaseOwner, saleId, tokenAmount);
+
+ uint256 totalPrice = sale.pricePerToken * tokenAmount;
+ uint256 fee = (sale.treasuryFee * totalPrice) / 10_000;
+ _token.safeTransferFrom(_msgSender(), address(this), totalPrice);
+ _token.transfer(sale.treasury, fee);
+ _token.transfer(sale.beneficiary, totalPrice - fee);
+
+ _transferTokens(sale.releases, sale.tokenId, tokenAmount, recipient);
+
+ sale.amountRemaining -= tokenAmount;
+
+ emit Purchase(
+ sale.releases, sale.tokenId, recipient, releaseOwner, saleId, tokenAmount, block.timestamp
+ );
+ }
+
+ /**
+ * @inheritdoc IMarketplace
+ */
+ function withdraw(address releaseOwner, uint256 saleId, uint256 tokenAmount) external nonReentrant {
+ if (tokenAmount == 0) revert TokenAmountCannotBeZero();
+ Sale storage sale = sales[releaseOwner][saleId];
+ if (_msgSender() != sale.seller) revert OnlySellerCanWithdraw();
+ if (tokenAmount > sale.amountRemaining) {
+ revert InsufficientSupply(sale.amountRemaining);
+ }
+ _transferTokens(sale.releases, sale.tokenId, tokenAmount, _msgSender());
+
+ sale.amountRemaining -= tokenAmount;
+
+ emit Withdraw(_msgSender(), saleId, tokenAmount);
+ }
+
+ /**
+ * @inheritdoc IMarketplace
+ */
+ function getSale(address releaseOwner, uint256 saleId) external view returns (Sale memory) {
+ return sales[releaseOwner][saleId];
+ }
+
+ /**
+ * @inheritdoc IMarketplace
+ */
+ function saleCount(address releaseOwner) external view returns (uint256) {
+ return sales[releaseOwner].length;
+ }
+
+ // Public Functions
+
+ /**
+ * @inheritdoc ERC165
+ */
+ function supportsInterface(bytes4 interfaceId)
+ public
+ view
+ override(AccessControl, ERC1155Holder)
+ returns (bool)
+ {
+ return super.supportsInterface(interfaceId);
+ }
+
+ // Internal Functions
+
+ /**
+ * @dev Verifies the purchase process for a sale
+ * @param releaseOwner - The address of the releaseOwner
+ * @param saleId - The id of the sale
+ * @param tokenAmount - The amount of tokens to purchase
+ */
+ function _getSaleForPurchase(
+ address releaseOwner,
+ uint256 saleId,
+ uint256 tokenAmount
+ ) internal view returns (Sale storage) {
+ Sale storage sale = sales[releaseOwner][saleId];
+ if (sale.startAt > block.timestamp) {
+ revert SaleNotStarted(sale.startAt);
+ }
+ if (sale.endAt != 0 && sale.endAt < block.timestamp) {
+ revert SaleHasEnded(sale.endAt, block.timestamp);
+ }
+ if (tokenAmount == 0) revert TokenAmountCannotBeZero();
+ if (tokenAmount > sale.amountRemaining) {
+ revert InsufficientSupply(sale.amountRemaining);
+ }
+
+ uint256 buyerBalance = IERC1155(sale.releases).balanceOf(_msgSender(), sale.tokenId);
+ if ((buyerBalance + tokenAmount) > sale.maxCountPerWallet) {
+ revert MaxSupplyReached(sale.maxCountPerWallet);
+ }
+ return sale;
+ }
+
+ /**
+ * @dev Transfers Release tokens from the contract to the recipient
+ * @param releases - The address of the Releases contract
+ * @param tokenId - The id of the token
+ * @param tokenAmount - The amount of tokens to transfer
+ * @param recipient - The address that will receive the Tokens
+ */
+ function _transferTokens(
+ address releases,
+ uint256 tokenId,
+ uint256 tokenAmount,
+ address recipient
+ ) internal {
+ IERC1155(releases).safeTransferFrom(address(this), recipient, tokenId, tokenAmount, "");
+ }
+}
diff --git a/moda-contracts/src/Membership.sol b/contracts/src/Membership.sol
similarity index 100%
rename from moda-contracts/src/Membership.sol
rename to contracts/src/Membership.sol
diff --git a/moda-contracts/src/Profile.sol b/contracts/src/Profile.sol
similarity index 100%
rename from moda-contracts/src/Profile.sol
rename to contracts/src/Profile.sol
diff --git a/contracts/src/Registry.sol b/contracts/src/Registry.sol
new file mode 100644
index 00000000..c23b8900
--- /dev/null
+++ b/contracts/src/Registry.sol
@@ -0,0 +1,668 @@
+// SPDX-License-Identifier: MIT
+pragma solidity 0.8.21;
+
+import {ITrackRegistration} from "./interfaces/Registry/ITrackRegistration.sol";
+import {IArtistRegistration} from "./interfaces/Registry/IArtistRegistration.sol";
+import {IReleaseRegistration} from "./interfaces/Registry/IReleaseRegistration.sol";
+import {IRegistryInitialize} from "./interfaces/Registry/IRegistryInitialize.sol";
+import {IReleasesFactory} from "./interfaces/Releases/IReleasesFactory.sol";
+import {IMembership} from "./interfaces/IMembership.sol";
+import {IRegistry} from "./interfaces/Registry/IRegistry.sol";
+import {ISplitsFactory} from "./interfaces/ISplitsFactory.sol";
+import {AccessControlUpgradeable} from
+ "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
+import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
+import {console2} from "forge-std/Test.sol";
+
+/// @notice A Registry is a contract where users can register artists, tracks, and releases.
+/// Membership to the Registry is controlled by `IMembership`.
+contract Registry is IRegistry, AccessControlUpgradeable {
+ /// @notice only an address with a verifier role can verify a track
+ bytes32 public constant VERIFIER_ROLE = keccak256("VERIFIER_ROLE");
+
+ /// @notice only an address with a releases registrar role can register a releases contract
+ bytes32 public constant RELEASES_REGISTRAR_ROLE = keccak256("RELEASES_REGISTRAR_ROLE");
+
+ /// @custom:storage-location erc7201:moda.storage.Registry
+ struct RegistryStorage {
+ IMembership _membership;
+ ISplitsFactory _splitsFactory;
+ string _name;
+ uint256 _artistCount;
+ /// @dev artistMetadataHash => artistIndex
+ mapping(string => uint256) _artistIndex;
+ /// @dev controller address => index[]
+ mapping(address => uint256[]) _controllerArtists;
+ /// @dev artistIndex => RegisteredArtist
+ mapping(uint256 => RegisteredArtist) _registeredArtists;
+ uint256 _trackCount;
+ /// @dev trackMetadataHash => trackIndex
+ mapping(string => uint256) _trackIndex;
+ /// @dev controller address => trackIndex[]
+ mapping(address => uint256[]) _controllerTracks;
+ /// @dev trackIndex => RegisteredTrack
+ mapping(uint256 => RegisteredTrack) _registeredTracks;
+ uint256 _releaseCount;
+ /// @dev releaseMetadataHash => ReleaseIndex
+ mapping(string => uint256) _releaseIndex;
+ /// @dev releaseIndex => RegisteredRelease
+ mapping(uint256 => RegisteredRelease) _registeredReleases;
+ /// @dev releases => tokenId => tracks on release
+ mapping(address => mapping(uint256 => string[])) _releaseTracks;
+ /// @dev tokenId => metadata hash
+ mapping(uint256 => string) _tokenMetadataHash;
+ }
+
+ // Errors
+
+ error MembershipRequired();
+ error ArtistHasNotBeenVerified();
+ error ArtistHasBeenRejected();
+ error ArtistAlreadyRegistered();
+ error TrackIsNotRegistered();
+ error TrackAlreadyRegistered();
+ error TrackHasNotBeenVerified();
+ error TrackHasBeenRejected();
+ error ReleaseAlreadyCreated();
+ error VerifierRoleRequired();
+ error ReleasesRegistrarRoleRequired();
+ error CallerDoesNotHavePermission();
+ error InvalidTokenContract();
+
+ // Storage location
+
+ // keccak256(abi.encode(uint256(keccak256("drop.storage.Registry")) - 1)) & ~bytes32(uint256(0xff))
+ bytes32 private constant RegistryStorageLocation =
+ 0xa5656c55b5ba9e124c34ca40a03cbf80e2cb3c5570304c90cf082d8212e93c00;
+
+ function _getRegistryStorage() private pure returns (RegistryStorage storage $) {
+ assembly {
+ $.slot := RegistryStorageLocation
+ }
+ }
+
+ // Modifiers
+
+ modifier onlyMember() {
+ RegistryStorage storage $ = _getRegistryStorage();
+ if (!$._membership.isMember(msg.sender)) revert MembershipRequired();
+ _;
+ }
+
+ /**
+ * @notice The initializer is disabled when deployed as an implementation contract
+ * @custom:oz-upgrades-unsafe-allow constructor
+ */
+ constructor() {
+ _disableInitializers();
+ }
+
+ // External Functions
+
+ /**
+ * @inheritdoc IRegistryInitialize
+ */
+ function initialize(
+ address owner,
+ string calldata name,
+ IMembership membership,
+ ISplitsFactory splitsFactory
+ ) external initializer {
+ RegistryStorage storage $ = _getRegistryStorage();
+ _grantRole(DEFAULT_ADMIN_ROLE, owner);
+ $._name = name;
+ $._membership = membership;
+ $._splitsFactory = splitsFactory;
+ }
+
+ // Artist Registration
+
+ /// @inheritdoc IArtistRegistration
+ function registerArtist(
+ string calldata artistMetadataHash,
+ address[] calldata controllers
+ ) external onlyMember {
+ RegistryStorage storage $ = _getRegistryStorage();
+
+ if ($._artistIndex[artistMetadataHash] != 0) revert ArtistAlreadyRegistered();
+
+ $._artistCount++;
+
+ $._artistIndex[artistMetadataHash] = $._artistCount;
+ $._registeredArtists[$._artistCount].artistMetadataHash = artistMetadataHash;
+ $._registeredArtists[$._artistCount].artistStatus = ArtistStatus.PENDING;
+
+ for (uint256 i = 0; i < controllers.length; i++) {
+ $._registeredArtists[$._artistCount].controllers[controllers[i]] = true;
+ $._controllerArtists[controllers[i]].push($._artistCount);
+ }
+
+ emit ArtistRegistered(_createId("ARTIST", $._artistCount), artistMetadataHash);
+ }
+
+ /// @inheritdoc IArtistRegistration
+ function getArtistAtIndex(uint256 artistIndex)
+ external
+ view
+ returns (string memory artistMetadataHash, ArtistStatus status)
+ {
+ RegistryStorage storage $ = _getRegistryStorage();
+
+ return (
+ $._registeredArtists[artistIndex].artistMetadataHash,
+ $._registeredArtists[artistIndex].artistStatus
+ );
+ }
+
+ /// @inheritdoc IArtistRegistration
+ function getArtistId(string calldata artistMetadataHash) external view returns (string memory) {
+ RegistryStorage storage $ = _getRegistryStorage();
+ uint256 index = $._artistIndex[artistMetadataHash];
+ return _createId("ARTIST", index);
+ }
+
+ /// @inheritdoc IArtistRegistration
+ function setArtistMetadataHash(
+ uint256 artistIndex,
+ string calldata newArtistMetadataHash
+ ) external onlyMember {
+ RegistryStorage storage $ = _getRegistryStorage();
+
+ _requireCallerIsArtistController(artistIndex, msg.sender);
+
+ $._registeredArtists[artistIndex].artistMetadataHash = newArtistMetadataHash;
+
+ emit ArtistUpdated(
+ msg.sender,
+ _createId("ARTIST", artistIndex),
+ newArtistMetadataHash,
+ $._registeredArtists[artistIndex].artistStatus
+ );
+ }
+
+ /// @inheritdoc IArtistRegistration
+ function setArtistStatus(uint256 artistIndex, ArtistStatus status) external onlyMember {
+ RegistryStorage storage $ = _getRegistryStorage();
+
+ _requireVerifierRole(msg.sender);
+ $._registeredArtists[artistIndex].artistStatus = status;
+
+ emit ArtistUpdated(
+ msg.sender,
+ _createId("ARTIST", artistIndex),
+ $._registeredArtists[artistIndex].artistMetadataHash,
+ status
+ );
+ }
+
+ /// @inheritdoc IArtistRegistration
+ function setArtistController(
+ uint256 artistIndex,
+ address controller,
+ bool isController
+ ) external onlyMember {
+ _requireCallerIsArtistController(artistIndex, msg.sender);
+
+ RegistryStorage storage $ = _getRegistryStorage();
+
+ if (isController != $._registeredArtists[artistIndex].controllers[controller]) {
+ $._registeredArtists[artistIndex].controllers[controller] = isController;
+ emit ArtistControllerUpdated(_createId("ARTIST", artistIndex), controller, true);
+ }
+ }
+
+ /// @inheritdoc IArtistRegistration
+ function isArtistController(address account, uint256 artistIndex) external view returns (bool) {
+ RegistryStorage storage $ = _getRegistryStorage();
+ return $._registeredArtists[artistIndex].controllers[account];
+ }
+
+ //TODO add get artist tracks
+
+ /// @inheritdoc IArtistRegistration
+ function getArtistsForController() external view returns (uint256[] memory) {
+ RegistryStorage storage $ = _getRegistryStorage();
+ return $._controllerArtists[msg.sender];
+ }
+
+ // Track Registration
+
+ /// @inheritdoc ITrackRegistration
+ function registerTrack(
+ uint256[] memory artistIndexes,
+ string calldata trackMetadataHash,
+ address beneficiary,
+ address[] calldata controllers
+ ) external onlyMember {
+ RegistryStorage storage $ = _getRegistryStorage();
+
+ for (uint256 i = 0; i < artistIndexes.length; i++) {
+ _requireArtistVerified(artistIndexes[i]);
+ _requireCallerIsArtistController(artistIndexes[i], msg.sender);
+ }
+
+ if ($._trackIndex[trackMetadataHash] != 0) revert TrackAlreadyRegistered();
+
+ $._trackCount++;
+
+ $._trackIndex[trackMetadataHash] = $._trackCount;
+
+ $._registeredTracks[$._trackCount].artistIndexes = artistIndexes;
+ $._registeredTracks[$._trackCount].trackMetadataHash = trackMetadataHash;
+ $._registeredTracks[$._trackCount].beneficiary = beneficiary;
+ $._registeredTracks[$._trackCount].trackStatus = TrackStatus.PENDING;
+ for (uint256 i = 0; i < controllers.length; i++) {
+ $._registeredTracks[$._trackCount].controllers[controllers[i]] = true;
+ $._controllerTracks[controllers[i]].push($._trackCount);
+ }
+
+ emit TrackRegistered(
+ _createId("TRACK", $._trackCount), artistIndexes, trackMetadataHash, beneficiary, msg.sender
+ );
+ }
+
+ /// @inheritdoc ITrackRegistration
+ function getTrackAtIndex(uint256 trackIndex)
+ external
+ view
+ returns (
+ uint256[] memory artistIndexes,
+ string memory trackMetadataHash,
+ address beneficiary,
+ TrackStatus status
+ )
+ {
+ RegistryStorage storage $ = _getRegistryStorage();
+
+ return (
+ $._registeredTracks[trackIndex].artistIndexes,
+ $._registeredTracks[trackIndex].trackMetadataHash,
+ $._registeredTracks[trackIndex].beneficiary,
+ $._registeredTracks[trackIndex].trackStatus
+ );
+ }
+
+ /// @inheritdoc ITrackRegistration
+ function getTrackId(string calldata trackMetadataHash) external view returns (string memory) {
+ RegistryStorage storage $ = _getRegistryStorage();
+
+ uint256 index = $._trackIndex[trackMetadataHash];
+
+ return _createId("TRACK", index);
+ }
+
+ /// @inheritdoc ITrackRegistration
+ function setTrackBeneficiary(uint256 trackIndex, address newBeneficiary) external onlyMember {
+ RegistryStorage storage $ = _getRegistryStorage();
+
+ if (!$._registeredTracks[trackIndex].controllers[msg.sender]) {
+ revert CallerDoesNotHavePermission();
+ }
+
+ $._registeredTracks[trackIndex].beneficiary = newBeneficiary;
+
+ emit TrackUpdated(
+ msg.sender,
+ _createId("TRACK", trackIndex),
+ $._registeredTracks[trackIndex].artistIndexes,
+ $._registeredTracks[trackIndex].trackMetadataHash,
+ newBeneficiary,
+ $._registeredTracks[trackIndex].trackStatus
+ );
+ }
+
+ /// @inheritdoc ITrackRegistration
+ function setTrackMetadataHash(
+ uint256 trackIndex,
+ string calldata newTrackMetadataHash
+ ) external onlyMember {
+ RegistryStorage storage $ = _getRegistryStorage();
+
+ if (!$._registeredTracks[trackIndex].controllers[msg.sender]) {
+ revert CallerDoesNotHavePermission();
+ }
+
+ $._registeredTracks[trackIndex].trackMetadataHash = newTrackMetadataHash;
+ $._trackIndex[newTrackMetadataHash] = trackIndex;
+
+ emit TrackUpdated(
+ msg.sender,
+ _createId("TRACK", trackIndex),
+ $._registeredTracks[trackIndex].artistIndexes,
+ newTrackMetadataHash,
+ $._registeredTracks[trackIndex].beneficiary,
+ $._registeredTracks[trackIndex].trackStatus
+ );
+ }
+
+ /// @inheritdoc ITrackRegistration
+ function setTrackStatus(uint256 trackIndex, TrackStatus status) external onlyMember {
+ RegistryStorage storage $ = _getRegistryStorage();
+
+ _requireVerifierRole(msg.sender);
+
+ $._registeredTracks[trackIndex].trackStatus = status;
+
+ emit TrackUpdated(
+ msg.sender,
+ _createId("TRACK", trackIndex),
+ $._registeredTracks[trackIndex].artistIndexes,
+ $._registeredTracks[trackIndex].trackMetadataHash,
+ $._registeredTracks[trackIndex].beneficiary,
+ status
+ );
+ }
+
+ /// @inheritdoc ITrackRegistration
+ function setTrackController(
+ uint256 trackIndex,
+ address controller,
+ bool isController
+ ) external onlyMember {
+ RegistryStorage storage $ = _getRegistryStorage();
+
+ if (!$._registeredTracks[trackIndex].controllers[msg.sender]) {
+ revert CallerDoesNotHavePermission();
+ }
+
+ if (isController != $._registeredTracks[trackIndex].controllers[controller]) {
+ $._registeredTracks[trackIndex].controllers[controller] = isController;
+ emit TrackControllerUpdated(_createId("TRACK", trackIndex), controller, isController);
+ }
+ }
+
+ /// @inheritdoc ITrackRegistration
+ function isTrackController(address account, uint256 trackIndex) external view returns (bool) {
+ RegistryStorage storage $ = _getRegistryStorage();
+
+ return $._registeredTracks[trackIndex].controllers[account];
+ }
+
+ /// @inheritdoc ITrackRegistration
+ function getTracksForController() external view returns (uint256[] memory) {
+ RegistryStorage storage $ = _getRegistryStorage();
+ return $._controllerTracks[msg.sender];
+ }
+
+ /// @inheritdoc IReleaseRegistration
+ function registerRelease(
+ string calldata releaseMetadataHash,
+ uint256[] memory trackIndexes,
+ address[] calldata controllers,
+ address[] calldata allowedTokenContracts
+ ) external onlyMember {
+ RegistryStorage storage $ = _getRegistryStorage();
+
+ $._releaseCount++;
+
+ if ($._releaseIndex[releaseMetadataHash] != 0) revert ReleaseAlreadyCreated();
+
+ address[] memory beneficiaries = new address[](trackIndexes.length);
+
+ for (uint256 i = 0; i < trackIndexes.length; i++) {
+ _requireTrackIsRegistered(trackIndexes[i]);
+ _requireTrackIsVerified(trackIndexes[i]);
+
+ $._registeredReleases[$._releaseCount].releaseTracks.push(
+ ReleaseTrack({trackIndex: trackIndexes[i], accessGranted: false})
+ );
+
+ beneficiaries[i] = $._registeredTracks[trackIndexes[i]].beneficiary;
+ }
+
+ $._releaseIndex[releaseMetadataHash] = $._releaseCount;
+
+ address split = ISplitsFactory($._splitsFactory).create(beneficiaries);
+
+ $._registeredReleases[$._releaseCount].releaseMetadataHash = releaseMetadataHash;
+ $._registeredReleases[$._releaseCount].beneficiary = split;
+
+ for (uint256 i = 0; i < controllers.length; i++) {
+ $._registeredReleases[$._releaseCount].controllers[controllers[i]] = true;
+ }
+ for (uint256 i = 0; i < allowedTokenContracts.length; i++) {
+ $._registeredReleases[$._releaseCount].allowedTokenContracts[allowedTokenContracts[i]] = true;
+ }
+
+ emit ReleaseRegistered(_createId("RELEASE", $._releaseCount), trackIndexes, msg.sender);
+ }
+
+ /// @inheritdoc IReleaseRegistration
+ function unregisterRelease(uint256 releaseIndex) external onlyRole(DEFAULT_ADMIN_ROLE) {
+ RegistryStorage storage $ = _getRegistryStorage();
+
+ // TODO what should happen here with any tokens that have been minted?
+
+ delete $._registeredReleases[releaseIndex];
+ emit ReleaseUnregistered(_createId("RELEASE", releaseIndex));
+ }
+
+ /// @inheritdoc IReleaseRegistration
+ function getReleaseAtIndex(uint256 releaseIndex)
+ external
+ view
+ returns (
+ string memory releaseMetadataHash,
+ address beneficiary,
+ ReleaseTrack[] memory releaseTracks
+ )
+ {
+ RegistryStorage storage $ = _getRegistryStorage();
+
+ return (
+ $._registeredReleases[releaseIndex].releaseMetadataHash,
+ $._registeredReleases[releaseIndex].beneficiary,
+ $._registeredReleases[releaseIndex].releaseTracks
+ );
+ }
+
+ /// @inheritdoc IReleaseRegistration
+ function getReleaseId(string calldata releaseMetadataHash) external view returns (string memory) {
+ RegistryStorage storage $ = _getRegistryStorage();
+
+ uint256 index = $._releaseIndex[releaseMetadataHash];
+
+ return _createId("RELEASE", index);
+ }
+
+ /// @inheritdoc IReleaseRegistration
+ function getReleaseTokens(uint256 releaseIndex) external view returns (ReleaseToken[] memory) {
+ RegistryStorage storage $ = _getRegistryStorage();
+ return $._registeredReleases[releaseIndex].releaseTokens;
+ }
+
+ /// @inheritdoc IReleaseRegistration
+ function setReleaseMetadataHash(
+ uint256 releaseIndex,
+ string calldata newReleaseMetadataHash
+ ) external onlyMember {
+ RegistryStorage storage $ = _getRegistryStorage();
+
+ if (!$._registeredReleases[releaseIndex].controllers[msg.sender]) {
+ revert CallerDoesNotHavePermission();
+ }
+
+ $._registeredReleases[releaseIndex].releaseMetadataHash = newReleaseMetadataHash;
+ $._releaseIndex[newReleaseMetadataHash] = releaseIndex;
+
+ emit ReleaseUpdated(
+ _createId("RELEASE", releaseIndex),
+ newReleaseMetadataHash,
+ $._registeredReleases[releaseIndex].beneficiary,
+ $._registeredReleases[releaseIndex].releaseTracks,
+ $._registeredReleases[releaseIndex].releaseTokens
+ );
+ }
+
+ /// @inheritdoc IReleaseRegistration
+ function addReleaseToken(uint256 releaseIndex, address tokenAddress, uint256 tokenId) external {
+ RegistryStorage storage $ = _getRegistryStorage();
+ if (!$._registeredReleases[releaseIndex].allowedTokenContracts[tokenAddress]) {
+ revert InvalidTokenContract();
+ }
+ if (!$._registeredReleases[releaseIndex].controllers[msg.sender]) {
+ revert CallerDoesNotHavePermission();
+ }
+
+ $._registeredReleases[releaseIndex].releaseTokens.push(
+ ReleaseToken(tokenAddress, tokenId, block.chainid)
+ );
+ $._tokenMetadataHash[tokenId] = $._registeredReleases[releaseIndex].releaseMetadataHash;
+
+ emit ReleaseTokenAdded(_createId("RELEASE", releaseIndex), tokenAddress, tokenId);
+ }
+
+ /// @inheritdoc IReleaseRegistration
+ function grantTrackAccess(uint256 releaseIndex, uint256 trackIndex, bool hasAccess) external {
+ RegistryStorage storage $ = _getRegistryStorage();
+
+ if (!$._registeredTracks[trackIndex].controllers[msg.sender]) {
+ revert CallerDoesNotHavePermission();
+ }
+
+ for (uint256 i = 0; i < $._registeredReleases[releaseIndex].releaseTracks.length; i++) {
+ if ($._registeredReleases[releaseIndex].releaseTracks[i].trackIndex == trackIndex) {
+ $._registeredReleases[releaseIndex].releaseTracks[i].accessGranted = hasAccess;
+ break;
+ }
+ }
+ }
+
+ /// @inheritdoc IReleaseRegistration
+ function checkTrackAccess(
+ uint256 releaseIndex,
+ uint256 trackIndex
+ ) external view returns (bool hasAccess) {
+ RegistryStorage storage $ = _getRegistryStorage();
+
+ for (uint256 i = 0; i < $._registeredReleases[releaseIndex].releaseTracks.length; i++) {
+ if ($._registeredReleases[releaseIndex].releaseTracks[i].trackIndex == trackIndex) {
+ return $._registeredReleases[releaseIndex].releaseTracks[i].accessGranted;
+ }
+ }
+ }
+
+ /// @inheritdoc IReleaseRegistration
+ function setReleaseController(
+ uint256 releaseIndex,
+ address controller,
+ bool isController
+ ) external onlyMember {
+ RegistryStorage storage $ = _getRegistryStorage();
+
+ if (!$._registeredReleases[releaseIndex].controllers[msg.sender]) {
+ revert CallerDoesNotHavePermission();
+ }
+
+ if (isController != $._registeredReleases[releaseIndex].controllers[controller]) {
+ $._registeredReleases[releaseIndex].controllers[controller] = isController;
+ emit ReleaseControllerUpdated(_createId("RELEASE", releaseIndex), controller, isController);
+ }
+ }
+
+ /// @inheritdoc IReleaseRegistration
+ function isReleaseController(address account, uint256 releaseIndex) external view returns (bool) {
+ RegistryStorage storage $ = _getRegistryStorage();
+
+ return $._registeredReleases[releaseIndex].controllers[account];
+ }
+
+ /// @inheritdoc IReleaseRegistration
+ function setAllowedTokenContract(
+ uint256 releaseIndex,
+ address tokenContract,
+ bool isAllowed
+ ) external onlyMember {
+ RegistryStorage storage $ = _getRegistryStorage();
+
+ if (!$._registeredReleases[releaseIndex].controllers[msg.sender]) {
+ revert CallerDoesNotHavePermission();
+ }
+
+ $._registeredReleases[releaseIndex].allowedTokenContracts[tokenContract] = isAllowed;
+
+ emit AllowedTokenContractUpdated(_createId("RELEASE", releaseIndex), tokenContract, isAllowed);
+ }
+
+ /// @inheritdoc IReleaseRegistration
+ function isAllowedTokenContract(
+ address tokenContract,
+ uint256 releaseIndex
+ ) external view returns (bool) {
+ RegistryStorage storage $ = _getRegistryStorage();
+
+ return $._registeredReleases[releaseIndex].allowedTokenContracts[tokenContract];
+ }
+
+ /// @inheritdoc IReleaseRegistration
+ function getTokenMetadataHash(uint256 tokenId) external view returns (string memory) {
+ RegistryStorage storage $ = _getRegistryStorage();
+ return $._tokenMetadataHash[tokenId];
+ }
+
+ // Internal Functions
+
+ // Artist
+
+ function _requireArtistVerified(uint256 artistIndex) internal view {
+ RegistryStorage storage $ = _getRegistryStorage();
+
+ if ($._registeredArtists[artistIndex].artistStatus == ArtistStatus.PENDING) {
+ revert ArtistHasNotBeenVerified();
+ } else if ($._registeredArtists[artistIndex].artistStatus == ArtistStatus.REJECTED) {
+ revert ArtistHasBeenRejected();
+ }
+ }
+
+ function _requireCallerIsArtistController(uint256 artistIndex, address caller) internal view {
+ RegistryStorage storage $ = _getRegistryStorage();
+
+ if (!$._registeredArtists[artistIndex].controllers[caller]) {
+ revert CallerDoesNotHavePermission();
+ }
+ }
+
+ // Track
+
+ function _requireTrackIsVerified(uint256 trackIndex) internal view {
+ RegistryStorage storage $ = _getRegistryStorage();
+
+ if ($._registeredTracks[trackIndex].trackStatus == TrackStatus.PENDING) {
+ revert TrackHasNotBeenVerified();
+ } else if ($._registeredTracks[trackIndex].trackStatus == TrackStatus.REJECTED) {
+ revert TrackHasBeenRejected();
+ }
+ }
+
+ function _requireTrackIsRegistered(uint256 trackIndex) internal view {
+ RegistryStorage storage $ = _getRegistryStorage();
+
+ if ($._trackIndex[$._registeredTracks[trackIndex].trackMetadataHash] == 0) {
+ revert TrackIsNotRegistered();
+ }
+ }
+
+ // Roles
+
+ function _requireVerifierRole(address account) internal view {
+ if (!hasRole(keccak256("VERIFIER_ROLE"), account)) {
+ revert VerifierRoleRequired();
+ }
+ }
+
+ function _requireReleasesRegistrarRole(address account) internal view {
+ if (!hasRole(keccak256("RELEASES_REGISTRAR_ROLE"), account)) {
+ revert ReleasesRegistrarRoleRequired();
+ }
+ }
+
+ // Utils
+
+ function _createId(string memory prefix, uint256 count) internal view returns (string memory) {
+ return string(
+ abi.encodePacked(
+ prefix, "-DROP-", Strings.toString(block.chainid), "-", Strings.toString(count)
+ )
+ );
+ }
+}
diff --git a/contracts/src/Releases.sol b/contracts/src/Releases.sol
new file mode 100644
index 00000000..68503eb0
--- /dev/null
+++ b/contracts/src/Releases.sol
@@ -0,0 +1,132 @@
+// SPDX-License-Identifier: MIT
+pragma solidity 0.8.21;
+
+import "@openzeppelin/contracts-upgradeable/token/common/ERC2981Upgradeable.sol";
+import "@openzeppelin/contracts-upgradeable/token/ERC1155/extensions/ERC1155SupplyUpgradeable.sol";
+import "@openzeppelin/contracts-upgradeable/token/ERC1155/utils/ERC1155HolderUpgradeable.sol";
+import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
+
+import {IReleases} from "./interfaces/Releases/IReleases.sol";
+import {IReleasesInitialize} from "./interfaces/Releases/IReleasesInitialize.sol";
+import {IRegistry} from "./interfaces/Registry/IRegistry.sol";
+import {IWithdrawRelease} from "./interfaces/Releases/IWithdrawRelease.sol";
+
+/**
+ * @notice Releases is a contract to allow artists or labels to create track or multiple
+ * track tokens called a "Release".
+ */
+contract Releases is
+ IReleasesInitialize,
+ IReleases,
+ IWithdrawRelease,
+ ERC1155SupplyUpgradeable,
+ ERC1155HolderUpgradeable,
+ ERC2981Upgradeable,
+ AccessControlUpgradeable
+{
+ // State Variables
+
+ uint256 constant MAX_ROYALTY_AMOUNT = 2_000;
+
+ string public name;
+ string public symbol;
+
+ uint256 public numberOfReleases;
+
+ mapping(uint256 => string) _metadataUris;
+
+ // Errors
+ error CannotBeZeroAddress();
+ error InvalidRoyaltyAmount();
+ error FieldCannotBeEmpty(string field);
+ error InvalidTokenId();
+
+ /// @dev users with a Releases admin role can create releases with a curated contract
+ bytes32 public constant RELEASE_ADMIN_ROLE = keccak256("RELEASE_ADMIN_ROLE");
+
+ /**
+ * @dev Constructor
+ * @notice The initializer is disabled when deployed as an implementation contract
+ */
+ constructor() {
+ _disableInitializers();
+ }
+
+ // External Functions
+
+ /**
+ * @dev Initializes the contract
+ * @param name_ - The name of the Releases contract
+ * @param symbol_ - The symbol of the Releases contract
+ */
+ function initialize(
+ address admin,
+ string calldata name_,
+ string calldata symbol_
+ ) external initializer {
+ __ERC1155_init("");
+ _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
+ if (admin == address(0)) revert CannotBeZeroAddress();
+ if (bytes(name_).length == 0) revert FieldCannotBeEmpty("name");
+ if (bytes(symbol_).length == 0) revert FieldCannotBeEmpty("symbol");
+ name = name_;
+ symbol = symbol_;
+ }
+
+ /// @inheritdoc IReleases
+ function create(
+ address receiver,
+ address beneficiary,
+ uint96 royaltyAmount,
+ uint256 totalSupply,
+ string calldata metadataUri
+ ) external {
+ if (royaltyAmount > MAX_ROYALTY_AMOUNT) revert InvalidRoyaltyAmount();
+
+ numberOfReleases++;
+
+ _metadataUris[numberOfReleases] = metadataUri;
+
+ _setTokenRoyalty(numberOfReleases, beneficiary, royaltyAmount);
+ _mint(receiver, numberOfReleases, totalSupply, "");
+
+ emit ReleaseCreated(numberOfReleases);
+ }
+
+ /// @inheritdoc IWithdrawRelease
+ function withdrawRelease(address receiver, uint256 tokenId, uint256 amount) external {
+ if (tokenId > numberOfReleases) revert InvalidTokenId();
+ _safeTransferFrom(address(this), receiver, tokenId, amount, "");
+ emit ReleaseWithdrawn(msg.sender, tokenId, amount);
+ }
+
+ // Public Functions
+
+ /**
+ * @dev See {IERC1155-uri}.
+ */
+ function uri(uint256 tokenId) public view override returns (string memory) {
+ if (tokenId > numberOfReleases) revert InvalidTokenId();
+
+ return _metadataUris[tokenId];
+ }
+
+ /**
+ * @dev See {IERC165-supportsInterface}.
+ */
+ function supportsInterface(bytes4 interfaceId)
+ public
+ view
+ virtual
+ override(
+ IERC165,
+ ERC1155Upgradeable,
+ ERC2981Upgradeable,
+ AccessControlUpgradeable,
+ ERC1155HolderUpgradeable
+ )
+ returns (bool)
+ {
+ return interfaceId == type(IReleases).interfaceId || super.supportsInterface(interfaceId);
+ }
+}
diff --git a/moda-contracts/src/ReleasesFactory.sol b/contracts/src/ReleasesFactory.sol
similarity index 50%
rename from moda-contracts/src/ReleasesFactory.sol
rename to contracts/src/ReleasesFactory.sol
index 3b2fd585..1e5808a9 100644
--- a/moda-contracts/src/ReleasesFactory.sol
+++ b/contracts/src/ReleasesFactory.sol
@@ -5,43 +5,30 @@ import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol";
import {IReleasesFactory} from "./interfaces/Releases/IReleasesFactory.sol";
import {IReleasesInitialize} from "./interfaces/Releases/IReleasesInitialize.sol";
import {IRegistry} from "./interfaces/Registry/IRegistry.sol";
-import {ICatalog} from "./interfaces/Catalog/ICatalog.sol";
-import {IOfficialContracts} from "./interfaces/Registry/IOfficialContracts.sol";
import {ISplitsFactory} from "./interfaces/ISplitsFactory.sol";
/**
* @notice ReleasesFactory creates Trustless Release contracts and registers them with a Catalog.
*/
contract ReleasesFactory is IReleasesFactory {
- IOfficialContracts public registry;
address public releasesMaster;
/**
* @notice Constructor
- * @param registry_ The registry contract that implements IOfficialContracts
* @param releasesMaster_ The address of the Releases implementation contract
*/
- constructor(IOfficialContracts registry_, address releasesMaster_) {
- registry = registry_;
+ constructor(address releasesMaster_) {
releasesMaster = releasesMaster_;
}
- /**
- * @inheritdoc IReleasesFactory
- */
- function create(
- address[] memory releaseAdmins,
- string memory name,
- string memory symbol,
- ICatalog catalog
- ) external {
+ /// @inheritdoc IReleasesFactory
+ function create(string calldata name, string calldata symbol) external returns (address) {
address releasesClone = Clones.clone(releasesMaster);
- ISplitsFactory splitsFactory = registry.getSplitsFactory();
- IReleasesInitialize(releasesClone).initialize(
- msg.sender, releaseAdmins, name, symbol, catalog, ISplitsFactory(splitsFactory)
- );
- ICatalog(catalog).registerReleasesContract(releasesClone, msg.sender);
+ IReleasesInitialize(releasesClone).initialize(msg.sender, name, symbol);
+
emit ReleasesCreated(msg.sender, releasesClone, name, symbol);
+
+ return releasesClone;
}
}
diff --git a/moda-contracts/src/SplitsFactory.sol b/contracts/src/SplitsFactory.sol
similarity index 88%
rename from moda-contracts/src/SplitsFactory.sol
rename to contracts/src/SplitsFactory.sol
index 385b62e1..85b8a0f3 100644
--- a/moda-contracts/src/SplitsFactory.sol
+++ b/contracts/src/SplitsFactory.sol
@@ -3,7 +3,6 @@ pragma solidity 0.8.21;
import "./interfaces/ISplitsFactory.sol";
import {ISplitMain} from "./interfaces/0xSplits/ISplitMain.sol";
-import {IOfficialContracts} from "./interfaces/Registry/IOfficialContracts.sol";
import "./interfaces/Registry/IRegistry.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
@@ -15,19 +14,27 @@ contract SplitsFactory is ISplitsFactory, Ownable {
/// @notice 1% distributor fee (10,000 = 1%)
uint32 constant DISTRIBUTOR_FEE = 1e4;
+ /// @notice The denominator when determining the treasury fee. e.g. treasuryFee * someValue / FEE_SCALE
+ uint32 constant FEE_SCALE = 10_000;
+
ISplitMain _splitMain;
- IOfficialContracts _registry;
+ address payable public treasury;
+ uint32 public treasuryFee;
+ /// @notice The denominator when determining the treasury fee. e.g. treasuryFee * someValue / FEE_SCALE
+ uint32 _treasuryFeeScale;
event SplitCreated(address indexed sender, address indexed split);
/**
* @notice Constructor
* @param splitMain The 0xSplit SplitMain contract address.
- * @param registry The top level Registry contract.
+ * @param treasury_ The address of the treasury.
+ * @param treasuryFee_ The fee to be charged to the treasury.
*/
- constructor(ISplitMain splitMain, IOfficialContracts registry) Ownable(msg.sender) {
+ constructor(ISplitMain splitMain, address treasury_, uint32 treasuryFee_) Ownable(msg.sender) {
_splitMain = splitMain;
- _registry = registry;
+ treasury = payable(treasury_);
+ treasuryFee = treasuryFee_;
}
/// @inheritdoc ISplitsFactory
@@ -39,14 +46,12 @@ contract SplitsFactory is ISplitsFactory, Ownable {
accountsUnfiltered[i] = beneficiaries[i];
}
- (address treasury, uint32 treasuryFee, uint32 treasuryFeeScale) = _registry.getTreasuryInfo();
-
accountsUnfiltered[accountsUnfiltered.length - 1] = treasury;
accountsUnfiltered = _sortAddresses(accountsUnfiltered);
(address[] memory accounts, uint256[] memory occurrences) = _filterDuplicates(accountsUnfiltered);
// Allocate Shares
- uint32 treasuryAllocation = PERCENTAGE_SCALE * uint32(treasuryFee) / uint32(treasuryFeeScale);
+ uint32 treasuryAllocation = PERCENTAGE_SCALE * uint32(treasuryFee) / FEE_SCALE;
uint32 shareHolderAllocation =
uint32((PERCENTAGE_SCALE - treasuryAllocation) / beneficiaries.length);
diff --git a/moda-contracts/src/interfaces/0xSplits/ISplitMain.sol b/contracts/src/interfaces/0xSplits/ISplitMain.sol
similarity index 100%
rename from moda-contracts/src/interfaces/0xSplits/ISplitMain.sol
rename to contracts/src/interfaces/0xSplits/ISplitMain.sol
diff --git a/moda-contracts/src/interfaces/ERC/IERC4906.sol b/contracts/src/interfaces/ERC/IERC4906.sol
similarity index 100%
rename from moda-contracts/src/interfaces/ERC/IERC4906.sol
rename to contracts/src/interfaces/ERC/IERC4906.sol
diff --git a/contracts/src/interfaces/IMarketplace.sol b/contracts/src/interfaces/IMarketplace.sol
new file mode 100644
index 00000000..2cf91971
--- /dev/null
+++ b/contracts/src/interfaces/IMarketplace.sol
@@ -0,0 +1,129 @@
+// SPDX-License-Identifier: MIT
+pragma solidity 0.8.21;
+
+import "./Releases/IReleases.sol";
+
+/**
+ * @title IMarketplace
+ * @dev Interface for the Marketplace
+ */
+interface IMarketplace {
+ /**
+ * @dev Represents a created Sale for a release
+ * seller - The address of the seller
+ * releaseOwner - The address of the release owner/seller
+ * beneficiary - The address that will receive the funds once the sale is completed
+ * treasury - The address of the treasury, can be the same as the beneficiary if no treasury exists
+ * treasuryFee - The fee to be paid to the treasury, based on a denominator of 10,000 eg. 500 = 5%
+ * releases - The address of the Releases contract that the release belongs to
+ * TokenId - The token id of the release
+ * AmountRemaining - The amount of tokens remaining to be sold
+ * AmountTotal - The total amount of tokens to be sold
+ * PricePerToken - The price per token
+ * StartAt - The start time of the sale
+ * EndAt - The end time of the sale, set to 0 for no end time
+ * MaxCountPerWallet - The maximum amount of tokens that can be purchased per wallet
+ */
+ struct Sale {
+ address seller;
+ address releaseOwner;
+ address payable beneficiary;
+ address payable treasury;
+ uint256 treasuryFee;
+ address releases;
+ uint256 tokenId;
+ uint256 amountRemaining;
+ uint256 amountTotal;
+ uint256 pricePerToken;
+ uint256 startAt;
+ uint256 endAt;
+ uint256 maxCountPerWallet;
+ }
+
+ /**
+ * @dev Emitted when a sale is created
+ */
+ event SaleCreated(address indexed releaseOwner, uint256 indexed saleId);
+
+ /**
+ * @dev Emitted when a sale is purchased
+ */
+ event Purchase(
+ address indexed releases,
+ uint256 indexed tokenId,
+ address indexed buyer,
+ address releaseOwner,
+ uint256 saleId,
+ uint256 tokenAmount,
+ uint256 timestamp
+ );
+
+ /**
+ * @dev Emitted when a sale is withdrawn
+ */
+ event Withdraw(address indexed recipient, uint256 indexed saleId, uint256 tokenAmount);
+
+ /**
+ * @dev Create a sale for a given release. The tokens must come from a releases contract
+ * that has been registered in the Catalog and they must be in the sellers wallet.
+ * @param releaseOwner - The address of the release owner
+ * @param beneficiary - The address that will receive the funds once the sale is completed
+ * @param treasury - The address of the treasury, can be the same as the beneficiary if no treasury exists
+ * @param treasuryFee - The fee to be paid to the treasury, based on a denominator of 10,000 eg. 500 = 5%
+ * @param releases - A contract that implements the IReleases interface
+ * @param tokenId - The id of the Token
+ * @param amountTotal - The amount of tokens to sell
+ * @param pricePerToken - The price per token
+ * @param startAt - The start time of the sale
+ * @param endAt - The end time of the sale, set to 0 for no end time
+ * @param maxCountPerWallet - The maximum amount of tokens that can be purchased per wallet
+ */
+ function createSale(
+ address releaseOwner,
+ address payable beneficiary,
+ address payable treasury,
+ uint256 treasuryFee,
+ IReleases releases,
+ uint256 tokenId,
+ uint256 amountTotal,
+ uint256 pricePerToken,
+ uint256 startAt,
+ uint256 endAt,
+ uint256 maxCountPerWallet
+ ) external;
+
+ /**
+ * @dev Purchase a release. Payment is in USDC.
+ * @param releaseOwner - The address of the release owner
+ * @param saleId - The id of the sale
+ * @param tokenAmount - The amount of tokens to purchase
+ * @param recipient - The address that will receive the Tokens
+ */
+ function purchase(
+ address releaseOwner,
+ uint256 saleId,
+ uint256 tokenAmount,
+ address recipient
+ ) external;
+
+ /**
+ * @dev Withdraw a Release. Caller must own the release.
+ * @param releaseOwner - The address of the release owner
+ * @param saleId - The id of the sale
+ * @param tokenAmount - The amount of tokens to withdraw
+ */
+ function withdraw(address releaseOwner, uint256 saleId, uint256 tokenAmount) external;
+
+ /**
+ * @dev Returns a Sale
+ * @param releaseOwner - The address of the release owner
+ * @param saleId - The id of the sale
+ */
+ function getSale(address releaseOwner, uint256 saleId) external view returns (Sale memory);
+
+ /**
+ * @dev Returns the number of sales for a given release owner
+ * @param releaseOwner - The address of the release owner
+ */
+ function saleCount(address releaseOwner) external view returns (uint256);
+}
diff --git a/moda-contracts/src/interfaces/IMembership.sol b/contracts/src/interfaces/IMembership.sol
similarity index 100%
rename from moda-contracts/src/interfaces/IMembership.sol
rename to contracts/src/interfaces/IMembership.sol
diff --git a/moda-contracts/src/interfaces/ISplitsFactory.sol b/contracts/src/interfaces/ISplitsFactory.sol
similarity index 100%
rename from moda-contracts/src/interfaces/ISplitsFactory.sol
rename to contracts/src/interfaces/ISplitsFactory.sol
diff --git a/moda-contracts/src/interfaces/Profile/IProfile.sol b/contracts/src/interfaces/Profile/IProfile.sol
similarity index 100%
rename from moda-contracts/src/interfaces/Profile/IProfile.sol
rename to contracts/src/interfaces/Profile/IProfile.sol
diff --git a/moda-contracts/src/interfaces/Profile/ISimpleOwnership.sol b/contracts/src/interfaces/Profile/ISimpleOwnership.sol
similarity index 100%
rename from moda-contracts/src/interfaces/Profile/ISimpleOwnership.sol
rename to contracts/src/interfaces/Profile/ISimpleOwnership.sol
diff --git a/contracts/src/interfaces/Registry/IArtistRegistration.sol b/contracts/src/interfaces/Registry/IArtistRegistration.sol
new file mode 100644
index 00000000..ff4da1cc
--- /dev/null
+++ b/contracts/src/interfaces/Registry/IArtistRegistration.sol
@@ -0,0 +1,94 @@
+// SPDX-License-Identifier: MIT
+pragma solidity 0.8.21;
+
+interface IArtistRegistration {
+ event ArtistRegistered(string id, string artistMetadataHash);
+
+ event ArtistControllerUpdated(string artistId, address controller, bool isController);
+
+ event ArtistUpdated(
+ address indexed updatedBy, string artistId, string artistMetadataHash, ArtistStatus status
+ );
+
+ enum ArtistStatus {
+ PENDING,
+ VERIFIED,
+ REJECTED
+ }
+
+ /**
+ * @notice Represents a registered Artist
+ * @param artistMetadataHash This is the IPFS hash of the artist metadata
+ * @param controllers The authorized addresses that can update the Artist
+ * @param artistStatus The status of the artist (PENDING, VERIFIED, REJECTED)
+ */
+ struct RegisteredArtist {
+ string artistMetadataHash;
+ mapping(address => bool) controllers;
+ ArtistStatus artistStatus;
+ }
+
+ /**
+ * @notice Registers an artist using the IPFS hash of the artist metadata.
+ * @param artistMetadataHash This is the IPFS hash of the artist metadata
+ * @param controllers The authorized addresses that can update the Artist
+ */
+ function registerArtist(
+ string calldata artistMetadataHash,
+ address[] calldata controllers
+ ) external;
+
+ /**
+ * @notice Returns all registered artist data
+ * @param artistIndex The index of the artist
+ */
+ function getArtistAtIndex(uint256 artistIndex)
+ external
+ view
+ returns (string memory artistMetadataHash, ArtistStatus status);
+
+ /**
+ * @notice Returns the artist id of the artist
+ * @param artistMetadataHash The metadata hash of the artist
+ */
+ function getArtistId(string calldata artistMetadataHash) external view returns (string memory);
+
+ /**
+ * @notice Sets the artist metadata hash
+ * The account setting the metadata hash needs to be a controller for the corresponding artist Id.
+ * @param artistIndex The index of the artist
+ * @param newArtistMetadataHash The new metadata hash of the artist
+ */
+ function setArtistMetadataHash(
+ uint256 artistIndex,
+ string calldata newArtistMetadataHash
+ ) external;
+
+ /**
+ * @notice Sets the status of the artist
+ * The account setting the status needs to have a verifier role
+ * @param artistIndex The index of the artist
+ * @param newStatus The new status of the artist
+ */
+ function setArtistStatus(uint256 artistIndex, ArtistStatus newStatus) external;
+
+ /**
+ * @notice Sets an artist controller
+ * The account setting the controller needs to be a controller for the corresponding artist Id.
+ * @param artistIndex The index of the artist
+ * @param controller The address of the controller
+ * @param isController The boolean value to set or unset the controller
+ */
+ function setArtistController(uint256 artistIndex, address controller, bool isController) external;
+
+ /**
+ * @notice Returns if the caller is a controller for the artist
+ * @param account The address to check
+ * @param artistIndex The index of the artist
+ */
+ function isArtistController(address account, uint256 artistIndex) external view returns (bool);
+ /**
+ * @notice Returns all artists indexes that the caller is a controller for
+ */
+ function getArtistsForController() external view returns (uint256[] memory);
+}
diff --git a/contracts/src/interfaces/Registry/IRegistry.sol b/contracts/src/interfaces/Registry/IRegistry.sol
new file mode 100644
index 00000000..6fe502bc
--- /dev/null
+++ b/contracts/src/interfaces/Registry/IRegistry.sol
@@ -0,0 +1,15 @@
+// SPDX-License-Identifier: MIT
+pragma solidity 0.8.21;
+
+import {IArtistRegistration} from "./IArtistRegistration.sol";
+import {ITrackRegistration} from "./ITrackRegistration.sol";
+import {IReleaseRegistration} from "../Registry/IReleaseRegistration.sol";
+import {IRegistryInitialize} from "./IRegistryInitialize.sol";
+
+/// @notice A contract deployed by an organization where artists and labels can register music.
+interface IRegistry is
+ IArtistRegistration,
+ ITrackRegistration,
+ IReleaseRegistration,
+ IRegistryInitialize
+{}
diff --git a/contracts/src/interfaces/Registry/IRegistryInitialize.sol b/contracts/src/interfaces/Registry/IRegistryInitialize.sol
new file mode 100644
index 00000000..0eefb4d2
--- /dev/null
+++ b/contracts/src/interfaces/Registry/IRegistryInitialize.sol
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: MIT
+pragma solidity 0.8.21;
+
+import {IMembership} from "../IMembership.sol";
+import {IReleasesFactory} from "../Releases/IReleasesFactory.sol";
+import {ISplitsFactory} from "../ISplitsFactory.sol";
+
+interface IRegistryInitialize {
+ /**
+ * @notice Initializes the contract
+ * @param owner The account that will gain ownership.
+ * @param name The name of the Registry
+ * @param membership A custom contract to gate user access.
+ * @param splitsFactory The SplitsFactory contract
+ */
+ function initialize(
+ address owner,
+ string calldata name,
+ IMembership membership,
+ ISplitsFactory splitsFactory
+ ) external;
+}
diff --git a/contracts/src/interfaces/Registry/IReleaseRegistration.sol b/contracts/src/interfaces/Registry/IReleaseRegistration.sol
new file mode 100644
index 00000000..669bb8a0
--- /dev/null
+++ b/contracts/src/interfaces/Registry/IReleaseRegistration.sol
@@ -0,0 +1,208 @@
+// SPDX-License-Identifier: MIT
+pragma solidity 0.8.21;
+
+/**
+ * @notice This interface defines a lightweight version of the required functionality to register releases
+ * when a release is successfully created.
+ */
+interface IReleaseRegistration {
+ /**
+ * @notice Emitted when a release is registered
+ */
+ event ReleaseRegistered(string releaseId, uint256[] trackIndexes, address registrerer);
+
+ /// @notice Emitted when a release is unregistered
+ event ReleaseUnregistered(string releaseId);
+
+ /// @notice Emitted when a release is updated
+ event ReleaseUpdated(
+ string releaseId,
+ string releaseMetadataHash,
+ address beneficiary,
+ ReleaseTrack[] releaseTracks,
+ ReleaseToken[] releaseTokens
+ );
+
+ event ReleaseControllerUpdated(string releaseId, address controller, bool isController);
+
+ event AllowedTokenContractUpdated(string releaseId, address tokenContract, bool isAllowed);
+
+ event ReleaseTokenAdded(string releaseId, address tokenAddress, uint256 tokenId);
+
+ /**
+ * @notice Represents a registered release.
+ * @param releaseMetadataHash The metadata hash of the release
+ * @param beneficiary The beneficiary of the release
+ * @param releaseTracks The tracks in the release
+ * @param controllers The authorized addresses that can update the release
+ * @param allowedTokenContracts The authorized token contract addresses
+ * that can be used to create a release token
+ * @param releaseTokens A list of release tokens created from the release
+ */
+ struct RegisteredRelease {
+ string releaseMetadataHash;
+ address beneficiary;
+ ReleaseTrack[] releaseTracks;
+ mapping(address => bool) controllers;
+ mapping(address => bool) allowedTokenContracts;
+ ReleaseToken[] releaseTokens;
+ }
+ /**
+ * @notice Represents a release token that has been created from a Registered release.
+ * @param tokenAddress The address of the Releases contract from which the tracks were released
+ * @param tokenId The token id of the release
+ */
+
+ struct ReleaseToken {
+ address tokenAddress;
+ uint256 tokenId;
+ uint256 chainId;
+ }
+
+ /**
+ * @notice Represents a track in a release
+ * @param trackIndex The index of the track
+ * @param accessGranted A boolean indicating if the track has been granted access
+ * by the track controller
+ */
+ struct ReleaseTrack {
+ uint256 trackIndex;
+ bool accessGranted;
+ }
+
+ /**
+ * @notice Registers a release. In order for a release to be registered
+ * the tracks must be registered and verified and the caller must be a
+ * controller for each track used.
+ * @param releaseMetadataHash The metadata hash of the release
+ * @param trackIndexes The indexes of the tracks in the release
+ * @param controllers The authorized accounts that can update the release
+ * and create release tokens
+ * @param allowedTokenContracts The contracts allowed to create release tokens
+ */
+ function registerRelease(
+ string calldata releaseMetadataHash,
+ uint256[] calldata trackIndexes,
+ address[] calldata controllers,
+ address[] calldata allowedTokenContracts
+ ) external;
+
+ /**
+ * @notice Unregisters a releases. This deletes a release hash enabling the release
+ * to be created again. Only the default admin can call this method.
+ * @param releaseIndex The index of the release
+ */
+ function unregisterRelease(uint256 releaseIndex) external;
+
+ /**
+ * @notice Returns a registered release
+ * @param releaseIndex The index of the release
+ */
+ function getReleaseAtIndex(uint256 releaseIndex)
+ external
+ view
+ returns (
+ string calldata releaseMetadataHash,
+ address beneficiary,
+ ReleaseTrack[] memory releaseTracks
+ );
+
+ /**
+ * @notice Returns the id of a release
+ * @param releaseMetadataHash The metadata hash of the release
+ */
+ function getReleaseId(string calldata releaseMetadataHash) external view returns (string memory);
+
+ /**
+ * @notice Returns a list of release tokens
+ * @param releaseIndex The index of the release
+ */
+ function getReleaseTokens(uint256 releaseIndex) external view returns (ReleaseToken[] memory);
+
+ /**
+ * @notice Sets the Release metadata hash, the caller must be a controller
+ * for the token contract the release was created from.
+ * @param releaseIndex The index of the release
+ * @param newReleaseMetadataHash The new metadata hash of the release
+ */
+ function setReleaseMetadataHash(
+ uint256 releaseIndex,
+ string calldata newReleaseMetadataHash
+ ) external;
+
+ /**
+ * @notice Adds a release token to a registered release
+ * @param releaseIndex The index of the release
+ * @param tokenAddress The address of the token contract
+ * @param tokenId The token id of the release
+ */
+ function addReleaseToken(uint256 releaseIndex, address tokenAddress, uint256 tokenId) external;
+
+ /**
+ * @notice Grants access to a track in a release
+ * @param releaseIndex The index of the release
+ * @param trackIndex The index of the track
+ * @param hasAccess A boolean indicating if the track controller has granted access
+ */
+ function grantTrackAccess(uint256 releaseIndex, uint256 trackIndex, bool hasAccess) external;
+
+ /**
+ * @notice Returns a boolean indicating if a track controller
+ * has granted access to a track to be used for the release
+ * @param releaseIndex The index of the release
+ * @param trackIndex The index of the track
+ */
+ function checkTrackAccess(
+ uint256 releaseIndex,
+ uint256 trackIndex
+ ) external view returns (bool hasAccess);
+
+ /**
+ * @notice Sets an release controller
+ * The account setting the controller needs to be a controller for the corresponding release Id.
+ * @param releaseIndex The index of the release
+ * @param controller The address of the controller
+ * @param isController The boolean value to set or unset the controller
+ */
+ function setReleaseController(
+ uint256 releaseIndex,
+ address controller,
+ bool isController
+ ) external;
+
+ /**
+ * @notice Returns a boolean indicating if an account is a controller for a release
+ * @param account The address of the account
+ * @param releaseIndex The index of the release
+ */
+ function isReleaseController(address account, uint256 releaseIndex) external view returns (bool);
+
+ /**
+ * @notice Sets an allowed token contract
+ * The account setting the controller needs to be a controller for the corresponding release Id.
+ * @param releaseIndex The index of the release
+ * @param isAllowed The boolean value to set or unset the allowed contract
+ */
+ function setAllowedTokenContract(
+ uint256 releaseIndex,
+ address tokenContract,
+ bool isAllowed
+ ) external;
+
+ /**
+ * @notice Returns a boolean indicating if the token contract is allowed
+ * to be used to create a release token
+ * @param tokenContract The address of the token contract
+ * @param releaseIndex The index of the release
+ */
+ function isAllowedTokenContract(
+ address tokenContract,
+ uint256 releaseIndex
+ ) external view returns (bool);
+
+ /**
+ * @notice Returns the metadata hash for a token, called by a token contract to construct the URI
+ * @param tokenId The id of the token
+ */
+ function getTokenMetadataHash(uint256 tokenId) external view returns (string memory);
+}
diff --git a/contracts/src/interfaces/Registry/ITrackRegistration.sol b/contracts/src/interfaces/Registry/ITrackRegistration.sol
new file mode 100644
index 00000000..d2b10a9f
--- /dev/null
+++ b/contracts/src/interfaces/Registry/ITrackRegistration.sol
@@ -0,0 +1,128 @@
+// SPDX-License-Identifier: MIT
+pragma solidity 0.8.21;
+
+interface ITrackRegistration {
+ /// @notice Emitted when a track is registered
+
+ event TrackRegistered(
+ string trackId,
+ uint256[] artistIndexes,
+ string trackMetadataHash,
+ address indexed beneficiary,
+ address indexed trackRegisterer
+ );
+
+ event TrackControllerUpdated(string trackId, address controller, bool isController);
+
+ /// @notice Emitted whenever a field in RegisterTrack is updated
+ event TrackUpdated(
+ address indexed updatedBy,
+ string trackId,
+ uint256[] artistIndexes,
+ string trackMetadataHash,
+ address beneficiary,
+ TrackStatus status
+ );
+
+ enum TrackStatus {
+ PENDING,
+ VERIFIED,
+ REJECTED
+ }
+
+ /**
+ * @notice Represents a registered track
+ * @param artistIndexes The unique indexes of the artists on the track
+ * @param trackMetadataHash This is the IPFS hash of the track metadata
+ * @param beneficiary The beneficiary of the track
+ * @param trackStatus The status of the track (PENDING, VERIFIED, REJECTED)
+ * @param controllers The authorized addresses that can update the track
+ */
+ struct RegisteredTrack {
+ uint256[] artistIndexes;
+ string trackMetadataHash;
+ address beneficiary;
+ TrackStatus trackStatus;
+ mapping(address => bool) controllers;
+ }
+
+ /**
+ * @notice Registers a track using the IPFS hash of the track metadata and the Artist Id.
+ * The account registering the track needs to be a controller for the corresponding artist Id.
+ * @param artistIndexes - The unique indexes of the artists on the track
+ * @param trackMetadataHash - The metadata hash of the track
+ * @param beneficiary - The beneficiary of the track (could be single or an 0xSplit)
+ * @param controllers - The authorized addresses that can update the track
+ */
+ function registerTrack(
+ uint256[] memory artistIndexes,
+ string calldata trackMetadataHash,
+ address beneficiary,
+ address[] calldata controllers
+ ) external;
+
+ /**
+ * @notice Returns all registered track data
+ * @param trackIndex The index of the track
+ */
+ function getTrackAtIndex(uint256 trackIndex)
+ external
+ view
+ returns (
+ uint256[] memory artistIds,
+ string memory trackMetadataHash,
+ address beneficiary,
+ TrackStatus status
+ );
+
+ /**
+ * @notice Returns the track id for a track
+ * @param trackMetadataHash - The registration hash of the track
+ */
+ function getTrackId(string calldata trackMetadataHash) external view returns (string memory);
+
+ /**
+ * @notice Sets the track beneficiary for a track
+ * @param trackIndex The index of the track
+ * @param newBeneficiary - The new beneficiary of the track
+ */
+ function setTrackBeneficiary(uint256 trackIndex, address newBeneficiary) external;
+
+ /**
+ * @notice Sets the track uri
+ * @notice Only the track owner can call this function
+ * @param trackIndex The index of the track
+ * @param newTrackMetadataHash - The new registration hash of the track
+ * i.e. the updated IPFS hash of the track metadata
+ */
+ function setTrackMetadataHash(uint256 trackIndex, string calldata newTrackMetadataHash) external;
+
+ /**
+ * @notice Sets a track status
+ * The account setting the status needs to have a verifier role
+ * @param trackIndex The index of the track
+ * @param status The new status of the track
+ */
+ function setTrackStatus(uint256 trackIndex, TrackStatus status) external;
+
+ /**
+ * @notice Sets a track controller
+ * The account setting the controller needs to be a controller for the corresponding track Id.
+ * @param trackIndex The index of the track
+ * @param controller The address of the controller
+ * @param isController The boolean value to set or unset the controller
+ */
+ function setTrackController(uint256 trackIndex, address controller, bool isController) external;
+
+ /**
+ * @notice Returns if the caller is a controller for the track
+ * @param account The address to check
+ * @param trackIndex The index of the track
+ */
+ function isTrackController(address account, uint256 trackIndex) external view returns (bool);
+
+ /**
+ * @notice Returns all tracks that the caller is a controller for
+ */
+ function getTracksForController() external view returns (uint256[] memory);
+}
diff --git a/moda-contracts/src/interfaces/Releases/IBurnable.sol b/contracts/src/interfaces/Releases/IBurnable.sol
similarity index 77%
rename from moda-contracts/src/interfaces/Releases/IBurnable.sol
rename to contracts/src/interfaces/Releases/IBurnable.sol
index 09b7fc97..fffffadb 100644
--- a/moda-contracts/src/interfaces/Releases/IBurnable.sol
+++ b/contracts/src/interfaces/Releases/IBurnable.sol
@@ -1,9 +1,6 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;
-import {IReleases} from "./IReleases.sol";
-import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
-
/**
* @notice IBurnable is an interface for burning a token.
*/
diff --git a/moda-contracts/src/interfaces/Releases/IReleases.sol b/contracts/src/interfaces/Releases/IReleases.sol
similarity index 63%
rename from moda-contracts/src/interfaces/Releases/IReleases.sol
rename to contracts/src/interfaces/Releases/IReleases.sol
index 7f8ec768..ce3cd3fd 100644
--- a/moda-contracts/src/interfaces/Releases/IReleases.sol
+++ b/contracts/src/interfaces/Releases/IReleases.sol
@@ -13,29 +13,20 @@ interface IReleases is IERC165 {
event ReleaseWithdrawn(address indexed receiver, uint256 tokenId, uint256 amount);
/**
- * @notice Creates a new release token and transfers to the receiver. Tracks must be registered
- * to the catalog contract that this contract is
- * scoped to before calling this function.
+ * @notice Creates a new release token and transfers to the receiver.
* @param receiver The address that will receive the release tokens
+ * @param beneficiary The address that will receive royalties from the release
* @param royaltyAmount The percentage of sale prices
* that should be paid to the beneficiary for re-sales.
* Calculated by / 10,000. e.g. For 10% royalties, pass in 1000
- * @param uri_ The URI of the token metadata
* @param totalSupply The total amount of tokens to mint
- * @param trackIds An array containing the registered track ids of the tracks
+ * @param metadataUri The URI for the metadata of the release
*/
function create(
address receiver,
+ address beneficiary,
uint96 royaltyAmount,
- string calldata uri_,
uint256 totalSupply,
- string[] calldata trackIds
+ string calldata metadataUri
) external;
-
- /**
- * @notice Sets the metadata URI for a release, only the default admin can call this
- * @param tokenId The ID of the token
- * @param uri_ The URI for the token
- */
- function setUri(uint256 tokenId, string calldata uri_) external;
}
diff --git a/moda-contracts/src/interfaces/Releases/IReleasesFactory.sol b/contracts/src/interfaces/Releases/IReleasesFactory.sol
similarity index 53%
rename from moda-contracts/src/interfaces/Releases/IReleasesFactory.sol
rename to contracts/src/interfaces/Releases/IReleasesFactory.sol
index badb8c34..d51f0518 100644
--- a/moda-contracts/src/interfaces/Releases/IReleasesFactory.sol
+++ b/contracts/src/interfaces/Releases/IReleasesFactory.sol
@@ -1,8 +1,6 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;
-import {ICatalog} from "../Catalog/ICatalog.sol";
-
/**
* @notice IReleasesFactory defines an interface for a factory contract that deploys trustless Releases contracts.
*/
@@ -15,17 +13,9 @@ interface IReleasesFactory {
);
/**
- * @notice Creates a new releases contract. The caller becomes
- * the owner and default admin of the new releases contract.
- * @param releaseAdmins The addresses of the release admins
+ * @notice Creates a new releases contract
* @param name The name of the releases contract
* @param symbol The symbol of the releases contract
- * @param catalog A contract that implements ICatalog
*/
- function create(
- address[] memory releaseAdmins,
- string memory name,
- string memory symbol,
- ICatalog catalog
- ) external;
+ function create(string calldata name, string calldata symbol) external returns (address);
}
diff --git a/contracts/src/interfaces/Releases/IReleasesInitialize.sol b/contracts/src/interfaces/Releases/IReleasesInitialize.sol
new file mode 100644
index 00000000..76c3787e
--- /dev/null
+++ b/contracts/src/interfaces/Releases/IReleasesInitialize.sol
@@ -0,0 +1,15 @@
+// SPDX-License-Identifier: MIT
+pragma solidity 0.8.21;
+
+import {IRegistry} from "../Registry/IRegistry.sol";
+import {ISplitsFactory} from "../ISplitsFactory.sol";
+
+interface IReleasesInitialize {
+ /**
+ * @notice Initializes the contract
+ * @param admin The address that will be given the role of default admin. See {AccessControl}
+ * @param name_ The name of the Releases contract
+ * @param symbol_ The symbol of the Releases contract
+ */
+ function initialize(address admin, string memory name_, string memory symbol_) external;
+}
diff --git a/moda-contracts/src/interfaces/Releases/IWithdrawRelease.sol b/contracts/src/interfaces/Releases/IWithdrawRelease.sol
similarity index 100%
rename from moda-contracts/src/interfaces/Releases/IWithdrawRelease.sol
rename to contracts/src/interfaces/Releases/IWithdrawRelease.sol
diff --git a/contracts/test/Marketplace.t.sol b/contracts/test/Marketplace.t.sol
new file mode 100644
index 00000000..10e02e82
--- /dev/null
+++ b/contracts/test/Marketplace.t.sol
@@ -0,0 +1,71 @@
+// SPDX-License-Identifier: UNLICENSED
+pragma solidity ^0.8.13;
+
+import {Test, console2} from "forge-std/Test.sol";
+import {RegistryTestSetUp} from "./Registry/RegistryTestSetUp.t.sol";
+import {Marketplace} from "../src/Marketplace.sol";
+import {ERC20TokenMock} from "./mocks/ERC20TokenMock.sol";
+import {IReleases} from "../src/interfaces/Releases/IReleases.sol";
+
+contract MarketplaceTest is Test {
+ Marketplace public marketplace;
+ ERC20TokenMock public token;
+
+ string name = "TestReleases";
+ string symbol = "TEST";
+ address admin = address(0x6);
+ address beneficiary = address(0x2);
+ address recipient = address(0x7);
+
+ address payable treasury = payable(address(0x5));
+ uint256 treasuryFee = 1000;
+
+ event SaleCreated(address indexed releaseAdmin, uint256 indexed saleId);
+ event Purchase(
+ address indexed releases,
+ uint256 indexed tokenId,
+ address indexed buyer,
+ address releaseOwner,
+ uint256 saleId,
+ uint256 tokenAmount,
+ uint256 timestamp
+ );
+
+ function setUp() public {
+ vm.startPrank(admin);
+ token = new ERC20TokenMock();
+ marketplace = new Marketplace(token);
+ }
+
+ function test_constructor() public {}
+
+ function createSale_setUp() public {}
+
+ function test_createSale() public {}
+
+ function test_createSale_RevertIf_beneficiary_address_is_zero() public {}
+
+ function test_createSale_RevertIf_amountTotal_is_zero() public {}
+
+ function test_createSale_RevertIf_startAt_is_after_endAt() public {}
+
+ function test_createSale_RevertIf_maxCountPerWallet_is_zero() public {}
+
+ function test_createSale_emits_event() public {}
+
+ function test_purchase() public {}
+
+ function test_purchase_RevertIf_Sale_has_not_started() public {}
+
+ function test_purchase_RevertIf_Sale_has_ended() public {}
+
+ function test_purchase_RevertIf_tokenAmount_is_zero() public {}
+
+ function test_purchase_RevertIf_tokenAmount_is_greater_than_amountRemaining() public {}
+
+ function test_purchase_RevertIf_maxCountPerWallet_is_exceeded() public {}
+
+ function test_purchase_emits_event() public {}
+
+ function test_withdraw() public {}
+}
diff --git a/moda-contracts/test/Profile.t.sol b/contracts/test/Profile.t.sol
similarity index 100%
rename from moda-contracts/test/Profile.t.sol
rename to contracts/test/Profile.t.sol
diff --git a/contracts/test/Registry-Upgrades.t.sol b/contracts/test/Registry-Upgrades.t.sol
new file mode 100644
index 00000000..c6b72d64
--- /dev/null
+++ b/contracts/test/Registry-Upgrades.t.sol
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: UNLICENSED
+pragma solidity 0.8.21;
+
+import {Test, console2} from "forge-std/Test.sol";
+import "../src/interfaces/IMembership.sol";
+import "../src/interfaces/ISplitsFactory.sol";
+import {RegistryV2Mock} from "../test/mocks/RegistryV2Mock.sol";
+import {Registry} from "../src/Registry.sol";
+import {Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol";
+import {IBeacon} from "@openzeppelin/contracts/proxy/beacon/IBeacon.sol";
+import {SplitsFactoryMock} from "./mocks/SplitsFactoryMock.sol";
+
+contract RegistryUpgradesTest is Test {
+ address public registryImplementation;
+ address public registryImplementationV2;
+ address public registryProxy;
+ address public beacon;
+
+ string public registryName = "Test";
+ uint256 public chainId = 0;
+ address public deployer = address(0x3);
+ IMembership public membership = IMembership(address(0x4));
+ SplitsFactoryMock public splitsFactory = SplitsFactoryMock(address(0x5));
+
+ address admin = address(0x4);
+
+ function setUp() public {
+ beacon = Upgrades.deployBeacon("Registry.sol:Registry", admin);
+ registryImplementation = IBeacon(beacon).implementation();
+
+ registryProxy = Upgrades.deployBeaconProxy(
+ beacon, abi.encodeCall(Registry.initialize, (admin, registryName, membership, splitsFactory))
+ );
+ }
+
+ function test_beacon() public view {
+ address proxyBeaconAddress = Upgrades.getBeaconAddress(registryProxy);
+ assertEq(proxyBeaconAddress, beacon);
+ }
+
+ function upgrade_beacon_with_RegistryV2_setUp() public {
+ vm.startPrank(admin);
+ Upgrades.upgradeBeacon(beacon, "RegistryV2Mock.sol");
+ vm.stopPrank();
+ }
+
+ function test_implementation_address_updated() public {
+ upgrade_beacon_with_RegistryV2_setUp();
+ registryImplementationV2 = IBeacon(beacon).implementation();
+ assertFalse(registryImplementation == registryImplementationV2);
+ }
+
+ function test_proxy_can_call_updated_Registry() public {
+ upgrade_beacon_with_RegistryV2_setUp();
+ RegistryV2Mock(registryProxy).setTestingUpgradeVariable("upgradeTest");
+ string memory upgradeTest = RegistryV2Mock(registryProxy).getTestingUpgradeVariable();
+ assertEq(upgradeTest, "upgradeTest");
+ }
+}
diff --git a/contracts/test/Registry/RegistryTestSetUp.t.sol b/contracts/test/Registry/RegistryTestSetUp.t.sol
new file mode 100644
index 00000000..30703733
--- /dev/null
+++ b/contracts/test/Registry/RegistryTestSetUp.t.sol
@@ -0,0 +1,102 @@
+// SPDX-License-Identifier: UNLICENSED
+pragma solidity ^0.8.21;
+
+import {TestUtils} from "../utils/TestUtils.sol";
+import {Registry} from "../../src/Registry.sol";
+import {Membership} from "../../test/mocks/MembershipMock.sol";
+import {ReleasesFactory} from "../../src/ReleasesFactory.sol";
+import {Releases} from "../../src/Releases.sol";
+import {SplitsFactoryMock} from "../mocks/SplitsFactoryMock.sol";
+import {Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol";
+import {IArtistRegistration} from "../../src/interfaces/Registry/IArtistRegistration.sol";
+import {ITrackRegistration} from "../../src/interfaces/Registry/ITrackRegistration.sol";
+
+contract RegistryTestSetUp is TestUtils {
+ Registry public registry;
+ Membership public membership;
+ SplitsFactoryMock public splitsFactory;
+ ReleasesFactory public releasesFactory;
+ Releases public releasesMaster;
+ Releases public releases;
+
+ address admin = _w.alice();
+ string public registryName = "ACME";
+ address public artist = _w.bob();
+ address public artistController = _w.eric();
+ address public trackVerifier = _w.carl();
+ address public mockSplit = _w.dave();
+ address public nonController = _w.frank();
+ address public nonMember = _w.gary();
+
+ string public artistMetadataHash = "artistHash";
+ address[] public artistControllers = [artist, artistController];
+
+ address public trackBeneficiary = _w.dave();
+ string public trackMetadataHash = "trackHash";
+ address[] public trackControllers = [artist, artistController];
+ uint256[] public artistIndexes = [1];
+
+ string releaseMetadataHash = "releaseHash";
+ uint256[] public trackIndexes = [1];
+ address[] releaseControllers = [artist, artistController];
+ address[] allowedTokenContracts = [address(releases)];
+
+ function setUp() public {
+ membership = new Membership();
+ splitsFactory = new SplitsFactoryMock(mockSplit);
+
+ address beacon = Upgrades.deployBeacon("Registry.sol:Registry", admin);
+
+ registry = Registry(
+ Upgrades.deployBeaconProxy(
+ beacon,
+ abi.encodeCall(Registry.initialize, (admin, "DropRegistry", membership, splitsFactory))
+ )
+ );
+
+ releasesMaster = new Releases();
+ releasesFactory = new ReleasesFactory(address(releasesMaster));
+ vm.startPrank(admin);
+ registry.grantRole(keccak256("RELEASES_REGISTRAR_ROLE"), address(releasesFactory));
+ registry.grantRole(keccak256("VERIFIER_ROLE"), admin);
+ vm.stopPrank();
+
+ membership.addMember(admin);
+ membership.addMember(artist);
+ membership.addMember(artistController);
+ membership.addMember(nonController);
+
+ vm.startPrank(artist);
+ address releasesAddress = releasesFactory.create("name", "symbol");
+ vm.stopPrank();
+
+ releases = Releases(releasesAddress);
+ }
+
+ function registerArtist_setUp() public {
+ vm.startPrank(artist);
+ registry.registerArtist(artistMetadataHash, artistControllers);
+ vm.startPrank(admin);
+ registry.setArtistStatus(1, IArtistRegistration.ArtistStatus.VERIFIED);
+ vm.stopPrank();
+ }
+
+ function registerTrack_setUp() public {
+ vm.startPrank(artist);
+ registry.registerTrack(artistIndexes, trackMetadataHash, trackBeneficiary, trackControllers);
+ vm.stopPrank();
+ }
+
+ function verifyTrack_setUp() public {
+ vm.startPrank(admin);
+ registry.setTrackStatus(1, ITrackRegistration.TrackStatus.VERIFIED);
+ vm.stopPrank();
+ }
+
+ function registeringRelease_setUp() public {
+ vm.startPrank(artist);
+ registry.registerRelease(
+ releaseMetadataHash, trackIndexes, releaseControllers, allowedTokenContracts
+ );
+ }
+}
diff --git a/contracts/test/Registry/Registry_Initialize.t.sol b/contracts/test/Registry/Registry_Initialize.t.sol
new file mode 100644
index 00000000..157741dc
--- /dev/null
+++ b/contracts/test/Registry/Registry_Initialize.t.sol
@@ -0,0 +1,18 @@
+// SPDX-License-Identifier: UNLICENSED
+pragma solidity ^0.8.21;
+
+import {RegistryTestSetUp} from "./RegistryTestSetUp.t.sol";
+
+contract RegistryInitializeTest is RegistryTestSetUp {
+ error InvalidInitialization();
+
+ /// Initialization revert
+
+ function test_initialize_RevertIf_already_initialized() public {
+ setUp();
+ vm.expectRevert(InvalidInitialization.selector);
+ vm.startPrank(admin);
+ registry.initialize(admin, registryName, membership, splitsFactory);
+ vm.stopPrank();
+ }
+}
diff --git a/contracts/test/Registry/Registry_RegisterArtist.t.sol b/contracts/test/Registry/Registry_RegisterArtist.t.sol
new file mode 100644
index 00000000..9ab40015
--- /dev/null
+++ b/contracts/test/Registry/Registry_RegisterArtist.t.sol
@@ -0,0 +1,50 @@
+// SPDX-License-Identifier: UNLICENSED
+pragma solidity ^0.8.21;
+
+import {RegistryTestSetUp} from "./RegistryTestSetUp.t.sol";
+import {Registry} from "../../src/Registry.sol";
+import {IArtistRegistration} from "../../src/interfaces/Registry/IArtistRegistration.sol";
+import {console2} from "forge-std/Test.sol";
+
+contract RegisterArtistTest is RegistryTestSetUp {
+ event ArtistRegistered(string id, string artistMetadataHash);
+
+ function test_registerArtist() public {
+ registerArtist_setUp();
+
+ (string memory metadataHash, IArtistRegistration.ArtistStatus status) =
+ registry.getArtistAtIndex(1);
+
+ assertEq(metadataHash, artistMetadataHash);
+ assertEq(uint256(status), uint256(IArtistRegistration.ArtistStatus.VERIFIED));
+ assertEq(registry.getArtistId(artistMetadataHash), "ARTIST-DROP-31337-1");
+ assertEq(registry.isArtistController(artist, 1), true);
+ assertEq(registry.isArtistController(artistController, 1), true);
+ }
+
+ function test_registerArtist_RevertsIf_artist_already_registered() public {
+ registerArtist_setUp();
+
+ vm.expectRevert(Registry.ArtistAlreadyRegistered.selector);
+ vm.startPrank(artist);
+ registry.registerArtist(artistMetadataHash, artistControllers);
+ vm.stopPrank();
+ }
+
+ function test_registerArtist_RevertIf_user_not_member() public {
+ vm.expectRevert(Registry.MembershipRequired.selector);
+ address nonMember = address(0x9);
+ vm.startPrank(nonMember);
+ registry.registerArtist(artistMetadataHash, artistControllers);
+ vm.stopPrank();
+ }
+
+ function test_registerArtist_emits_event() public {
+ vm.expectEmit(true, true, true, true);
+ emit ArtistRegistered("ARTIST-DROP-31337-1", artistMetadataHash);
+
+ vm.startPrank(artist);
+ registry.registerArtist(artistMetadataHash, artistControllers);
+ vm.stopPrank();
+ }
+}
diff --git a/contracts/test/Registry/Registry_RegisterRelease.t.sol b/contracts/test/Registry/Registry_RegisterRelease.t.sol
new file mode 100644
index 00000000..bb10df53
--- /dev/null
+++ b/contracts/test/Registry/Registry_RegisterRelease.t.sol
@@ -0,0 +1,125 @@
+// SPDX-License-Identifier: UNLICENSED
+pragma solidity ^0.8.21;
+
+import {RegistryTestSetUp} from "./RegistryTestSetUp.t.sol";
+import {Registry} from "../../src/Registry.sol";
+import {ITrackRegistration} from "../../src/interfaces/Registry/ITrackRegistration.sol";
+import {console2} from "forge-std/Test.sol";
+
+contract RegisterReleaseTest is RegistryTestSetUp {
+ event ReleaseRegistered(string releaseId, uint256[] trackIndexes, address registrerer);
+ event ReleaseUnregistered(string releaseId);
+
+ function test_registerRelease() public {
+ registerArtist_setUp();
+ registerTrack_setUp();
+ verifyTrack_setUp();
+ registeringRelease_setUp();
+
+ (
+ string memory releaseMetadataHash_,
+ address beneficiary,
+ Registry.ReleaseTrack[] memory releaseTracks
+ ) = registry.getReleaseAtIndex(1);
+
+ string memory releaseId = registry.getReleaseId(releaseMetadataHash);
+
+ assertEq(releaseMetadataHash_, releaseMetadataHash);
+ assertEq(beneficiary, mockSplit);
+ assertEq(releaseId, "RELEASE-DROP-31337-1");
+ assertEq(releaseTracks[0].trackIndex, 1);
+ assertEq(releaseTracks[0].accessGranted, false);
+ }
+
+ function test_registerRelease_RevertIf_tracks_unverified() public {
+ registerArtist_setUp();
+ registerTrack_setUp();
+
+ vm.expectRevert(Registry.TrackHasNotBeenVerified.selector);
+
+ vm.startPrank(artist);
+ registry.registerRelease(
+ releaseMetadataHash, trackIndexes, releaseControllers, allowedTokenContracts
+ );
+ }
+
+ function test_registerRelease_RevertIf_track_not_registered() public {
+ uint256[] memory unregisteredTrackIndexes = new uint256[](1);
+ unregisteredTrackIndexes[0] = 2;
+
+ vm.expectRevert(Registry.TrackIsNotRegistered.selector);
+
+ vm.startPrank(artist);
+ registry.registerRelease(
+ releaseMetadataHash, unregisteredTrackIndexes, releaseControllers, allowedTokenContracts
+ );
+ }
+
+ function test_registerRelease_RevertIf_release_already_created() public {
+ registerArtist_setUp();
+ registerTrack_setUp();
+ verifyTrack_setUp();
+ registeringRelease_setUp();
+
+ vm.expectRevert(Registry.ReleaseAlreadyCreated.selector);
+
+ registry.registerRelease(
+ releaseMetadataHash, trackIndexes, releaseControllers, allowedTokenContracts
+ );
+ }
+
+ function test_registerRelease_emits_event() public {
+ registerArtist_setUp();
+ registerTrack_setUp();
+ verifyTrack_setUp();
+
+ vm.startPrank(artist);
+
+ vm.expectEmit(true, true, true, true);
+ emit ReleaseRegistered("RELEASE-DROP-31337-1", trackIndexes, artist);
+ registry.registerRelease(
+ releaseMetadataHash, trackIndexes, releaseControllers, allowedTokenContracts
+ );
+ }
+
+ // unregisterRelease
+
+ function test_unregisterRelease() public {
+ registerArtist_setUp();
+ registerTrack_setUp();
+ verifyTrack_setUp();
+ registeringRelease_setUp();
+
+ vm.startPrank(admin);
+ registry.unregisterRelease(1);
+
+ (
+ string memory releaseMetadataHash_,
+ address beneficiary,
+ Registry.ReleaseTrack[] memory releaseTracks
+ ) = registry.getReleaseAtIndex(1);
+
+ assertEq(releaseMetadataHash_, "");
+ assertEq(beneficiary, address(0));
+ assertEq(releaseTracks.length, 0);
+ }
+
+ // function test_unregisterRelease_emits_event() public {
+ // registeringRelease_setUp();
+ // vm.startPrank(artist);
+ // registry.setReleasesApprovalForAll(artist, address(releases), true);
+ // vm.stopPrank();
+ // vm.startPrank(address(releases));
+ // registry.registerRelease(
+ // registeringReleaseData.trackIds, registeringReleaseData.uri, registeringReleaseData.tokenId
+ // );
+ // vm.stopPrank();
+
+ // bytes32 releaseHash = registry.getReleaseHash(address(releases), registeringReleaseData.tokenId);
+ // vm.expectEmit(true, true, true, true);
+ // emit ReleaseUnregistered(releaseHash);
+ // vm.startPrank(admin);
+ // registry.unregisterRelease(releaseHash);
+ // vm.stopPrank();
+ // }
+}
diff --git a/contracts/test/Registry/Registry_RegisterTrack.t.sol b/contracts/test/Registry/Registry_RegisterTrack.t.sol
new file mode 100644
index 00000000..a8647e19
--- /dev/null
+++ b/contracts/test/Registry/Registry_RegisterTrack.t.sol
@@ -0,0 +1,78 @@
+// SPDX-License-Identifier: UNLICENSED
+pragma solidity ^0.8.21;
+
+import {RegistryTestSetUp} from "./RegistryTestSetUp.t.sol";
+import {Registry} from "../../src/Registry.sol";
+import {ITrackRegistration} from "../../src/interfaces/Registry/ITrackRegistration.sol";
+import {IArtistRegistration} from "../../src/interfaces/Registry/IArtistRegistration.sol";
+
+contract RegisterTrackTest is RegistryTestSetUp {
+ event TrackRegistered(
+ string trackId,
+ uint256[] artistIndexes,
+ string trackMetadataHash,
+ address indexed beneficiary,
+ address indexed trackRegisterer
+ );
+
+ function test_registerTrack() public {
+ registerArtist_setUp();
+ registerTrack_setUp();
+
+ uint256[] memory indexes = new uint256[](1);
+ indexes[0] = 1;
+
+ (
+ uint256[] memory artistIndexes_,
+ string memory trackMetadataHash_,
+ address trackBeneficiary_,
+ ITrackRegistration.TrackStatus status
+ ) = registry.getTrackAtIndex(1);
+
+ assertEq(artistIndexes_, indexes);
+ assertEq(trackMetadataHash_, trackMetadataHash);
+ assertEq(trackBeneficiary_, trackBeneficiary);
+ assertEq(uint256(status), uint256(ITrackRegistration.TrackStatus.PENDING));
+ assertEq(registry.getTrackId(trackMetadataHash), "TRACK-DROP-31337-1");
+ }
+
+ function test_registerTrack_RevertIf_track_already_registered() public {
+ registerArtist_setUp();
+ registerTrack_setUp();
+
+ vm.expectRevert(Registry.TrackAlreadyRegistered.selector);
+ vm.startPrank(artist);
+ registry.registerTrack(artistIndexes, trackMetadataHash, trackBeneficiary, trackControllers);
+ vm.stopPrank();
+ }
+
+ function test_registerTrack_RevertIf_user_not_member() public {
+ vm.expectRevert(Registry.MembershipRequired.selector);
+ vm.startPrank(nonMember);
+ registry.registerTrack(artistIndexes, trackMetadataHash, trackBeneficiary, trackControllers);
+ vm.stopPrank();
+ }
+
+ function test_registerTrack_RevertIf_user_is_not_artist_controller() public {
+ registerArtist_setUp();
+
+ vm.expectRevert(Registry.CallerDoesNotHavePermission.selector);
+ vm.startPrank(nonController);
+ registry.registerTrack(artistIndexes, trackMetadataHash, trackBeneficiary, trackControllers);
+ vm.stopPrank();
+ }
+
+ function test_registerTrack_emits_event() public {
+ registerArtist_setUp();
+
+ uint256[] memory indexes = new uint256[](1);
+ indexes[0] = 1;
+
+ vm.expectEmit(true, true, true, true);
+ emit TrackRegistered("TRACK-DROP-31337-1", indexes, trackMetadataHash, trackBeneficiary, artist);
+
+ vm.startPrank(artist);
+ registry.registerTrack(artistIndexes, trackMetadataHash, trackBeneficiary, trackControllers);
+ vm.stopPrank();
+ }
+}
diff --git a/contracts/test/Releases.t.sol b/contracts/test/Releases.t.sol
new file mode 100644
index 00000000..62d6f9d8
--- /dev/null
+++ b/contracts/test/Releases.t.sol
@@ -0,0 +1,54 @@
+// SPDX-License-Identifier: UNLICENSED
+pragma solidity ^0.8.13;
+
+import {Test, console2} from "forge-std/Test.sol";
+import {RegistryTestSetUp} from "./Registry/RegistryTestSetUp.t.sol";
+import {IReleases} from "../src/interfaces/Releases/IReleases.sol";
+
+contract ReleasesTest is RegistryTestSetUp {
+ error InvalidInitialization();
+
+ event ReleaseCreated(uint256 tokenId);
+ event ReleaseWithdrawn(address indexed receiver, uint256 tokenId, uint256 amount);
+ event URI(string value, uint256 indexed id);
+
+ // Initialization
+
+ function test_initialize() public {}
+
+ function test_initialize_RevertIf_already_initialized() public {}
+
+ // create release
+
+ function test_create() public {}
+
+ function test_create_RevertIf_caller_is_not_release_admin() public {}
+
+ function test_create_RevertIf_royalty_amount_is_over_2000() public {}
+
+ function test_create_emits_event() public {}
+
+ // withdrawRelease
+
+ function test_withdrawRelease() public {}
+
+ function test_withdrawRelease_RevertIf_tokenId_is_invalid() public {}
+
+ function test_withdrawRelease_emits_event() public {}
+
+ // setUri
+
+ function test_setUri() public {}
+
+ function test_setUri_RevertIf_tokenId_is_invalid() public {}
+
+ function test_setUri_emits_event() public {}
+
+ // royaltyInfo
+
+ function test_royaltyInfo() public {}
+
+ // supportsInterface
+
+ function test_supportsInterface() public view {}
+}
diff --git a/contracts/test/ReleasesFactory.t.sol b/contracts/test/ReleasesFactory.t.sol
new file mode 100644
index 00000000..11d23394
--- /dev/null
+++ b/contracts/test/ReleasesFactory.t.sol
@@ -0,0 +1,26 @@
+// SPDX-License-Identifier: UNLICENSED
+pragma solidity ^0.8.13;
+
+import {Test, console2} from "forge-std/Test.sol";
+import {ReleasesFactory} from "../src/ReleasesFactory.sol";
+import {Releases} from "../src/Releases.sol";
+import {Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol";
+
+contract ReleasesFactoryTest is Test {
+ Releases public releasesMaster;
+ ReleasesFactory public releasesFactory;
+
+ address public admin = address(0xa);
+
+ string name = "DRIP";
+ string symbol = "DRIP";
+
+ function setUp() public {
+ releasesMaster = new Releases();
+ releasesFactory = new ReleasesFactory(address(releasesMaster));
+ }
+
+ function test_constructor() public {}
+
+ function test_create() public {}
+}
diff --git a/moda-contracts/test/SplitsFactory.t.sol b/contracts/test/SplitsFactory.t.sol
similarity index 82%
rename from moda-contracts/test/SplitsFactory.t.sol
rename to contracts/test/SplitsFactory.t.sol
index 712121a7..b1fda786 100644
--- a/moda-contracts/test/SplitsFactory.t.sol
+++ b/contracts/test/SplitsFactory.t.sol
@@ -3,35 +3,10 @@ pragma solidity ^0.8.21;
import {SplitsFactory} from "../src/SplitsFactory.sol";
import {ISplitsFactory} from "../src/interfaces/ISplitsFactory.sol";
-import {IManagement} from "../src/interfaces/IManagement.sol";
import {ISplitMain} from "../src/interfaces/0xSplits/ISplitMain.sol";
-import {IOfficialContracts} from "../src/interfaces/Registry/IOfficialContracts.sol";
import {SplitMainMock} from "./mocks/SplitMainMock.sol";
import {TestUtils} from "./utils/TestUtils.sol";
-
-contract Registry is IOfficialContracts {
- address _treasury;
- uint32 _fee;
- uint32 _feeMultiplier;
-
- constructor(address treasury, uint32 fee, uint32 feeMultiplier) {
- _treasury = treasury;
- _fee = fee;
- _feeMultiplier = feeMultiplier;
- }
-
- function getTreasuryInfo() external view returns (address, uint32, uint32) {
- return (_treasury, _fee, _feeMultiplier);
- }
-
- function getSplitsFactory() external pure returns (ISplitsFactory) {
- return ISplitsFactory(address(0));
- }
-
- function getManagement() external pure returns (IManagement) {
- return IManagement(address(0));
- }
-}
+import {Registry} from "../src/Registry.sol";
contract SplitsFactoryTest is TestUtils {
SplitsFactory _splitsFactory;
@@ -46,9 +21,9 @@ contract SplitsFactoryTest is TestUtils {
event SplitCreated(address indexed sender, address indexed split);
function setUp() public {
- _registry = new Registry(_treasury, _Fee, _FeeMultiplier);
_splitMain = new SplitMainMock();
- _splitsFactory = new SplitsFactory(_splitMain, _registry);
+ vm.startPrank(_w.alice());
+ _splitsFactory = new SplitsFactory(_splitMain, _treasury, _Fee);
}
function setupBeneficiaries() public {
@@ -59,9 +34,7 @@ contract SplitsFactoryTest is TestUtils {
// constructor
function test_constructor_sets_sender_as_owner() public {
- vm.startPrank(_w.alice());
- SplitsFactory sf = new SplitsFactory(_splitMain, _registry);
- assertEq(sf.owner(), _w.alice());
+ assertEq(_splitsFactory.owner(), _w.alice());
vm.stopPrank();
}
diff --git a/moda-contracts/test/SplitsFactoryWithFork.t.sol b/contracts/test/SplitsFactoryWithFork.t.sol
similarity index 83%
rename from moda-contracts/test/SplitsFactoryWithFork.t.sol
rename to contracts/test/SplitsFactoryWithFork.t.sol
index 2af55a89..7c8d7430 100644
--- a/moda-contracts/test/SplitsFactoryWithFork.t.sol
+++ b/contracts/test/SplitsFactoryWithFork.t.sol
@@ -3,51 +3,24 @@ pragma solidity ^0.8.21;
import {SplitsFactory} from "../src/SplitsFactory.sol";
import {ISplitsFactory} from "../src/interfaces/ISplitsFactory.sol";
-import {IManagement} from "../src/interfaces/IManagement.sol";
import {ISplitMain} from "../src/interfaces/0xSplits/ISplitMain.sol";
-import {IOfficialContracts} from "../src/interfaces/Registry/IOfficialContracts.sol";
import {SplitMainMock} from "./mocks/SplitMainMock.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
-import "forge-std/Test.sol";
+import {TestUtils} from "./utils/TestUtils.sol";
-contract Registry is IOfficialContracts {
- address _treasury;
- uint32 _fee;
- uint32 _feeMultiplier;
-
- constructor(address treasury, uint32 fee, uint32 feeMultiplier) {
- _treasury = treasury;
- _fee = fee;
- _feeMultiplier = feeMultiplier;
- }
-
- function getTreasuryInfo() external view returns (address, uint32, uint32) {
- return (_treasury, _fee, _feeMultiplier);
- }
-
- function getSplitsFactory() external pure returns (ISplitsFactory) {
- return ISplitsFactory(address(0));
- }
-
- function getManagement() external pure returns (IManagement) {
- return IManagement(address(0));
- }
-}
-
-contract SplitsFactoryWithForkTest is Test {
+contract SplitsFactoryWithForkTest is TestUtils {
SplitsFactory _splitsFactory;
- Registry _registry;
ISplitMain _splitMain;
- string EVM_RPC_URL = vm.envString("EVM_RPC_URL");
- address _splitMainAddress = 0x2ed6c4B5dA6378c7897AC67Ba9e43102Feb694EE;
+ string EVM_RPC_URL = vm.envString("TESTNET_RPC_URL");
+ address _splitMainAddress = vm.envAddress("SPLIT_MAIN_ADDRESS");
address _payer = makeAddr("_payer");
- address _treasury = makeAddr("_treasury");
address _deployer = makeAddr("_deployer");
address _alice = makeAddr("_user_alice");
address _bob = makeAddr("_user_bob");
address _charlie = makeAddr("_user_charlie");
address _distributor = makeAddr("_distributor");
+ address treasury = makeAddr("treasury");
address _split;
@@ -76,9 +49,9 @@ contract SplitsFactoryWithForkTest is Test {
vm.deal(_payer, 10 ether);
vm.startPrank(_deployer);
- _registry = new Registry(_treasury, _fee, _feeMultiplier);
+
_splitMain = ISplitMain(_splitMainAddress);
- _splitsFactory = new SplitsFactory(_splitMain, _registry);
+ _splitsFactory = new SplitsFactory(_splitMain, treasury, _fee);
vm.stopPrank();
}
@@ -108,7 +81,7 @@ contract SplitsFactoryWithForkTest is Test {
}
function filter_sort_allocate_setUp() public returns (address[] memory, uint32[] memory) {
- _beneficiaries.push(_treasury);
+ _beneficiaries.push(treasury);
(address[] memory filteredBeneficiaries,) = _filterDuplicates(_beneficiaries);
address[] memory sortedBeneficiaries = _sortAddresses(filteredBeneficiaries);
@@ -116,7 +89,7 @@ contract SplitsFactoryWithForkTest is Test {
uint32[] memory percentAllocations = new uint32[](4);
for (uint256 i = 0; i < percentAllocations.length; i++) {
- if (sortedBeneficiaries[i] == _treasury) {
+ if (sortedBeneficiaries[i] == treasury) {
percentAllocations[i] = 100_000;
} else if (sortedBeneficiaries[i] == _alice) {
percentAllocations[i] = 450_000;
@@ -147,7 +120,7 @@ contract SplitsFactoryWithForkTest is Test {
uint256 balanceAlice = _splitMain.getETHBalance(_alice);
uint256 balanceBob = _splitMain.getETHBalance(_bob);
uint256 balanceCharlie = _splitMain.getETHBalance(_charlie);
- uint256 balanceTreasury = _splitMain.getETHBalance(_treasury);
+ uint256 balanceTreasury = _splitMain.getETHBalance(treasury);
uint256 distributorBalance = _splitMain.getETHBalance(_distributor);
assertLt(totalGas, distributorBalance, "Gas used should be less than the distributor's balance");
@@ -186,14 +159,14 @@ contract SplitsFactoryWithForkTest is Test {
vm.startPrank(_charlie);
_splitMain.withdraw(_charlie, 2.2275 ether, new ERC20[](0));
vm.stopPrank();
- vm.startPrank(_treasury);
- _splitMain.withdraw(_treasury, 0.99 ether, new ERC20[](0));
+ vm.startPrank(treasury);
+ _splitMain.withdraw(treasury, 0.99 ether, new ERC20[](0));
vm.stopPrank();
uint256 balanceAlice = _alice.balance;
uint256 balanceBob = _bob.balance;
uint256 balanceCharlie = _charlie.balance;
- uint256 balanceTreasury = _treasury.balance;
+ uint256 balanceTreasury = treasury.balance;
// The SplitMain contract leaves 1 wei in each account after
// withdraw is called for gas optimization purposes.
diff --git a/moda-contracts/test/mocks/AccessControlledMock.sol b/contracts/test/mocks/AccessControlledMock.sol
similarity index 100%
rename from moda-contracts/test/mocks/AccessControlledMock.sol
rename to contracts/test/mocks/AccessControlledMock.sol
diff --git a/moda-contracts/test/mocks/ERC165Mock.sol b/contracts/test/mocks/ERC165Mock.sol
similarity index 100%
rename from moda-contracts/test/mocks/ERC165Mock.sol
rename to contracts/test/mocks/ERC165Mock.sol
diff --git a/contracts/test/mocks/ERC20TokenMock.sol b/contracts/test/mocks/ERC20TokenMock.sol
new file mode 100644
index 00000000..84c5cb59
--- /dev/null
+++ b/contracts/test/mocks/ERC20TokenMock.sol
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: MIT
+pragma solidity 0.8.21;
+
+import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
+
+contract ERC20TokenMock is ERC20("token", "TOKEN") {
+ function mint(address to, uint256 amount) external {
+ _mint(to, amount);
+ }
+}
diff --git a/moda-contracts/test/mocks/FaultyMembershipMock.sol b/contracts/test/mocks/FaultyMembershipMock.sol
similarity index 100%
rename from moda-contracts/test/mocks/FaultyMembershipMock.sol
rename to contracts/test/mocks/FaultyMembershipMock.sol
diff --git a/moda-contracts/test/mocks/MembershipMock.sol b/contracts/test/mocks/MembershipMock.sol
similarity index 100%
rename from moda-contracts/test/mocks/MembershipMock.sol
rename to contracts/test/mocks/MembershipMock.sol
diff --git a/moda-contracts/test/mocks/OwnedContractMock.sol b/contracts/test/mocks/OwnedContractMock.sol
similarity index 100%
rename from moda-contracts/test/mocks/OwnedContractMock.sol
rename to contracts/test/mocks/OwnedContractMock.sol
diff --git a/moda-contracts/test/mocks/CatalogV2Mock.sol b/contracts/test/mocks/RegistryV2Mock.sol
similarity index 53%
rename from moda-contracts/test/mocks/CatalogV2Mock.sol
rename to contracts/test/mocks/RegistryV2Mock.sol
index e51c28aa..1278b1d3 100644
--- a/moda-contracts/test/mocks/CatalogV2Mock.sol
+++ b/contracts/test/mocks/RegistryV2Mock.sol
@@ -1,27 +1,27 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;
-import {Catalog} from "../../src/Catalog.sol";
+import {Registry} from "../../src/Registry.sol";
-/// @custom:oz-upgrades-from Catalog
-contract CatalogV2Mock is Catalog {
+/// @custom:oz-upgrades-from Registry
+contract RegistryV2Mock is Registry {
/// State Variables
- /// @custom:storage-location erc7201:moda.storage.CatalogV2
- struct CatalogV2Storage {
+ /// @custom:storage-location erc7201:moda.storage.RegistryV2
+ struct RegistryV2Storage {
/// @dev additional variable to test upgradeability
string _testingUpgradeVariable;
}
// Storage location
- // keccak256(abi.encode(uint256(keccak256("moda.storage.CatalogV2")) - 1)) & ~bytes32(uint256(0xff))
- bytes32 private constant CatalogV2StorageLocation =
+ // keccak256(abi.encode(uint256(keccak256("moda.storage.RegistryV2")) - 1)) & ~bytes32(uint256(0xff))
+ bytes32 private constant RegistryV2StorageLocation =
0xb8760629ab51d65b8fdd44998b12236c513908a4a393c4bcf5d9b3c642fa5700;
- function _getCatalogV2Storage() private pure returns (CatalogV2Storage storage $Version2) {
+ function _getRegistryV2Storage() private pure returns (RegistryV2Storage storage $Version2) {
assembly {
- $Version2.slot := CatalogV2StorageLocation
+ $Version2.slot := RegistryV2StorageLocation
}
}
@@ -31,7 +31,7 @@ contract CatalogV2Mock is Catalog {
* @dev For testing upgradeability
*/
function setTestingUpgradeVariable(string calldata testingUpgradeVariable) external {
- CatalogV2Storage storage $Version2 = _getCatalogV2Storage();
+ RegistryV2Storage storage $Version2 = _getRegistryV2Storage();
$Version2._testingUpgradeVariable = testingUpgradeVariable;
}
@@ -39,7 +39,7 @@ contract CatalogV2Mock is Catalog {
* @dev For testing upgradeability
*/
function getTestingUpgradeVariable() external view returns (string memory) {
- CatalogV2Storage storage $Version2 = _getCatalogV2Storage();
+ RegistryV2Storage storage $Version2 = _getRegistryV2Storage();
return $Version2._testingUpgradeVariable;
}
}
diff --git a/moda-contracts/test/mocks/SplitMainMock.sol b/contracts/test/mocks/SplitMainMock.sol
similarity index 100%
rename from moda-contracts/test/mocks/SplitMainMock.sol
rename to contracts/test/mocks/SplitMainMock.sol
diff --git a/moda-contracts/test/mocks/SplitsFactoryMock.sol b/contracts/test/mocks/SplitsFactoryMock.sol
similarity index 100%
rename from moda-contracts/test/mocks/SplitsFactoryMock.sol
rename to contracts/test/mocks/SplitsFactoryMock.sol
diff --git a/moda-contracts/test/utils/TestUtils.sol b/contracts/test/utils/TestUtils.sol
similarity index 59%
rename from moda-contracts/test/utils/TestUtils.sol
rename to contracts/test/utils/TestUtils.sol
index 87a6f9db..fe76906f 100644
--- a/moda-contracts/test/utils/TestUtils.sol
+++ b/contracts/test/utils/TestUtils.sol
@@ -1,10 +1,10 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
-import "forge-std/Test.sol";
+import {Test, console2} from "forge-std/Test.sol";
import {Wallets} from "./Wallets.sol";
contract TestUtils is Test {
Wallets internal _w = new Wallets();
- address internal _treasury = address(0x123);
+ address payable internal _treasury = payable(address(0x123));
}
diff --git a/moda-contracts/test/utils/Wallets.sol b/contracts/test/utils/Wallets.sol
similarity index 100%
rename from moda-contracts/test/utils/Wallets.sol
rename to contracts/test/utils/Wallets.sol
diff --git a/moda-contracts/lib/forge-std b/moda-contracts/lib/forge-std
deleted file mode 160000
index 2f112697..00000000
--- a/moda-contracts/lib/forge-std
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 2f112697506eab12d433a65fdc31a639548fe365
diff --git a/moda-contracts/lib/openzeppelin-contracts-upgradeable b/moda-contracts/lib/openzeppelin-contracts-upgradeable
deleted file mode 160000
index 4ca003c9..00000000
--- a/moda-contracts/lib/openzeppelin-contracts-upgradeable
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 4ca003c9635d2c16756cf8c9db6760e2d3653dee
diff --git a/moda-contracts/lib/openzeppelin-foundry-upgrades b/moda-contracts/lib/openzeppelin-foundry-upgrades
deleted file mode 160000
index 5d1cbcdb..00000000
--- a/moda-contracts/lib/openzeppelin-foundry-upgrades
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 5d1cbcdbf079071b84dfafd8843ef76d48c57a77
diff --git a/moda-contracts/package.json b/moda-contracts/package.json
deleted file mode 100644
index 60a9b26a..00000000
--- a/moda-contracts/package.json
+++ /dev/null
@@ -1,28 +0,0 @@
-{
- "name": "moda-contracts",
- "version": "1.0.0",
- "description": "",
- "main": "index.js",
- "scripts": {
- "test": "forge clean && forge build && forge test --ffi -vv",
- "coverage": "forge coverage --report lcov --ffi && genhtml lcov.info --branch-coverage --output-dir coverage ",
- "size": "forge build --sizes",
- "contracts:docs": "forge doc -s -p 8080",
- "contracts:install": "forge install",
- "_deploy_Registry": "forge script script/DeployRegistry.s.sol:DeployRegistry --rpc-url rpcUrl --broadcast --verify -vvvv",
- "_deploy_Profile": "forge script script/DeployProfile.s.sol:DeployProfile --rpc-url rpcUrl --broadcast --verify -vvvv",
- "_deploy_Management": "forge script script/DeployManagement.s.sol:DeployManagement --rpc-url rpcUrl --broadcast --verify -vvvv",
- "_deploy_SplitsFactory": "forge script script/DeploySplitsFactory.s.sol:DeploySplitsFactory --rpc-url rpcUrl --broadcast --verify -vvvv",
- "_deploy_CatalogFactory": "forge clean && forge build && forge script script/DeployCatalogFactory.s.sol:DeployCatalogFactory --rpc-url rpcUrl --broadcast --verify -vvvv --ffi",
- "_set_OfficialContracts": "forge script script/SetOfficialContracts.s.sol:SetOfficialContracts --rpc-url rpcUrl --broadcast -vvvv",
- "_deploy_ReleasesFactory": "forge clean && forge build && forge script script/DeployReleasesFactory.s.sol:DeployReleasesFactory --rpc-url rpcUrl --broadcast --verify -vvvv --ffi",
- "_deploy_OpenReleasesFactory": "forge clean && forge build && forge script script/DeployOpenReleasesFactory.s.sol:DeployOpenReleasesFactory --rpc-url rpcUrl --broadcast --verify -vvvv --ffi",
- "1_deploy_Membership": "forge script script/DeployMembership.s.sol:DeployMembership --rpc-url rpcUrl --broadcast --verify -vvvv",
- "2_deploy_Catalog": "forge script script/DeployCatalog.s.sol:DeployCatalog --rpc-url rpcUrl --broadcast -vvvv",
- "add_verified_roles": "node script/utils/add_verified_role/addAddressesToVerify.js"
-
- },
- "keywords": [],
- "author": "",
- "license": "ISC"
-}
diff --git a/moda-contracts/script/DeployCatalog.s.sol b/moda-contracts/script/DeployCatalog.s.sol
deleted file mode 100644
index 9117c82a..00000000
--- a/moda-contracts/script/DeployCatalog.s.sol
+++ /dev/null
@@ -1,30 +0,0 @@
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity ^0.8.21;
-
-import {Script} from "forge-std/Script.sol";
-import {console} from "forge-std/console.sol";
-import {CatalogFactory} from "../src/CatalogFactory.sol";
-import {DeployedContracts} from "./utils/DeployedContracts.sol";
-import {IMembership} from "../src/interfaces/IMembership.sol";
-
-contract DeployCatalog is Script {
- function run() public {
- uint256 privateKey = vm.envUint("DEPLOYER_PRIVATE_KEY");
- vm.startBroadcast(privateKey);
-
- string memory name = vm.envString("CATALOG_NAME");
-
- /// @dev This contract has a few transaction so the CatalogFactory address is not in the first index
- CatalogFactory catalogFactory =
- CatalogFactory(DeployedContracts.getAt("DeployCatalogFactory.s.sol", block.chainid, 3));
- IMembership membership =
- IMembership(DeployedContracts.get("DeployMembership.s.sol", block.chainid));
-
- address catalog = catalogFactory.create(name, membership);
-
- console.logString("Your Catalog was deployed to:");
- console.logAddress(catalog);
-
- vm.stopBroadcast();
- }
-}
diff --git a/moda-contracts/script/DeployCatalogFactory.s.sol b/moda-contracts/script/DeployCatalogFactory.s.sol
deleted file mode 100644
index 258c33be..00000000
--- a/moda-contracts/script/DeployCatalogFactory.s.sol
+++ /dev/null
@@ -1,26 +0,0 @@
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity ^0.8.21;
-
-import {Script, console2} from "forge-std/Script.sol";
-import {CatalogFactory} from "../src/CatalogFactory.sol";
-import {Registry} from "../src/Registry.sol";
-import {Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol";
-import {IBeacon} from "@openzeppelin/contracts/proxy/beacon/IBeacon.sol";
-import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
-import {DeployedContracts} from "./utils/DeployedContracts.sol";
-
-contract DeployCatalogFactory is Script {
- function run() public {
- uint256 privateKey = vm.envUint("DEPLOYER_PRIVATE_KEY");
- address deployer = vm.addr(privateKey);
- vm.startBroadcast(privateKey);
-
- Registry registry = Registry(DeployedContracts.get("DeployRegistry.s.sol", block.chainid));
- IBeacon beacon = IBeacon(Upgrades.deployBeacon("Catalog.sol", deployer));
- CatalogFactory catalog = new CatalogFactory(registry, address(beacon));
-
- registry.grantRole(registry.CATALOG_REGISTRAR_ROLE(), address(catalog));
-
- vm.stopBroadcast();
- }
-}
diff --git a/moda-contracts/script/DeployManagement.s.sol b/moda-contracts/script/DeployManagement.s.sol
deleted file mode 100644
index 21f2a341..00000000
--- a/moda-contracts/script/DeployManagement.s.sol
+++ /dev/null
@@ -1,14 +0,0 @@
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity ^0.8.21;
-
-import {Script, console2} from "forge-std/Script.sol";
-import "../src/Management.sol";
-
-contract DeployManagement is Script {
- function run() public {
- uint256 privateKey = vm.envUint("DEPLOYER_PRIVATE_KEY");
- vm.startBroadcast(privateKey);
- new Management();
- vm.stopBroadcast();
- }
-}
diff --git a/moda-contracts/script/DeployOpenReleasesFactory.s.sol b/moda-contracts/script/DeployOpenReleasesFactory.s.sol
deleted file mode 100644
index b28aace7..00000000
--- a/moda-contracts/script/DeployOpenReleasesFactory.s.sol
+++ /dev/null
@@ -1,24 +0,0 @@
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity ^0.8.21;
-
-import {Script} from "forge-std/Script.sol";
-import {OpenReleasesFactory} from "../src/OpenReleasesFactory.sol";
-import {Registry} from "../src/Registry.sol";
-import {OpenReleases} from "../src/OpenReleases.sol";
-import {Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol";
-import {DeployedContracts} from "./utils/DeployedContracts.sol";
-
-contract DeployOpenReleasesFactory is Script {
- function run() public {
- uint256 privateKey = vm.envUint("DEPLOYER_PRIVATE_KEY");
- vm.startBroadcast(privateKey);
-
- Registry registry = Registry(DeployedContracts.get("DeployRegistry.s.sol", block.chainid));
- OpenReleases openReleases = new OpenReleases();
- OpenReleasesFactory factory = new OpenReleasesFactory(registry, address(openReleases));
-
- registry.grantRole(registry.RELEASES_REGISTRAR_ROLE(), address(factory));
-
- vm.stopBroadcast();
- }
-}
diff --git a/moda-contracts/script/DeployRegistry.s.sol b/moda-contracts/script/DeployRegistry.s.sol
deleted file mode 100644
index 4c4d2b5b..00000000
--- a/moda-contracts/script/DeployRegistry.s.sol
+++ /dev/null
@@ -1,17 +0,0 @@
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity ^0.8.21;
-
-import {Script, console2} from "forge-std/Script.sol";
-import "../src/Registry.sol";
-
-contract DeployRegistry is Script {
- function run() public {
- uint256 privateKey = vm.envUint("DEPLOYER_PRIVATE_KEY");
- uint256 treasuryFee = vm.envUint("TREASURY_FEE");
- address payable treasury = payable(vm.envAddress("TREASURY_ADDRESS"));
-
- vm.startBroadcast(privateKey);
- new Registry(treasury, uint32(treasuryFee));
- vm.stopBroadcast();
- }
-}
diff --git a/moda-contracts/script/SetOfficialContracts.s.sol b/moda-contracts/script/SetOfficialContracts.s.sol
deleted file mode 100644
index 80ae4b96..00000000
--- a/moda-contracts/script/SetOfficialContracts.s.sol
+++ /dev/null
@@ -1,28 +0,0 @@
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity ^0.8.21;
-
-import {Script, console2} from "forge-std/Script.sol";
-import "../src/Registry.sol";
-import {IManagement} from "../src/interfaces/IManagement.sol";
-import {ISplitsFactory} from "../src/interfaces/ISplitsFactory.sol";
-import {DeployedContracts} from "./utils/DeployedContracts.sol";
-import {IOfficialContracts} from "../src/interfaces/Registry/IOfficialContracts.sol";
-
-contract SetOfficialContracts is Script {
- function run() public {
- uint256 privateKey = vm.envUint("DEPLOYER_PRIVATE_KEY");
-
- vm.startBroadcast(privateKey);
-
- Registry registry = Registry(DeployedContracts.get("DeployRegistry.s.sol", block.chainid));
- IManagement management =
- IManagement(DeployedContracts.get("DeployManagement.s.sol", block.chainid));
- ISplitsFactory splitsFactory =
- ISplitsFactory(DeployedContracts.getAt("DeploySplitsFactory.s.sol", block.chainid, 1));
-
- registry.setManagement(management);
- registry.setSplitsFactory(splitsFactory);
-
- vm.stopBroadcast();
- }
-}
diff --git a/moda-contracts/src/Catalog.sol b/moda-contracts/src/Catalog.sol
deleted file mode 100644
index 2bd494b2..00000000
--- a/moda-contracts/src/Catalog.sol
+++ /dev/null
@@ -1,525 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity 0.8.21;
-
-import {ITrackRegistration} from "./interfaces/Catalog/ITrackRegistration.sol";
-import {IReleaseRegistration} from "./interfaces/Releases/IReleaseRegistration.sol";
-import {IReleasesApproval} from "./interfaces/Releases/IReleasesApproval.sol";
-import {IReleasesRegistration} from "./interfaces/Releases/IReleasesRegistration.sol";
-import {IReleases} from "./interfaces/Releases/IReleases.sol";
-import {IOpenReleases} from "./interfaces/Releases/IOpenReleases.sol";
-import {ICatalog} from "./interfaces/Catalog/ICatalog.sol";
-import {IMembership} from "./interfaces/IMembership.sol";
-import {IRegistry} from "./interfaces/Registry/IRegistry.sol";
-import {IManagement} from "./interfaces/IManagement.sol";
-import {IOfficialContracts} from "./interfaces/Registry/IOfficialContracts.sol";
-import {AccessControlUpgradeable} from
- "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
-import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
-
-/// @notice A Catalog is a contract where artists and labels can register tracks.
-/// Membership to the Catalog is controlled by `IMembership`.
-contract Catalog is ICatalog, AccessControlUpgradeable {
- /// @notice an address with AUTO_VERIFIED_ROLE will have their tracks verified on registration
- bytes32 public constant AUTO_VERIFIED_ROLE = keccak256("AUTO_VERIFIED_ROLE");
-
- /// @custom:storage-location erc7201:moda.storage.Catalog
- struct CatalogStorage {
- IRegistry _registry;
- IMembership _membership;
- string _name;
- uint256 _trackCount;
- /// @dev trackRegistrationHash => trackId
- mapping(string => string) _trackIds;
- /// @dev trackId => RegisteredTrack
- mapping(string => RegisteredTrack) _registeredTracks;
- /// @dev trackId => releases => true/false
- mapping(string => mapping(address => bool)) _singleTrackReleasesPermission;
- /// @dev trackOwner => releases => true/false
- mapping(address => mapping(address => bool)) _allTracksReleasesPermission;
- /// @dev releasesOwner => releases
- mapping(address => address) _registeredReleasesContracts;
- /// @dev release => releaseOwner
- mapping(address => address) _registeredReleasesOwners;
- /// @dev releaseHash => RegisteredRelease
- mapping(bytes32 => RegisteredRelease) _registeredReleases;
- /// @dev releases => tokenId => tracks on release
- mapping(address => mapping(uint256 => string[])) _releaseTracks;
- /// @dev releases => tokenId => uri
- mapping(address => mapping(uint256 => string)) _releaseUris;
- }
-
- // Errors
-
- error TrackIsNotRegistered();
- error TrackAlreadyRegistered();
- error TrackIsInvalid();
- error ReleasesContractIsNotRegistered();
- error ReleasesContractIsAlreadyRegistered();
- error ReleasesContractDoesNotHavePermission();
- error ReleaseAlreadyCreated();
- error MembershipRequired();
- error MustBeTrackOwnerOrManager();
- error VerifierRoleRequired();
- error ReleasesRegistrarRoleRequired();
-
- // Storage location
-
- // keccak256(abi.encode(uint256(keccak256("moda.storage.Catalog")) - 1)) & ~bytes32(uint256(0xff))
- bytes32 private constant CatalogStorageLocation =
- 0x29716ba11260d206d72844135e3b7e5c7c3a8e39cde3c7b2b654f553db068900;
-
- function _getCatalogStorage() private pure returns (CatalogStorage storage $) {
- assembly {
- $.slot := CatalogStorageLocation
- }
- }
-
- /**
- * @notice The initializer is disabled when deployed as an implementation contract
- * @custom:oz-upgrades-unsafe-allow constructor
- */
- constructor() {
- _disableInitializers();
- }
-
- // External Functions
-
- /**
- * @notice Initializes the contract
- * @param owner The account that will gain ownership.
- * @param name The name of the Catalog
- * @param registry The top level Registry contract where all Catalogs must register.
- * @param membership A custom contract to gate user access.
- */
- function initialize(
- address owner,
- string calldata name,
- IRegistry registry,
- IMembership membership
- ) external initializer {
- CatalogStorage storage $ = _getCatalogStorage();
- _grantRole(DEFAULT_ADMIN_ROLE, owner);
- $._name = name;
- $._registry = registry;
- $._membership = membership;
- }
-
- /// @inheritdoc ITrackRegistration
- function registerTrack(
- address trackOwner,
- address trackBeneficiary,
- string calldata trackRegistrationHash
- ) external {
- CatalogStorage storage $ = _getCatalogStorage();
-
- _requireTrackIsNotRegistered(trackRegistrationHash);
- _requireMembership(msg.sender);
- _requireTrackWritePermissions(trackOwner, msg.sender);
-
- string memory id = string(
- abi.encodePacked(
- $._name, "-", Strings.toString(block.chainid), "-", Strings.toString($._trackCount)
- )
- );
- $._trackIds[trackRegistrationHash] = id;
-
- bool hasAutoVerification = hasRole(AUTO_VERIFIED_ROLE, msg.sender);
-
- TrackStatus status = hasAutoVerification ? TrackStatus.VALIDATED : TrackStatus.PENDING;
-
- $._registeredTracks[id] = RegisteredTrack(
- status, trackOwner, trackBeneficiary, trackRegistrationHash, "", "", address(0)
- );
- $._trackCount++;
-
- emit TrackRegistered(trackRegistrationHash, id, msg.sender);
- }
-
- /// @inheritdoc ITrackRegistration
- function getTrack(string calldata trackId) external view returns (RegisteredTrack memory) {
- CatalogStorage storage $ = _getCatalogStorage();
-
- return $._registeredTracks[trackId];
- }
-
- /// @inheritdoc ITrackRegistration
- function getTrackId(string calldata trackRegistrationHash) external view returns (string memory) {
- CatalogStorage storage $ = _getCatalogStorage();
-
- return $._trackIds[trackRegistrationHash];
- }
-
- /// @inheritdoc ITrackRegistration
- function setTrackStatus(string calldata trackId, TrackStatus status) external {
- CatalogStorage storage $ = _getCatalogStorage();
-
- _requireVerifierRole(msg.sender);
- _requireTrackIsRegistered(trackId);
-
- RegisteredTrack storage track = $._registeredTracks[trackId];
- track.trackStatus = status;
- track.trackVerifier = msg.sender;
-
- emit TrackUpdated(
- status,
- track.trackOwner,
- track.trackBeneficiary,
- track.trackRegistrationHash,
- track.fingerprintHash,
- track.validationHash,
- msg.sender
- );
- }
-
- /// @inheritdoc ITrackRegistration
- function setTrackBeneficiary(string calldata trackId, address newTrackBeneficiary) external {
- CatalogStorage storage $ = _getCatalogStorage();
-
- _requireTrackIsRegistered(trackId);
- RegisteredTrack storage track = $._registeredTracks[trackId];
- _requireTrackWritePermissions(trackId, msg.sender);
- track.trackBeneficiary = newTrackBeneficiary;
- emit TrackUpdated(
- track.trackStatus,
- track.trackOwner,
- newTrackBeneficiary,
- track.trackRegistrationHash,
- track.fingerprintHash,
- track.validationHash,
- track.trackVerifier
- );
- }
-
- /// @inheritdoc ITrackRegistration
- function setTrackMetadata(
- string calldata trackId,
- string calldata newTrackRegistrationHash
- ) external {
- _requireTrackIsRegistered(trackId);
- CatalogStorage storage $ = _getCatalogStorage();
-
- RegisteredTrack storage track = $._registeredTracks[trackId];
- _requireTrackWritePermissions(trackId, msg.sender);
- track.trackRegistrationHash = newTrackRegistrationHash;
-
- emit TrackUpdated(
- track.trackStatus,
- track.trackOwner,
- track.trackBeneficiary,
- newTrackRegistrationHash,
- track.fingerprintHash,
- track.validationHash,
- track.trackVerifier
- );
- }
-
- /// @inheritdoc ITrackRegistration
- function setTrackFingerprintHash(
- string calldata trackId,
- string calldata fingerprintHash
- ) external {
- CatalogStorage storage $ = _getCatalogStorage();
-
- _requireTrackIsRegistered(trackId);
- RegisteredTrack storage track = $._registeredTracks[trackId];
- _requireTrackWritePermissions(trackId, msg.sender);
- track.fingerprintHash = fingerprintHash;
- emit TrackUpdated(
- track.trackStatus,
- track.trackOwner,
- track.trackBeneficiary,
- track.trackRegistrationHash,
- fingerprintHash,
- track.validationHash,
- track.trackVerifier
- );
- }
-
- /// @inheritdoc ITrackRegistration
- function setTrackValidationHash(string calldata trackId, string calldata validationHash) external {
- CatalogStorage storage $ = _getCatalogStorage();
-
- _requireTrackIsRegistered(trackId);
- RegisteredTrack storage track = $._registeredTracks[trackId];
- _requireTrackWritePermissions(trackId, msg.sender);
- track.validationHash = validationHash;
- emit TrackUpdated(
- track.trackStatus,
- track.trackOwner,
- track.trackBeneficiary,
- track.trackRegistrationHash,
- track.fingerprintHash,
- validationHash,
- track.trackVerifier
- );
- }
-
- /// @inheritdoc IReleasesRegistration
- function registerReleasesContract(address releases, address releasesOwner) external {
- CatalogStorage storage $ = _getCatalogStorage();
-
- if (!hasRole(DEFAULT_ADMIN_ROLE, releasesOwner)) _requireMembership(releasesOwner);
- _requireReleasesRegistrarRole(msg.sender);
- _requireReleasesContractNotRegistered(releases);
-
- $._registeredReleasesContracts[releasesOwner] = releases;
- $._registeredReleasesOwners[releases] = releasesOwner;
- emit ReleasesRegistered(releases, releasesOwner);
- }
-
- /// @inheritdoc IReleasesRegistration
- function unregisterReleasesContract(address releases) external onlyRole(DEFAULT_ADMIN_ROLE) {
- CatalogStorage storage $ = _getCatalogStorage();
-
- _requireReleasesContractIsRegistered(releases);
- address releasesOwner = $._registeredReleasesOwners[releases];
- delete $._registeredReleasesContracts[releasesOwner];
- delete $._registeredReleasesOwners[releases];
- emit ReleasesUnregistered(releases, releasesOwner);
- }
-
- /// @inheritdoc IReleasesRegistration
- function getReleasesOwner(address releases) external view returns (address owner) {
- CatalogStorage storage $ = _getCatalogStorage();
-
- return $._registeredReleasesOwners[releases];
- }
-
- /// @inheritdoc IReleasesRegistration
- function getReleasesContract(address releasesOwner) external view returns (address releases) {
- CatalogStorage storage $ = _getCatalogStorage();
-
- return $._registeredReleasesContracts[releasesOwner];
- }
-
- /// @inheritdoc IReleasesApproval
- function setReleasesApproval(string calldata trackId, address releases, bool hasApproval) external {
- CatalogStorage storage $ = _getCatalogStorage();
-
- _requireTrackWritePermissions(trackId, msg.sender);
- _requireReleasesContractIsRegistered(releases);
- $._singleTrackReleasesPermission[trackId][releases] = hasApproval;
-
- emit TrackApprovalChanged(trackId, releases, hasApproval);
- }
-
- /// @inheritdoc IReleasesApproval
- function setReleasesApprovalForAll(
- address trackOwner,
- address releases,
- bool hasApproval
- ) external {
- CatalogStorage storage $ = _getCatalogStorage();
- _requireTrackWritePermissions(trackOwner, msg.sender);
- _requireReleasesContractIsRegistered(releases);
-
- $._allTracksReleasesPermission[trackOwner][releases] = hasApproval;
-
- emit AllTracksApprovalChanged(trackOwner, releases, hasApproval);
- }
-
- /// @inheritdoc IReleasesApproval
- function getReleasesApproval(
- string calldata trackId,
- address releases
- ) external view returns (bool) {
- CatalogStorage storage $ = _getCatalogStorage();
-
- return $._singleTrackReleasesPermission[trackId][releases];
- }
-
- /// @inheritdoc IReleasesApproval
- function getReleasesApprovalForAll(
- address trackOwner,
- address releases
- ) external view returns (bool) {
- CatalogStorage storage $ = _getCatalogStorage();
-
- return $._allTracksReleasesPermission[trackOwner][releases];
- }
-
- /// @inheritdoc IReleasesApproval
- function hasTrackAccess(string calldata trackId, address caller) external view returns (bool) {
- CatalogStorage storage $ = _getCatalogStorage();
- return $._registeredTracks[trackId].trackOwner == caller
- || IOfficialContracts(address($._registry)).getManagement().isManager(
- $._registeredTracks[trackId].trackOwner, caller
- );
- }
-
- /// @inheritdoc IReleaseRegistration
- function registerRelease(
- string[] calldata trackIds,
- string calldata uri,
- uint256 tokenId
- ) external {
- CatalogStorage storage $ = _getCatalogStorage();
-
- _requireReleasesContractIsRegistered(msg.sender);
- bool isOpen = IOpenReleases(msg.sender).supportsInterface(type(IOpenReleases).interfaceId);
- for (uint256 i = 0; i < trackIds.length; i++) {
- address trackOwner = $._registeredTracks[trackIds[i]].trackOwner;
-
- bool hasFullPermission = $._allTracksReleasesPermission[trackOwner][msg.sender];
- _requireTrackIsRegistered(trackIds[i]);
- _requireTrackIsValid(trackIds[i]);
-
- if (!hasFullPermission && !isOpen) {
- _requireReleasesContractHasPermission(trackIds[i]);
- }
-
- $._releaseTracks[msg.sender][tokenId].push(trackIds[i]);
- }
- $._releaseUris[msg.sender][tokenId] = uri;
- bytes32 releaseHash = _createReleaseHash(trackIds, uri);
- _requireReleaseNotDuplicate(releaseHash);
- $._registeredReleases[releaseHash] = RegisteredRelease(msg.sender, tokenId, trackIds);
- emit ReleaseRegistered(trackIds, msg.sender, tokenId);
- }
-
- /// @inheritdoc IReleaseRegistration
- function unregisterRelease(bytes32 releaseHash) external onlyRole(DEFAULT_ADMIN_ROLE) {
- CatalogStorage storage $ = _getCatalogStorage();
-
- delete $._registeredReleases[releaseHash];
- emit ReleaseUnregistered(releaseHash);
- }
-
- /// @inheritdoc IReleaseRegistration
- function getReleaseTracks(
- address releases,
- uint256 tokenId
- ) external view returns (string[] memory) {
- CatalogStorage storage $ = _getCatalogStorage();
-
- return $._releaseTracks[releases][tokenId];
- }
-
- /// @inheritdoc IReleaseRegistration
- function getReleaseHash(address releases, uint256 tokenId) external view returns (bytes32) {
- CatalogStorage storage $ = _getCatalogStorage();
-
- string[] memory trackIds = $._releaseTracks[releases][tokenId];
- string memory uri = $._releaseUris[releases][tokenId];
- bytes32 releaseHash = _createReleaseHash(trackIds, uri);
- return releaseHash;
- }
-
- /// Public Functions
-
- /// @inheritdoc IReleaseRegistration
- function getRegisteredRelease(bytes32 releaseHash) public view returns (RegisteredRelease memory) {
- CatalogStorage storage $ = _getCatalogStorage();
-
- return $._registeredReleases[releaseHash];
- }
-
- /// Internal Functions
-
- function _requireMembership(address caller) internal view {
- CatalogStorage storage $ = _getCatalogStorage();
-
- if (!$._membership.isMember(caller)) revert MembershipRequired();
- }
-
- function _requireTrackWritePermissions(address trackOwner, address caller) internal view {
- CatalogStorage storage $ = _getCatalogStorage();
-
- if (
- caller != trackOwner
- && !IOfficialContracts(address($._registry)).getManagement().isManager(trackOwner, caller)
- ) {
- revert MustBeTrackOwnerOrManager();
- }
- }
-
- function _requireTrackWritePermissions(string calldata trackId, address caller) internal view {
- _requireTrackWritePermissions(caller, _getCatalogStorage()._registeredTracks[trackId].trackOwner);
- }
-
- function _requireVerifierRole(address account) internal view {
- CatalogStorage storage $ = _getCatalogStorage();
-
- if (!IRegistry($._registry).hasRole(keccak256("VERIFIER_ROLE"), account)) {
- revert VerifierRoleRequired();
- }
- }
-
- function _requireReleasesRegistrarRole(address account) internal view {
- CatalogStorage storage $ = _getCatalogStorage();
-
- if (!IRegistry($._registry).hasRole(keccak256("RELEASES_REGISTRAR_ROLE"), account)) {
- revert ReleasesRegistrarRoleRequired();
- }
- }
-
- function _requireTrackIsRegistered(string calldata trackId) internal view {
- CatalogStorage storage $ = _getCatalogStorage();
-
- if (bytes($._registeredTracks[trackId].trackRegistrationHash).length == 0) {
- revert TrackIsNotRegistered();
- }
- }
-
- function _requireTrackIsNotRegistered(string calldata trackRegistrationHash) internal view {
- CatalogStorage storage $ = _getCatalogStorage();
-
- if (bytes($._trackIds[trackRegistrationHash]).length != 0) {
- revert TrackAlreadyRegistered();
- }
- }
-
- function _requireTrackIsValid(string calldata trackId) internal view {
- CatalogStorage storage $ = _getCatalogStorage();
-
- if ($._registeredTracks[trackId].trackStatus == TrackStatus.INVALIDATED) {
- revert TrackIsInvalid();
- }
- }
-
- function _requireReleasesContractHasPermission(string calldata trackId) internal view {
- CatalogStorage storage $ = _getCatalogStorage();
-
- if (!$._singleTrackReleasesPermission[trackId][msg.sender]) {
- revert ReleasesContractDoesNotHavePermission();
- }
- }
-
- function _requireReleaseNotDuplicate(bytes32 releaseHash) internal view {
- if (getRegisteredRelease(releaseHash).releases != address(0)) {
- revert ReleaseAlreadyCreated();
- }
- }
-
- function _requireReleasesContractIsRegistered(address releases) internal view {
- CatalogStorage storage $ = _getCatalogStorage();
-
- if ($._registeredReleasesOwners[releases] == address(0)) {
- revert ReleasesContractIsNotRegistered();
- }
- }
-
- function _requireReleasesContractNotRegistered(address releases) internal view {
- CatalogStorage storage $ = _getCatalogStorage();
-
- if ($._registeredReleasesOwners[releases] != address(0)) {
- revert ReleasesContractIsAlreadyRegistered();
- }
- }
-
- function _createReleaseHash(
- string[] memory trackIds,
- string memory uri
- ) internal pure returns (bytes32) {
- bytes memory packedHashes = _packStringArray(trackIds);
- return keccak256(abi.encode(packedHashes, uri));
- }
-
- function _packStringArray(string[] memory array) internal pure returns (bytes memory) {
- bytes memory packedBytes = "";
- for (uint256 i = 0; i < array.length; i++) {
- packedBytes = abi.encode(packedBytes, array[i]);
- }
- return packedBytes;
- }
-}
diff --git a/moda-contracts/src/CatalogFactory.sol b/moda-contracts/src/CatalogFactory.sol
deleted file mode 100644
index e947ca6d..00000000
--- a/moda-contracts/src/CatalogFactory.sol
+++ /dev/null
@@ -1,40 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity 0.8.21;
-
-import "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol";
-import {ICatalog} from "./interfaces/Catalog/ICatalog.sol";
-import {ICatalogFactory} from "./interfaces/Catalog/ICatalogFactory.sol";
-import {ICatalogInitialize} from "./interfaces/Catalog/ICatalogInitialize.sol";
-import {IRegistry} from "./interfaces/Registry/IRegistry.sol";
-import {IMembership} from "./interfaces/IMembership.sol";
-
-/**
- * @notice A factory to create Trustless Catalog contracts and to register with the Registry.
- */
-contract CatalogFactory is ICatalogFactory {
- IRegistry public registry;
- address public catalogBeacon;
-
- /**
- * @notice Constructor
- * @param registry_ - The address of the Registry contract, where Catalogs must be registered.
- * @param catalogBeacon_ - The address of the catalog beacon that holds
- * the Catalog implementation address
- */
- constructor(IRegistry registry_, address catalogBeacon_) {
- registry = registry_;
- catalogBeacon = catalogBeacon_;
- }
-
- /**
- * @inheritdoc ICatalogFactory
- */
- function create(string calldata name, IMembership membership) external returns (address) {
- address catalog = address(new BeaconProxy(catalogBeacon, ""));
- ICatalogInitialize(catalog).initialize(msg.sender, name, registry, membership);
- registry.registerCatalog(ICatalog(catalog));
- emit CatalogCreated(msg.sender, catalog, name);
-
- return catalog;
- }
-}
diff --git a/moda-contracts/src/Management.sol b/moda-contracts/src/Management.sol
deleted file mode 100644
index b141503a..00000000
--- a/moda-contracts/src/Management.sol
+++ /dev/null
@@ -1,43 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity 0.8.21;
-
-import {IManagement} from "./interfaces/IManagement.sol";
-import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
-
-/// @notice Management is where users can whitelist other accounts to manage tracks and releases across multiple contracts.
-contract Management is IManagement {
- using EnumerableSet for EnumerableSet.AddressSet;
-
- mapping(address => EnumerableSet.AddressSet) _managers;
-
- /// @inheritdoc IManagement
- function addManagers(address[] calldata managers) external {
- for (uint256 i = 0; i < managers.length; i++) {
- bool added = _managers[msg.sender].add(managers[i]);
- if (added) emit ManagementPermissionSet(msg.sender, managers[i], true);
- }
- }
-
- /// @inheritdoc IManagement
- function removeManagers(address[] calldata managers) external {
- for (uint256 i = 0; i < managers.length; i++) {
- bool removed = _managers[msg.sender].remove(managers[i]);
- if (removed) emit ManagementPermissionSet(msg.sender, managers[i], false);
- }
- }
-
- /// @inheritdoc IManagement
- function getManagerCount(address artist) external view returns (uint256) {
- return _managers[artist].length();
- }
-
- /// @inheritdoc IManagement
- function getManager(address artist, uint256 index) external view returns (address) {
- return _managers[artist].at(index);
- }
-
- /// @inheritdoc IManagement
- function isManager(address artist, address who) external view returns (bool) {
- return _managers[artist].contains(who);
- }
-}
diff --git a/moda-contracts/src/OpenReleases.sol b/moda-contracts/src/OpenReleases.sol
deleted file mode 100644
index 95c78ba4..00000000
--- a/moda-contracts/src/OpenReleases.sol
+++ /dev/null
@@ -1,202 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity 0.8.21;
-
-import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
-import "@openzeppelin/contracts-upgradeable/token/common/ERC2981Upgradeable.sol";
-import "@openzeppelin/contracts-upgradeable/token/ERC1155/extensions/ERC1155SupplyUpgradeable.sol";
-import "@openzeppelin/contracts-upgradeable/token/ERC1155/utils/ERC1155HolderUpgradeable.sol";
-
-import {IOpenReleases} from "./interfaces/Releases/IOpenReleases.sol";
-import {IOpenReleasesInitialize} from "./interfaces/Releases/IOpenReleasesInitialize.sol";
-import {ICatalog} from "./interfaces/Catalog/ICatalog.sol";
-import {ISplitsFactory} from "./interfaces/ISplitsFactory.sol";
-
-/**
- * @notice The contract allows any track owner to be able to create a release.
- */
-contract OpenReleases is
- IOpenReleases,
- IOpenReleasesInitialize,
- ERC1155SupplyUpgradeable,
- ERC1155HolderUpgradeable,
- AccessControlUpgradeable,
- ERC2981Upgradeable
-{
- // State Variables
- uint256 constant MAX_ROYALTY_AMOUNT = 2_000;
-
- ICatalog _catalog;
- ISplitsFactory _splitsFactory;
-
- string public name;
- string public symbol;
-
- uint256 public numberOfReleases;
-
- mapping(uint256 => address) _releaseOwners;
- mapping(uint256 => string) _uris;
-
- // Errors
- error CannotBeZeroAddress();
- error InvalidRoyaltyAmount();
- error FieldCannotBeEmpty(string field);
- error CallerIsNotReleaseAdmin();
- error InvalidTokenId();
- error CallerDoesNotHaveAccess();
-
- /// @custom:oz-upgrades-unsafe-allow constructor
- constructor() {
- _disableInitializers();
- }
-
- /**
- * @notice Initializer
- * @param admin The address of the organization admin
- * @param name_ The name of the token
- * @param symbol_ The symbol of the token
- * @param catalog A contract that implements ICatalog
- * @param splitsFactory A contract that implements ISplitsFactory
- */
- function initialize(
- address admin,
- string memory name_,
- string memory symbol_,
- ICatalog catalog,
- ISplitsFactory splitsFactory
- ) external initializer {
- __ERC1155_init("");
- if (admin == address(0)) revert CannotBeZeroAddress();
- if (bytes(name_).length == 0) revert FieldCannotBeEmpty("name");
- if (bytes(symbol_).length == 0) revert FieldCannotBeEmpty("symbol");
- _grantRole(DEFAULT_ADMIN_ROLE, admin);
-
- name = name_;
- symbol = symbol_;
- _catalog = catalog;
- _splitsFactory = splitsFactory;
- }
-
- // External
-
- /**
- * @dev Creates a new release token and transfers to the receiver
- * @param receiver The address that will receive the release tokens
- * @param royaltyAmount The percentage of sale prices that should be
- * paid to the beneficiary for re-sales. Calculated by / 10,000.
- * e.g. For 10% royalties, pass in 1000
- * @param uri_ The URI of the token metadata
- * @param totalSupply The total amount of tokens to mint
- * @param trackIds An array containing the registered track ids of the tracks
- */
- function create(
- address receiver,
- uint96 royaltyAmount,
- string calldata uri_,
- uint256 totalSupply,
- string[] calldata trackIds
- ) external {
- _requireCallerHasTrackAccess(trackIds);
- if (royaltyAmount > MAX_ROYALTY_AMOUNT) revert InvalidRoyaltyAmount();
-
- numberOfReleases++;
- _uris[numberOfReleases] = uri_;
- _releaseOwners[numberOfReleases] = msg.sender;
-
- ICatalog(_catalog).registerRelease(trackIds, uri_, numberOfReleases);
-
- address[] memory beneficiaries = new address[](trackIds.length);
- for (uint256 i = 0; i < trackIds.length; i++) {
- beneficiaries[i] = ICatalog(_catalog).getTrack(trackIds[i]).trackBeneficiary;
- }
- address split = ISplitsFactory(_splitsFactory).create(beneficiaries);
-
- _setTokenRoyalty(numberOfReleases, split, royaltyAmount);
- _mint(receiver, numberOfReleases, totalSupply, "");
-
- emit ReleaseCreated(numberOfReleases);
- }
-
- /**
- * @dev see {IReleases-setUri}
- */
- function setUri(uint256 tokenId, string calldata uri_) external {
- _requireTokenIdExists(tokenId);
- _requireCallerIsReleaseOwner(tokenId);
- _uris[tokenId] = uri_;
- emit URI(uri_, tokenId);
- }
-
- /**
- * @dev see {IBurnable-burn}
- * @notice This function can be used to completely remove a release, the tokens
- * will be burned and the URI will be removed, only the default admin of the contract
- * can call this function
- */
- function burn(uint256 tokenId) external onlyRole(DEFAULT_ADMIN_ROLE) {
- _requireTokenIdExists(tokenId);
- address owner = _releaseOwners[tokenId];
- uint256 totalSupply = totalSupply(tokenId);
- _burn(owner, tokenId, totalSupply);
- delete _uris[tokenId];
- delete _releaseOwners[tokenId];
- emit Burned(tokenId, owner);
- }
-
- // Public
-
- /**
- * @dev See {IERC1155-uri}.
- */
- function uri(uint256 tokenId) public view override returns (string memory) {
- _requireTokenIdExists(tokenId);
- return _uris[tokenId];
- }
-
- /**
- * @dev See {IERC165-supportsInterface}.
- */
- function supportsInterface(bytes4 interfaceId)
- public
- view
- virtual
- override(
- IERC165,
- ERC1155Upgradeable,
- ERC2981Upgradeable,
- AccessControlUpgradeable,
- ERC1155HolderUpgradeable
- )
- returns (bool)
- {
- return interfaceId == type(IOpenReleases).interfaceId || super.supportsInterface(interfaceId);
- }
-
- // Internal
-
- /**
- * @dev Checks the token id exists
- * @param tokenId The ID of the token
- */
- function _requireTokenIdExists(uint256 tokenId) internal view {
- if (tokenId > numberOfReleases) revert InvalidTokenId();
- }
-
- /**
- * @dev Checks is the caller is registered as having track access, this could
- * be the track owner or a manager
- */
- function _requireCallerHasTrackAccess(string[] memory trackIds) internal view {
- for (uint256 i = 0; i < trackIds.length; i++) {
- if (!ICatalog(_catalog).hasTrackAccess(trackIds[i], msg.sender)) {
- revert CallerDoesNotHaveAccess();
- }
- }
- }
-
- /**
- * @dev Checks the caller is the release owner or the release owner manager
- */
- function _requireCallerIsReleaseOwner(uint256 tokenId) internal view {
- if (msg.sender != _releaseOwners[tokenId]) revert CallerDoesNotHaveAccess();
- }
-}
diff --git a/moda-contracts/src/OpenReleasesFactory.sol b/moda-contracts/src/OpenReleasesFactory.sol
deleted file mode 100644
index c96704fb..00000000
--- a/moda-contracts/src/OpenReleasesFactory.sol
+++ /dev/null
@@ -1,42 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity 0.8.21;
-
-import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol";
-import {IOpenReleasesFactory} from "./interfaces/Releases/IOpenReleasesFactory.sol";
-import {IOpenReleasesInitialize} from "./interfaces/Releases/IOpenReleasesInitialize.sol";
-import {IRegistry} from "./interfaces/Registry/IRegistry.sol";
-import {ICatalog} from "./interfaces/Catalog/ICatalog.sol";
-import {IOfficialContracts} from "./interfaces/Registry/IOfficialContracts.sol";
-import {ISplitsFactory} from "./interfaces/ISplitsFactory.sol";
-
-/**
- * @notice OpenReleasesFactory creates Trustless Release contracts and registers them with a Catalog.
- */
-contract OpenReleasesFactory is IOpenReleasesFactory {
- IOfficialContracts public registry;
- address public releasesMaster;
-
- /**
- * @notice Constructor
- * @param registry_ The registry contract that implements IOfficialContracts
- * @param releasesMaster_ The address of the OpenReleases implementation contract
- */
- constructor(IOfficialContracts registry_, address releasesMaster_) {
- registry = registry_;
- releasesMaster = releasesMaster_;
- }
-
- /**
- * @inheritdoc IOpenReleasesFactory
- */
- function create(string memory name, string memory symbol, ICatalog catalog) external {
- address releasesClone = Clones.clone(releasesMaster);
-
- ISplitsFactory splitsFactory = registry.getSplitsFactory();
- IOpenReleasesInitialize(releasesClone).initialize(
- msg.sender, name, symbol, catalog, ISplitsFactory(splitsFactory)
- );
- catalog.registerReleasesContract(releasesClone, msg.sender);
- emit OpenReleasesCreated(msg.sender, releasesClone, name, symbol);
- }
-}
diff --git a/moda-contracts/src/Registry.sol b/moda-contracts/src/Registry.sol
deleted file mode 100644
index 03a37199..00000000
--- a/moda-contracts/src/Registry.sol
+++ /dev/null
@@ -1,134 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity 0.8.21;
-
-import {IRegistry} from "./interfaces/Registry/IRegistry.sol";
-import {ICatalog} from "./interfaces/Catalog/ICatalog.sol";
-import {IOfficialContracts} from "./interfaces/Registry/IOfficialContracts.sol";
-import {IManagement} from "./interfaces/IManagement.sol";
-import {ISplitsFactory} from "./interfaces/ISplitsFactory.sol";
-import "@openzeppelin/contracts/access/extensions/AccessControlEnumerable.sol";
-
-/**
- * @notice The Registry is a single top-level contract typically managed by a DAO.
- * Catalogs, managed by an organization, must register with the Registry to participate in the ecosystem.
- * The Registry also contains DAO treasury information to receive a portion of sales and royalties.
- */
-contract Registry is IRegistry, IOfficialContracts, AccessControlEnumerable {
- using EnumerableSet for EnumerableSet.AddressSet;
-
- /// @notice The max fee an admin can set. This is calculated as the numerator. e.g. 1_000 / 10_000 = 10%
- uint32 constant MAX_TREASURY_FEE = 1_000;
-
- /// @notice The denominator when determining the treasury fee. e.g. treasuryFee * someValue / FEE_SCALE
- uint32 constant FEE_SCALE = 10_000;
-
- // State Variables
-
- /// @notice only an address with a verifier role can verify a track
- bytes32 public constant VERIFIER_ROLE = keccak256("VERIFIER_ROLE");
-
- /// @notice only an address with a catalog registrar role can register a catalog contract
- bytes32 public constant CATALOG_REGISTRAR_ROLE = keccak256("CATALOG_REGISTRAR_ROLE");
-
- /// @notice only an address with a releases registrar role can register a releases contract
- bytes32 public constant RELEASES_REGISTRAR_ROLE = keccak256("RELEASES_REGISTRAR_ROLE");
-
- address payable _treasury;
- uint32 _treasuryFee;
- ISplitsFactory _splitsFactory;
- IManagement _management;
-
- EnumerableSet.AddressSet _catalogs;
-
- // Errors
-
- error AddressCannotBeZero();
- error CatalogAlreadyRegistered();
- error CatalogIsNotRegistered();
- error CatalogRegistrationFailed();
- error CatalogUnregistrationFailed();
-
- constructor(address payable treasury, uint32 treasuryFee) {
- _treasury = treasury;
- _treasuryFee = treasuryFee;
- _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
- }
-
- /// @inheritdoc IRegistry
- function registerCatalog(ICatalog catalog) external onlyRole(CATALOG_REGISTRAR_ROLE) {
- if (address(catalog) == address(0)) revert AddressCannotBeZero();
- if (_catalogs.contains(address(catalog))) revert CatalogAlreadyRegistered();
-
- if (_catalogs.add(address(catalog))) {
- emit CatalogRegistered(address(catalog), msg.sender);
- return;
- }
-
- revert CatalogRegistrationFailed();
- }
-
- /// @inheritdoc IRegistry
- /// @notice Only a default admin can call this
- function unregisterCatalog(address catalog) external onlyRole(DEFAULT_ADMIN_ROLE) {
- if (!_catalogs.contains(catalog)) revert CatalogIsNotRegistered();
-
- if (_catalogs.remove(catalog)) {
- emit CatalogUnregistered(catalog, msg.sender);
- return;
- }
-
- revert CatalogUnregistrationFailed();
- }
-
- /// @inheritdoc IRegistry
- function isRegisteredCatalog(address catalog) external view returns (bool) {
- return _catalogs.contains(catalog);
- }
-
- /// @inheritdoc IRegistry
- /// @notice The caller must be a default admin
- function setTreasuryFee(uint32 newFee) external onlyRole(DEFAULT_ADMIN_ROLE) {
- require(newFee <= MAX_TREASURY_FEE, "Fee too high");
-
- uint32 oldFee = _treasuryFee;
- _treasuryFee = newFee;
-
- emit TreasuryFeeChanged(oldFee, newFee);
- }
-
- /// @inheritdoc IRegistry
- /// @notice The caller must be a default admin
- function setTreasury(address newTreasury) external onlyRole(DEFAULT_ADMIN_ROLE) {
- if (newTreasury == address(0)) revert AddressCannotBeZero();
- address oldTreasury = _treasury;
- _treasury = payable(newTreasury);
- emit TreasuryChanged(oldTreasury, newTreasury);
- }
-
- /// @inheritdoc IOfficialContracts
- function getTreasuryInfo() external view returns (address, uint32, uint32) {
- return (_treasury, _treasuryFee, FEE_SCALE);
- }
-
- /// @inheritdoc IRegistry
- /// @notice The caller must be a default admin
- function setSplitsFactory(ISplitsFactory splitsFactory) external onlyRole(DEFAULT_ADMIN_ROLE) {
- _splitsFactory = splitsFactory;
- }
-
- /// @inheritdoc IOfficialContracts
- function getSplitsFactory() external view returns (ISplitsFactory) {
- return _splitsFactory;
- }
-
- /// @inheritdoc IRegistry
- /// @notice The caller must be a default admin
- function setManagement(IManagement management) external onlyRole(DEFAULT_ADMIN_ROLE) {
- _management = management;
- }
-
- /// @inheritdoc IOfficialContracts
- function getManagement() external view returns (IManagement) {
- return _management;
- }
-}
diff --git a/moda-contracts/src/Releases.sol b/moda-contracts/src/Releases.sol
deleted file mode 100644
index 19c0439a..00000000
--- a/moda-contracts/src/Releases.sol
+++ /dev/null
@@ -1,197 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity 0.8.21;
-
-import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
-import "@openzeppelin/contracts-upgradeable/token/common/ERC2981Upgradeable.sol";
-import "@openzeppelin/contracts-upgradeable/token/ERC1155/extensions/ERC1155SupplyUpgradeable.sol";
-import "@openzeppelin/contracts-upgradeable/token/ERC1155/utils/ERC1155HolderUpgradeable.sol";
-
-import {IReleases} from "./interfaces/Releases/IReleases.sol";
-import {IReleasesInitialize} from "./interfaces/Releases/IReleasesInitialize.sol";
-import {IWithdrawRelease} from "./interfaces/Releases/IWithdrawRelease.sol";
-import {ICatalog} from "./interfaces/Catalog/ICatalog.sol";
-import {ISplitsFactory} from "./interfaces/ISplitsFactory.sol";
-
-/**
- * @notice Releases is a contract to allow artists or labels to create track or multiple
- * track tokens called a "Release". `Releases` are registered with a Catalog, and can only create a
- * Release of tracks that have also been registered in the Catalog.
- */
-contract Releases is
- IReleases,
- IReleasesInitialize,
- IWithdrawRelease,
- ERC1155SupplyUpgradeable,
- ERC1155HolderUpgradeable,
- AccessControlUpgradeable,
- ERC2981Upgradeable
-{
- // State Variables
-
- uint256 constant MAX_ROYALTY_AMOUNT = 2_000;
-
- ICatalog _catalog;
-
- ISplitsFactory _splitsFactory;
-
- string public name;
- string public symbol;
-
- uint256 public numberOfReleases;
-
- mapping(uint256 => string) _uris;
-
- /// @dev users with a Releases admin role can create releases with a curated contract
- bytes32 public constant RELEASE_ADMIN_ROLE = keccak256("RELEASE_ADMIN_ROLE");
-
- // Errors
- error CannotBeZeroAddress();
- error InvalidRoyaltyAmount();
- error FieldCannotBeEmpty(string field);
- error CallerIsNotReleaseAdmin();
- error InvalidTokenId();
- error CallerDoesNotHaveTrackAccess();
-
- /**
- * @dev Constructor
- * @notice The initializer is disabled when deployed as an implementation contract
- */
- constructor() {
- _disableInitializers();
- }
-
- // External Functions
-
- /**
- * @dev Initializes the contract
- * @param admin - The address that will be given the role of default admin. See {AccessControl}
- * @param releaseAdmins - An array of addresses that will be given the role of release admin
- * @param name_ - The name of the Releases contract
- * @param symbol_ - The symbol of the Releases contract
- * @param catalog - A contract that implements ICatalog
- * @param splitsFactory - A contract that implements ISplitsFactory
- */
- function initialize(
- address admin,
- address[] calldata releaseAdmins,
- string calldata name_,
- string calldata symbol_,
- ICatalog catalog,
- ISplitsFactory splitsFactory
- ) external initializer {
- __ERC1155_init("");
- if (admin == address(0)) revert CannotBeZeroAddress();
- if (bytes(name_).length == 0) revert FieldCannotBeEmpty("name");
- if (bytes(symbol_).length == 0) revert FieldCannotBeEmpty("symbol");
- _grantRole(DEFAULT_ADMIN_ROLE, admin);
- for (uint8 i = 0; i < releaseAdmins.length; i++) {
- _grantRole(RELEASE_ADMIN_ROLE, releaseAdmins[i]);
- }
- name = name_;
- symbol = symbol_;
- _catalog = catalog;
- _splitsFactory = splitsFactory;
- }
-
- /**
- * @dev Creates a new release token and transfers to the receiver
- * @notice Only release admins can call this function
- * @param receiver - The address that will receive the release tokens
- * @param royaltyAmount - The percentage of sale prices that should be
- * paid to the beneficiary for re-sales. Calculated by / 10,000.
- * e.g. For 10% royalties, pass in 1000
- * @param uri_ - The URI of the token metadata
- * @param totalSupply - The total amount of tokens to mint
- * @param trackIds - An array containing the registered track ids of the tracks
- */
- function create(
- address receiver,
- uint96 royaltyAmount,
- string calldata uri_,
- uint256 totalSupply,
- string[] calldata trackIds
- ) external {
- if (!hasRole(RELEASE_ADMIN_ROLE, msg.sender)) revert CallerIsNotReleaseAdmin();
- if (royaltyAmount > MAX_ROYALTY_AMOUNT) revert InvalidRoyaltyAmount();
-
- numberOfReleases++;
- _uris[numberOfReleases] = uri_;
-
- ICatalog(_catalog).registerRelease(trackIds, uri_, numberOfReleases);
-
- address[] memory beneficiaries = new address[](trackIds.length);
- for (uint256 i = 0; i < trackIds.length; i++) {
- beneficiaries[i] = ICatalog(_catalog).getTrack(trackIds[i]).trackBeneficiary;
- }
- address split = ISplitsFactory(_splitsFactory).create(beneficiaries);
-
- _setTokenRoyalty(numberOfReleases, split, royaltyAmount);
- _mint(receiver, numberOfReleases, totalSupply, "");
-
- emit ReleaseCreated(numberOfReleases);
- }
-
- /**
- * @inheritdoc IWithdrawRelease
- * @notice Only release admins can call this function
- * @notice This function bypasses the approval check, the
- * contract does not need to set approval to the receiver.
- */
- function withdrawRelease(
- address receiver,
- uint256 tokenId,
- uint256 amount
- ) external onlyRole(RELEASE_ADMIN_ROLE) {
- _requireTokenIdExists(tokenId);
- _safeTransferFrom(address(this), receiver, tokenId, amount, "");
- emit ReleaseWithdrawn(msg.sender, tokenId, amount);
- }
-
- /**
- * @inheritdoc IReleases
- */
- function setUri(uint256 tokenId, string calldata uri_) external onlyRole(DEFAULT_ADMIN_ROLE) {
- _requireTokenIdExists(tokenId);
- _uris[tokenId] = uri_;
- emit URI(uri_, tokenId);
- }
-
- // Public Functions
-
- /**
- * @dev See {IERC1155-uri}.
- */
- function uri(uint256 tokenId) public view override returns (string memory) {
- _requireTokenIdExists(tokenId);
- return _uris[tokenId];
- }
-
- /**
- * @dev See {IERC165-supportsInterface}.
- */
- function supportsInterface(bytes4 interfaceId)
- public
- view
- virtual
- override(
- IERC165,
- ERC1155Upgradeable,
- ERC2981Upgradeable,
- AccessControlUpgradeable,
- ERC1155HolderUpgradeable
- )
- returns (bool)
- {
- return interfaceId == type(IReleases).interfaceId || super.supportsInterface(interfaceId);
- }
-
- // Internal
-
- /**
- * @dev Checks the token id exists
- * @param tokenId The ID of the token
- */
- function _requireTokenIdExists(uint256 tokenId) internal view {
- if (tokenId > numberOfReleases) revert InvalidTokenId();
- }
-}
diff --git a/moda-contracts/src/interfaces/Catalog/ICatalog.sol b/moda-contracts/src/interfaces/Catalog/ICatalog.sol
deleted file mode 100644
index 5afabc30..00000000
--- a/moda-contracts/src/interfaces/Catalog/ICatalog.sol
+++ /dev/null
@@ -1,16 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity 0.8.21;
-
-import {ITrackRegistration} from "./ITrackRegistration.sol";
-import {IReleaseRegistration} from "../Releases/IReleaseRegistration.sol";
-import {IReleasesApproval} from "../Releases/IReleasesApproval.sol";
-import {IReleasesRegistration} from "../Releases/IReleasesRegistration.sol";
-import {IReleases} from "../Releases/IReleases.sol";
-
-/// @notice A contract deployed by an organization where artists and labels can register music.
-interface ICatalog is
- ITrackRegistration,
- IReleasesRegistration,
- IReleaseRegistration,
- IReleasesApproval
-{}
diff --git a/moda-contracts/src/interfaces/Catalog/ICatalogFactory.sol b/moda-contracts/src/interfaces/Catalog/ICatalogFactory.sol
deleted file mode 100644
index 41130958..00000000
--- a/moda-contracts/src/interfaces/Catalog/ICatalogFactory.sol
+++ /dev/null
@@ -1,16 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity 0.8.21;
-
-import {IMembership} from "../IMembership.sol";
-
-interface ICatalogFactory {
- event CatalogCreated(address indexed owner, address indexed catalog, string name);
-
- /**
- * @dev Creates a new Catalog contract
- * @param name The name of the Catalog contract
- * @param membership A contract that implements `IMembership`.
- * `IMembership` can be used to gate users based on rules that you define.
- */
- function create(string calldata name, IMembership membership) external returns (address);
-}
diff --git a/moda-contracts/src/interfaces/Catalog/ICatalogInitialize.sol b/moda-contracts/src/interfaces/Catalog/ICatalogInitialize.sol
deleted file mode 100644
index 317341f4..00000000
--- a/moda-contracts/src/interfaces/Catalog/ICatalogInitialize.sol
+++ /dev/null
@@ -1,23 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity 0.8.21;
-
-import {IMembership} from "../IMembership.sol";
-import {IRegistry} from "../Registry/IRegistry.sol";
-
-interface ICatalogInitialize {
- /**
- * @dev Initializes a Catalog, called by the CatalogFactory when
- * a new catalog is created to set the initial state
- * @param owner The account with admin power
- * @param name The name of the Catalog contract
- * @param registry The address of the Registry contract where all released tracks are published
- * @param membership A contract that implements the `IMembership` interface.
- * This can be used to gate who can use your contracts.
- */
- function initialize(
- address owner,
- string calldata name,
- IRegistry registry,
- IMembership membership
- ) external;
-}
diff --git a/moda-contracts/src/interfaces/Catalog/ITrackRegistration.sol b/moda-contracts/src/interfaces/Catalog/ITrackRegistration.sol
deleted file mode 100644
index 00acfc90..00000000
--- a/moda-contracts/src/interfaces/Catalog/ITrackRegistration.sol
+++ /dev/null
@@ -1,129 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity 0.8.21;
-
-interface ITrackRegistration {
- /// @notice Emitted when a track is registered
- event TrackRegistered(string trackRegistrationHash, string trackId, address indexed trackOwner);
-
- /**
- * @notice Emitted whenever the track registration hash is updated
- * as the registration hash is the IPFS hash of the metadata,
- * this is emitted when calling updateTrackMetadata
- */
- event TrackRegistrationHashUpdated(
- string trackId, string newTrackRegistrationHash, address indexed trackOwner
- );
-
- /// @notice Emitted whenever a field in RegisterTrack is updated
- event TrackUpdated(
- TrackStatus indexed trackStatus,
- address indexed trackOwner,
- address trackBeneficiary,
- string trackRegistrationHash,
- string fingerprintHash,
- string validationHash,
- address trackVerifier
- );
-
- /**
- * @notice The registration state of the track
- * @notice `PENDING` A track was registered, but is pending approval. This will be the initial state for anyone registering without the `GOLD_ROLE`.
- * @notice `VALIDATED` After a track has been submitted and is approved by a privileged account it will be `VALIDATED`.
- * @notice `INVALIDATED` If a track submission is fraudulent or invalid and has been rejected by a privileged account it will be `INVALIDATED`.
- */
- enum TrackStatus {
- PENDING,
- VALIDATED,
- INVALIDATED
- }
-
- /**
- * @notice Represents a registered track
- * @param trackStatus The registration state of the track (pending, validated, invalidated)
- * @param trackOwner The account that owns the rights to a track.
- * This is usually the artist address, but could be a contract.
- * @param TrackRegistrationHash This is a IPFS hash of the track metadata
- * @param FingerprintHash This is a IPFS hash of the track fingerprint
- * @param ValidationHash This is a hash of proof of validation data
- * @param TrackVerifier The account that validated the track
- */
- struct RegisteredTrack {
- TrackStatus trackStatus;
- address trackOwner;
- address trackBeneficiary;
- string trackRegistrationHash;
- string fingerprintHash;
- string validationHash;
- address trackVerifier;
- }
-
- /**
- * @notice Registers a track using the IPFS hash of the track metadata.
- * Track status is set to pending by default unless the user has the GOLD_ROLE.
- * The Track can be registered by the trackOwner or a manager.
- * @param trackOwner - The address of the owner of the track
- * @param trackRegistrationHash - The registration hash of the track
- * @param trackBeneficiary - The beneficiary of the track (could be single or an 0xSplit)
- */
- function registerTrack(
- address trackOwner,
- address trackBeneficiary,
- string calldata trackRegistrationHash
- ) external;
-
- /**
- * @notice Returns all registered track data
- * @param trackId - The id of the track
- */
- function getTrack(string calldata trackId) external view returns (RegisteredTrack memory);
-
- /**
- * @notice Returns the track id for a track
- * @param trackRegistrationHash - The registration hash of the track
- */
- function getTrackId(string calldata trackRegistrationHash) external view returns (string memory);
-
- /**
- * @notice Sets the track status for a track
- * @notice Only a caller with the Verifier role can call this function
- * @param trackId - The id of the track
- * @param status - The new status of the track
- */
- function setTrackStatus(string calldata trackId, TrackStatus status) external;
-
- /**
- * @notice Sets the track beneficiary for a track
- * @param trackId - The id of the track
- * @param newTrackBeneficiary - The new beneficiary of the track
- */
- function setTrackBeneficiary(string calldata trackId, address newTrackBeneficiary) external;
-
- /**
- * @notice Sets the track fingerprint hash for a track
- * @param trackId - The id of the track
- * @param fingerprintHash - The new fingerprint hash of the track
- */
- function setTrackFingerprintHash(
- string calldata trackId,
- string calldata fingerprintHash
- ) external;
-
- /**
- * @notice Sets the track validation hash for a track
- * @param trackId - The id of the track
- * @param validationHash - The new validation hash of the track
- */
- function setTrackValidationHash(string calldata trackId, string calldata validationHash) external;
-
- /**
- * @notice Sets the track uri
- * @notice Only the track owner can call this function
- * @param trackId - The id of the track
- * @param newTrackRegistrationHash - The new registration hash of the track
- * i.e. the updated IPFS hash of the track metadata
- */
- function setTrackMetadata(
- string calldata trackId,
- string calldata newTrackRegistrationHash
- ) external;
-}
diff --git a/moda-contracts/src/interfaces/IManagement.sol b/moda-contracts/src/interfaces/IManagement.sol
deleted file mode 100644
index 70f83eef..00000000
--- a/moda-contracts/src/interfaces/IManagement.sol
+++ /dev/null
@@ -1,39 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity 0.8.21;
-
-interface IManagement {
- /// @notice Emitted when a manager is added or removed by an artist
- event ManagementPermissionSet(address indexed artist, address indexed manager, bool isManager);
-
- /**
- * @notice Grants a manager role to a list of addresses for a particular artist caller
- * @param managers The addresses of the managers
- */
- function addManagers(address[] calldata managers) external;
-
- /**
- * @notice Revokes a manager role to a list of addresses for a particular artist caller
- * @param managers The addresses of the managers
- */
- function removeManagers(address[] calldata managers) external;
-
- /**
- * @notice Returns the total number of managers for a particular artist
- * @param artist The address of the artist
- */
- function getManagerCount(address artist) external view returns (uint256);
-
- /**
- * @notice Returns a manager at a particular index for a particular artist
- * @param artist The address of the artist
- * @param index The index of the manager
- */
- function getManager(address artist, uint256 index) external view returns (address);
-
- /**
- * @notice Returns true if the address is a manager for a particular artist
- * @param artist The address of the artist
- * @param who The address of the manager
- */
- function isManager(address artist, address who) external view returns (bool);
-}
diff --git a/moda-contracts/src/interfaces/Registry/IOfficialContracts.sol b/moda-contracts/src/interfaces/Registry/IOfficialContracts.sol
deleted file mode 100644
index e36a6654..00000000
--- a/moda-contracts/src/interfaces/Registry/IOfficialContracts.sol
+++ /dev/null
@@ -1,24 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity 0.8.21;
-
-import {IManagement} from "../IManagement.sol";
-import {ISplitsFactory} from "../ISplitsFactory.sol";
-
-/// @notice A lightweight interface for retrieving contract addresses.
-interface IOfficialContracts {
- /**
- * @notice Returns the address of the Treasury, a suggested percentage fee, and the fee scale.
- * e.g. If the percentage fee is `1_000` and the fee scale is `10_000`, then `(1_000 / 10_000) * 100 = 10%`
- */
- function getTreasuryInfo() external view returns (address, uint32, uint32);
-
- /**
- * @notice Returns the address of the Splits Factory
- */
- function getSplitsFactory() external view returns (ISplitsFactory);
-
- /**
- * @notice Returns the address of the Management contract
- */
- function getManagement() external view returns (IManagement);
-}
diff --git a/moda-contracts/src/interfaces/Registry/IRegistry.sol b/moda-contracts/src/interfaces/Registry/IRegistry.sol
deleted file mode 100644
index 4e40363a..00000000
--- a/moda-contracts/src/interfaces/Registry/IRegistry.sol
+++ /dev/null
@@ -1,69 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity 0.8.21;
-
-import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol";
-import {IManagement} from "../IManagement.sol";
-import {ISplitsFactory} from "../ISplitsFactory.sol";
-import {ICatalog} from "../Catalog/ICatalog.sol";
-
-/// @notice IRegistry defines the interface of the top level contract where all catalogs are registered.
-interface IRegistry is IAccessControl {
- /// @notice Emitted when a catalog is registered for an organization
- event CatalogRegistered(address indexed catalog, address registrar);
-
- /// @notice Emitted when a catalog is unregistered
- event CatalogUnregistered(address indexed catalog, address registrar);
-
- /// @notice Emitted when the treasury fee is changed by a default admin
- event TreasuryFeeChanged(uint32 oldFee, uint32 newFee);
-
- /// @notice Emitted when the treasury address is changed by a default admin
- event TreasuryChanged(address oldTreasury, address newTreasury);
-
- // Catalogs
-
- /**
- * @notice Register a new catalog. Only a default admin can call this.
- * @param catalog A contract that implements the ICatalog interface
- */
- function registerCatalog(ICatalog catalog) external;
-
- /**
- * @notice Unregisters a deprecated or malicious catalog
- * @param catalog The address of the catalog
- */
- function unregisterCatalog(address catalog) external;
-
- /**
- * @notice Check if a catalog is registered
- * @param catalog The address of a catalog
- */
- function isRegisteredCatalog(address catalog) external view returns (bool);
-
- // Treasury
-
- /**
- * @notice Sets the numerator for calculating the percentage of fees to be sent to the Treasury
- * @notice TreasuryFee is based on a denominator of 10,000 (e.g. 1000 is 10.0%). Max of 10%
- * @param fee The percentage of fees to be sent to the Treasury
- */
- function setTreasuryFee(uint32 fee) external;
-
- /**
- * @notice Sets the address of the Treasury
- * @param treasury The address of theTreasury
- */
- function setTreasury(address treasury) external;
-
- /**
- * @notice Sets a splits factory
- * @param splitsFactory A contract that implements ISplitsFactory
- */
- function setSplitsFactory(ISplitsFactory splitsFactory) external;
-
- /**
- * @notice Sets the address of the management contract. The caller must be a default admin.
- * @param management A contract that implements the IManagement interface
- */
- function setManagement(IManagement management) external;
-}
diff --git a/moda-contracts/src/interfaces/Releases/IOpenReleases.sol b/moda-contracts/src/interfaces/Releases/IOpenReleases.sol
deleted file mode 100644
index 69dd785a..00000000
--- a/moda-contracts/src/interfaces/Releases/IOpenReleases.sol
+++ /dev/null
@@ -1,11 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity 0.8.21;
-
-import {IReleases} from "./IReleases.sol";
-import {IBurnable} from "./IBurnable.sol";
-
-/**
- * @notice IOpenReleases is "open" for anyone to release a drop. It contains `IBurnable`
- * functionality for privileged accounts to remove malicious tokens.
- */
-interface IOpenReleases is IReleases, IBurnable {}
diff --git a/moda-contracts/src/interfaces/Releases/IOpenReleasesFactory.sol b/moda-contracts/src/interfaces/Releases/IOpenReleasesFactory.sol
deleted file mode 100644
index 2c4286f3..00000000
--- a/moda-contracts/src/interfaces/Releases/IOpenReleasesFactory.sol
+++ /dev/null
@@ -1,26 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity 0.8.21;
-
-import {ICatalog} from "../Catalog/ICatalog.sol";
-
-/**
- * @notice IOpenReleasesFactory defines an interface for a factory contract that deploys trustless OpenReleases contracts.
- * OpenReleases are permission-less contract for releasing tracks. They are generally deployed by an organization for a Catalog.
- */
-interface IOpenReleasesFactory {
- /**
- * @notice Emitted when a new open releases contract is created
- */
- event OpenReleasesCreated(
- address indexed releasesOwner, address indexed releases, string name, string symbol
- );
-
- /**
- * @notice Creates a new open releases contract. The caller becomes
- * the owner and default admin of the new releases contract.
- * @param name The name of the releases contract
- * @param symbol The symbol of the releases contract
- * @param catalog A contract that implements ICatalog
- */
- function create(string memory name, string memory symbol, ICatalog catalog) external;
-}
diff --git a/moda-contracts/src/interfaces/Releases/IOpenReleasesInitialize.sol b/moda-contracts/src/interfaces/Releases/IOpenReleasesInitialize.sol
deleted file mode 100644
index 33390072..00000000
--- a/moda-contracts/src/interfaces/Releases/IOpenReleasesInitialize.sol
+++ /dev/null
@@ -1,23 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity 0.8.21;
-
-import {ICatalog} from "../Catalog/ICatalog.sol";
-import {ISplitsFactory} from "../ISplitsFactory.sol";
-
-interface IOpenReleasesInitialize {
- /**
- * @notice Initializes the contract
- * @param admin The address that will be given the role of default admin. See {AccessControl}
- * @param name_ The name of the Releases contract
- * @param symbol_ The symbol of the Releases contract
- * @param catalog A contract that implements ICatalog
- * @param splitsFactory A contract that implements ISplitsFactory
- */
- function initialize(
- address admin,
- string memory name_,
- string memory symbol_,
- ICatalog catalog,
- ISplitsFactory splitsFactory
- ) external;
-}
diff --git a/moda-contracts/src/interfaces/Releases/IReleaseRegistration.sol b/moda-contracts/src/interfaces/Releases/IReleaseRegistration.sol
deleted file mode 100644
index 21120664..00000000
--- a/moda-contracts/src/interfaces/Releases/IReleaseRegistration.sol
+++ /dev/null
@@ -1,78 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity 0.8.21;
-
-/**
- * @notice This interface defines a lightweight version of the required functionality to register releases
- * when a release is successfully created.
- */
-interface IReleaseRegistration {
- /**
- * @notice Emitted when a release is registered, initiated by the Releases contract
- * when a release is successfully created
- */
- event ReleaseRegistered(string[] trackIds, address releases, uint256 tokenId);
-
- /// @notice Emitted when a release is unregistered
- event ReleaseUnregistered(bytes32 releaseHash);
-
- /**
- * @notice Represents a registered release.
- * @param releases The address of the Releases contract from which the tracks were released
- * @param TokenId The token id of the release
- * @param TrackIds The ids of the tracks in the release
- */
- struct RegisteredRelease {
- address releases;
- uint256 tokenId;
- string[] trackIds;
- }
-
- /**
- * @notice Registers a release. In order for a release to be registered
- * the Releases contract must be registered, the tracks must be registered,
- * the Releases contract must have the track owners permission to release the track,
- * and this exact combination of ordered tracks and metadata uri must not have been
- * registered previously.
- * @param trackIds The track ids of the tracks
- * @param uri The metadata uri of the release
- * @param tokenId The token id of the release
- */
- function registerRelease(
- string[] calldata trackIds,
- string calldata uri,
- uint256 tokenId
- ) external;
-
- /**
- * @notice Unregisters a releases. This deletes a release hash enabling the release
- * to be created again. Only the default admin can call this method.
- * @param releaseHash The hash of the release
- */
- function unregisterRelease(bytes32 releaseHash) external;
-
- /**
- * @notice Returns the release hash for a release
- * @param releases The address of the Releases contract contract
- * @param tokenId The token id of the release
- */
- function getReleaseHash(address releases, uint256 tokenId) external view returns (bytes32);
-
- /**
- * @notice Returns a registered release
- * @param releaseHash The hash of the release
- */
- function getRegisteredRelease(bytes32 releaseHash)
- external
- view
- returns (RegisteredRelease memory);
-
- /**
- * @notice Returns an array of track ids for a given token id and releases address
- * @param releases The address of the Releases contract contract
- * @param tokenId The token id of the release
- */
- function getReleaseTracks(
- address releases,
- uint256 tokenId
- ) external view returns (string[] memory);
-}
diff --git a/moda-contracts/src/interfaces/Releases/IReleasesApproval.sol b/moda-contracts/src/interfaces/Releases/IReleasesApproval.sol
deleted file mode 100644
index 5938a3ed..00000000
--- a/moda-contracts/src/interfaces/Releases/IReleasesApproval.sol
+++ /dev/null
@@ -1,62 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity 0.8.21;
-
-/**
- * @notice A lightweight interface for approving other accounts to create releases with your track.
- */
-interface IReleasesApproval {
- /**
- * @notice Emitted when a track owner has changed permissions for a
- * releases contract to be able to release their track
- */
- event TrackApprovalChanged(string trackId, address indexed releases, bool hasApproval);
-
- /**
- * @notice Emitted when a track owner has given permission to a
- * releases contract to be able to release all their tracks
- */
- event AllTracksApprovalChanged(address indexed artist, address indexed releases, bool hasApproval);
-
- /**
- * @notice Add or revoke approval for a Releases contract to release a track
- * @param trackId - The ID of the track
- * @param releases - The address of the Releases contract
- * @param hasApproval - The approval to set true/false
- */
- function setReleasesApproval(string calldata trackId, address releases, bool hasApproval) external;
-
- /**
- * @notice Sets approval for a Releases contract to be able to release
- * all tracks owned by the artist
- * @param artist - The address of the artist
- * @param releases - The address of the Releases contract
- * @param hasApproval - The approval to set true/false
- */
- function setReleasesApprovalForAll(address artist, address releases, bool hasApproval) external;
-
- /**
- * @notice Returns true/false if a Releases contract has approval
- * to release a track
- * @param trackId - The track id of the track
- * @param releases - The address of the Releases contract
- */
- function getReleasesApproval(
- string calldata trackId,
- address releases
- ) external view returns (bool);
-
- /**
- * @notice Returns true/false if a Releases contract has approval
- * to release all tracks owned by the caller
- * @param artist - The address of the artist
- * @param releases - The address of the Releases contract
- */
- function getReleasesApprovalForAll(address artist, address releases) external view returns (bool);
-
- /**
- * @notice Checks the caller has permission to release a track through an open Releases contract.
- * @param trackId The id of the track
- * @param caller The address of the caller
- */
- function hasTrackAccess(string calldata trackId, address caller) external view returns (bool);
-}
diff --git a/moda-contracts/src/interfaces/Releases/IReleasesInitialize.sol b/moda-contracts/src/interfaces/Releases/IReleasesInitialize.sol
deleted file mode 100644
index b78ca290..00000000
--- a/moda-contracts/src/interfaces/Releases/IReleasesInitialize.sol
+++ /dev/null
@@ -1,25 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity 0.8.21;
-
-import {ICatalog} from "../Catalog/ICatalog.sol";
-import {ISplitsFactory} from "../ISplitsFactory.sol";
-
-interface IReleasesInitialize {
- /**
- * @notice Initializes the contract
- * @param admin The address that will be given the role of default admin. See {AccessControl}
- * @param releaseAdmins An array of addresses that will be given the role of release admin
- * @param name_ The name of the Releases contract
- * @param symbol_ The symbol of the Releases contract
- * @param catalog A contract that implements ICatalog
- * @param splitsFactory A contract that implements ISplitsFactory
- */
- function initialize(
- address admin,
- address[] memory releaseAdmins,
- string memory name_,
- string memory symbol_,
- ICatalog catalog,
- ISplitsFactory splitsFactory
- ) external;
-}
diff --git a/moda-contracts/src/interfaces/Releases/IReleasesRegistration.sol b/moda-contracts/src/interfaces/Releases/IReleasesRegistration.sol
deleted file mode 100644
index 95a0cd11..00000000
--- a/moda-contracts/src/interfaces/Releases/IReleasesRegistration.sol
+++ /dev/null
@@ -1,39 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity 0.8.21;
-
-interface IReleasesRegistration {
- /// @notice Emitted when a Releases contract is registered
- event ReleasesRegistered(address releases, address indexed releasesOwner);
-
- /// @notice Emitted when a Releases contract is unregistered
- event ReleasesUnregistered(address releases, address indexed releasesOwner);
-
- /**
- * @notice Registers a Releases contract, a release can only be created from registered
- * Releases contract and only tokens from registered Releases contract can be
- * listed on the marketplace.
- * @notice Only the factory can call this function
- * @param releases The address of the Releases contract
- * @param releasesOwner The owner of the Releases contract
- */
- function registerReleasesContract(address releases, address releasesOwner) external;
-
- /**
- * @notice Unregisters a Releases Contract. This will make the Releases contract
- * unable to create releases.
- * @param releases The address of the Releases contract
- */
- function unregisterReleasesContract(address releases) external;
-
- /**
- * @notice Returns the owner of a Releases contract
- * @param releases The address of the Releases contract
- */
- function getReleasesOwner(address releases) external view returns (address owner);
-
- /**
- * @notice Returns the Releases contract of an owner
- * @param releasesOwner The owner of the Releases contract
- */
- function getReleasesContract(address releasesOwner) external view returns (address releases);
-}
diff --git a/moda-contracts/test/Catalog-Upgrades.t.sol b/moda-contracts/test/Catalog-Upgrades.t.sol
deleted file mode 100644
index 9e38a65f..00000000
--- a/moda-contracts/test/Catalog-Upgrades.t.sol
+++ /dev/null
@@ -1,67 +0,0 @@
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity 0.8.21;
-
-import {Test, console2} from "forge-std/Test.sol";
-import "../src/Catalog.sol";
-import "../src/interfaces/IMembership.sol";
-import "../src/interfaces/ISplitsFactory.sol";
-import "../test/mocks/CatalogV2Mock.sol";
-import "../src/Registry.sol";
-import "../src/Management.sol";
-import {Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol";
-import {IBeacon} from "@openzeppelin/contracts/proxy/beacon/IBeacon.sol";
-
-contract CatalogUpgradesTest is Test {
- Management public management;
- Registry public registry;
- address public catalogImplementation;
- address public catalogImplementationV2;
- address public catalogProxy;
- address public beacon;
-
- string public catalogName = "Test";
- uint256 public chainId = 0;
- ISplitsFactory public splitsFactory = ISplitsFactory(address(0x1));
- address payable public treasuryAddress = payable(address(0x2));
- address public catalogDeployer = address(0x3);
- IMembership public membership = IMembership(address(0x4));
-
- address admin = address(0x4);
-
- function setUp() public {
- registry = new Registry(treasuryAddress, 1000);
- registry.setManagement(management);
- registry.setSplitsFactory(splitsFactory);
-
- beacon = Upgrades.deployBeacon("Catalog.sol", admin);
- catalogImplementation = IBeacon(beacon).implementation();
-
- catalogProxy = Upgrades.deployBeaconProxy(
- beacon, abi.encodeCall(Catalog.initialize, (admin, catalogName, registry, membership))
- );
- }
-
- function test_beacon() public {
- address proxyBeaconAddress = Upgrades.getBeaconAddress(catalogProxy);
- assertEq(proxyBeaconAddress, beacon);
- }
-
- function upgrade_beacon_with_CatalogV2_setUp() public {
- vm.startPrank(admin);
- Upgrades.upgradeBeacon(beacon, "CatalogV2Mock.sol");
- vm.stopPrank();
- }
-
- function test_implementation_address_updated() public {
- upgrade_beacon_with_CatalogV2_setUp();
- catalogImplementationV2 = IBeacon(beacon).implementation();
- assertFalse(catalogImplementation == catalogImplementationV2);
- }
-
- function test_proxy_can_call_updated_Catalog() public {
- upgrade_beacon_with_CatalogV2_setUp();
- CatalogV2Mock(catalogProxy).setTestingUpgradeVariable("upgradeTest");
- string memory upgradeTest = CatalogV2Mock(catalogProxy).getTestingUpgradeVariable();
- assertEq(upgradeTest, "upgradeTest");
- }
-}
diff --git a/moda-contracts/test/Catalog.t.sol b/moda-contracts/test/Catalog.t.sol
deleted file mode 100644
index b0bb7015..00000000
--- a/moda-contracts/test/Catalog.t.sol
+++ /dev/null
@@ -1,814 +0,0 @@
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity ^0.8.21;
-
-import {Test, console2} from "forge-std/Test.sol";
-import "../src/CatalogFactory.sol";
-import "../src/Catalog.sol";
-import {Registry} from "../src/Registry.sol";
-import {Management} from "../src/Management.sol";
-import {Membership} from "../test/mocks/MembershipMock.sol";
-import {ITrackRegistration} from "../src/interfaces/Catalog/ITrackRegistration.sol";
-import "../src/Releases.sol";
-import "../src/ReleasesFactory.sol";
-import {Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol";
-
-contract CatalogTest is Test {
- CatalogFactory public catalogFactory;
- Catalog public catalog;
- Management public management;
- Membership public membership;
- Registry public registry;
- ReleasesFactory public releasesFactory;
- Releases public releasesMaster;
- Releases public releases;
-
- address public trackBeneficiary = address(0x5);
- string public trackRegistrationHash = "trackHash";
- string public trackId = "";
-
- address catalogBeacon;
- address admin = address(0xa);
- address public catalogAdmin = address(0x1);
- string public catalogName = "ACME";
- address public artist = address(0x4);
- address payable public treasuryAddress = payable(address(0x11));
- ISplitsFactory public splitsFactory = ISplitsFactory(address(0x12));
-
- error InvalidInitialization();
-
- event TrackRegistered(string trackRegistrationHash, string trackId, address indexed trackOwner);
- event TrackRegistrationHashUpdated(
- string trackId, string newTrackRegistrationHash, address indexed trackOwner
- );
- event TrackUpdated(
- ICatalog.TrackStatus indexed trackStatus,
- address indexed trackOwner,
- address trackBeneficiary,
- string trackRegistrationHash,
- string fingerprintHash,
- string validationHash,
- address trackVerifier
- );
- event ReleasesRegistered(address releases, address indexed releasesOwner);
- event ReleasesUnregistered(address releases, address indexed releasesOwner);
- event TrackApprovalChanged(string trackId, address indexed releases, bool hasApproval);
- event AllTracksApprovalChanged(address indexed artist, address indexed releases, bool hasApproval);
- event ReleaseRegistered(string[] trackIds, address releases, uint256 tokenId);
- event ReleaseUnregistered(bytes32 releaseHash);
-
- function setUp() public {
- management = new Management();
- membership = new Membership();
- registry = new Registry(treasuryAddress, 1000);
- registry.setManagement(management);
- registry.setSplitsFactory(splitsFactory);
-
- catalogBeacon = Upgrades.deployBeacon("Catalog.sol", admin);
- catalogFactory = new CatalogFactory(registry, catalogBeacon);
- console2.log("factory", address(catalogFactory));
- registry.grantRole(keccak256("CATALOG_REGISTRAR_ROLE"), address(catalogFactory));
-
- vm.startPrank(catalogAdmin);
- catalog = Catalog(catalogFactory.create(catalogName, IMembership(membership)));
- vm.stopPrank();
-
- releasesMaster = new Releases();
- releasesFactory = new ReleasesFactory(registry, address(releasesMaster));
- registry.grantRole(keccak256("RELEASES_REGISTRAR_ROLE"), address(releasesFactory));
-
- membership.addMember(artist);
-
- address[] memory releaseAdmins = new address[](1);
- releaseAdmins[0] = artist;
-
- vm.startPrank(artist);
- releasesFactory.create(releaseAdmins, "name", "symbol", catalog);
- vm.stopPrank();
-
- releases = Releases(catalog.getReleasesContract(artist));
- }
-
- function setup_auto_verified(address account) public {
- membership.addMember(account);
- vm.startPrank(catalogAdmin);
- catalog.grantRole(catalog.AUTO_VERIFIED_ROLE(), account);
- }
-
- /// Initialization revert
-
- function test_initialize_RevertIf_already_initialized() public {
- setUp();
- vm.expectRevert(InvalidInitialization.selector);
- vm.startPrank(catalogAdmin);
- catalog.initialize(admin, catalogName, registry, membership);
- vm.stopPrank();
- }
-
- /// registerTrack
-
- function registerTrack_setUp() public {
- vm.startPrank(artist);
- catalog.registerTrack(artist, trackBeneficiary, trackRegistrationHash);
- vm.stopPrank();
- trackId = catalog.getTrackId(trackRegistrationHash);
- }
-
- function test_registerTrack() public {
- registerTrack_setUp();
- Catalog.RegisteredTrack memory track = catalog.getTrack(trackId);
-
- assertEq(uint256(track.trackStatus), uint256(ITrackRegistration.TrackStatus.PENDING));
- assertEq(track.trackOwner, artist);
- assertEq(track.trackBeneficiary, trackBeneficiary);
- assertEq(track.trackRegistrationHash, trackRegistrationHash);
- assertEq(track.fingerprintHash, "");
- assertEq(track.validationHash, "");
- }
-
- function test_registerTrack_RevertIf_track_already_registered() public {
- registerTrack_setUp();
-
- vm.expectRevert(Catalog.TrackAlreadyRegistered.selector);
- vm.startPrank(artist);
- catalog.registerTrack(artist, trackBeneficiary, trackRegistrationHash);
- vm.stopPrank();
- }
-
- function test_registerTrack_RevertIf_user_not_member() public {
- vm.expectRevert(Catalog.MembershipRequired.selector);
- address nonMember = address(0x9);
- vm.startPrank(nonMember);
- catalog.registerTrack(nonMember, trackBeneficiary, trackRegistrationHash);
- vm.stopPrank();
- }
-
- function test_registerTrack_RevertIf_user_not_artist() public {
- address nonArtist = address(0x9);
- membership.addMember(nonArtist);
- vm.startPrank(nonArtist);
- vm.expectRevert(Catalog.MustBeTrackOwnerOrManager.selector);
- catalog.registerTrack(artist, trackBeneficiary, trackRegistrationHash);
- vm.stopPrank();
- }
-
- function test_registerTrack_emits_event() public {
- vm.expectEmit(true, true, true, true);
- emit TrackRegistered(trackRegistrationHash, "ACME-31337-0", artist);
-
- vm.startPrank(artist);
- catalog.registerTrack(artist, trackBeneficiary, trackRegistrationHash);
- vm.stopPrank();
- }
-
- // registerTrack as manager
-
- function test_registerTrack_as_manager_setUp() public {
- vm.startPrank(artist);
- address manager = address(0x6);
- membership.addMember(manager);
- address[] memory managers = new address[](1);
- managers[0] = manager;
- management.addManagers(managers);
- vm.stopPrank();
-
- vm.startPrank(manager);
- catalog.registerTrack(artist, trackBeneficiary, trackRegistrationHash);
- vm.stopPrank();
-
- trackId = catalog.getTrackId(trackRegistrationHash);
- }
-
- function test_registerTrack_as_manager() public {
- test_registerTrack_as_manager_setUp();
- Catalog.RegisteredTrack memory track = catalog.getTrack(trackId);
-
- assertEq(uint256(track.trackStatus), uint256(ITrackRegistration.TrackStatus.PENDING));
- assertEq(track.trackOwner, artist);
- assertEq(track.trackBeneficiary, trackBeneficiary);
- assertEq(track.trackRegistrationHash, trackRegistrationHash);
- assertEq(track.fingerprintHash, "");
- assertEq(track.validationHash, "");
- }
-
- // registerTrack with AUTO_VERIFIED_ROLE
-
- function test_registerTrack_with_AUTO_VERIFIED_ROLE() public {
- address beneficiary = address(0x5);
- string memory trackHash = "track";
- setup_auto_verified(artist);
-
- vm.startPrank(artist);
- catalog.registerTrack(artist, beneficiary, trackHash);
- vm.stopPrank();
- string memory actualTrackId = catalog.getTrackId(trackHash);
- Catalog.RegisteredTrack memory track = catalog.getTrack(actualTrackId);
-
- assertEq(uint256(track.trackStatus), uint256(ITrackRegistration.TrackStatus.VALIDATED));
- assertEq(track.trackOwner, artist);
- assertEq(track.trackBeneficiary, beneficiary);
- assertEq(track.trackRegistrationHash, trackHash);
- assertEq(track.fingerprintHash, "");
- assertEq(track.validationHash, "");
- }
-
- // setTrackStatus
-
- function setTrackStatus_setUp() public {
- registerTrack_setUp();
- registry.grantRole(keccak256("VERIFIER_ROLE"), address(this));
- catalog.setTrackStatus(trackId, ITrackRegistration.TrackStatus.VALIDATED);
- }
-
- function test_setTrackStatus() public {
- setTrackStatus_setUp();
- assertEq(
- uint256(catalog.getTrack(trackId).trackStatus),
- uint256(ITrackRegistration.TrackStatus.VALIDATED)
- );
- }
-
- function test_setTrackStatus_RevertIf_no_verifier_role() public {
- registerTrack_setUp();
- address nonVerifier = address(0x9);
- vm.expectRevert(Catalog.VerifierRoleRequired.selector);
- vm.startPrank(nonVerifier);
- catalog.setTrackStatus(trackId, ITrackRegistration.TrackStatus.VALIDATED);
- vm.stopPrank();
- }
-
- function test_setTrackStatus_RevertIf_track_not_registered() public {
- registry.grantRole(keccak256("VERIFIER_ROLE"), address(this));
- string memory unregisteredId = "unregisteredId";
- vm.expectRevert(Catalog.TrackIsNotRegistered.selector);
- catalog.setTrackStatus(unregisteredId, ITrackRegistration.TrackStatus.VALIDATED);
- }
-
- function test_setTrackStatus_emits_event() public {
- registerTrack_setUp();
- registry.grantRole(keccak256("VERIFIER_ROLE"), address(this));
- vm.expectEmit(true, true, true, true);
- emit TrackUpdated(
- ITrackRegistration.TrackStatus.VALIDATED,
- artist,
- trackBeneficiary,
- trackRegistrationHash,
- "",
- "",
- address(this)
- );
- catalog.setTrackStatus(trackId, ITrackRegistration.TrackStatus.VALIDATED);
- }
-
- struct UpdateTrackData {
- address newTrackBeneficiary;
- string newTrackRegistrationHash;
- string fingerprintHash;
- string validationHash;
- }
-
- UpdateTrackData updateTrackData = UpdateTrackData({
- newTrackBeneficiary: address(0x7),
- newTrackRegistrationHash: "newTrackHash",
- fingerprintHash: "fingerprintHash",
- validationHash: "validationHash"
- });
-
- // setTrackBeneficiary
-
- function test_setTrackBeneficiary() public {
- registerTrack_setUp();
- vm.startPrank(artist);
- catalog.setTrackBeneficiary(trackId, updateTrackData.newTrackBeneficiary);
- vm.stopPrank();
- assertEq(catalog.getTrack(trackId).trackBeneficiary, updateTrackData.newTrackBeneficiary);
- }
-
- function test_setTrackBeneficiary_RevertIf_track_not_registered() public {
- string memory unregisteredId = "unregisteredId";
- vm.expectRevert(Catalog.TrackIsNotRegistered.selector);
- vm.startPrank(artist);
- catalog.setTrackBeneficiary(unregisteredId, updateTrackData.newTrackBeneficiary);
- vm.stopPrank();
- }
-
- function test_setTrackBeneficiary_RevertIf_not_track_owner() public {
- registerTrack_setUp();
- address nonOwner = address(0x9);
- vm.expectRevert(Catalog.MustBeTrackOwnerOrManager.selector);
- vm.startPrank(nonOwner);
- catalog.setTrackBeneficiary(trackId, updateTrackData.newTrackBeneficiary);
- vm.stopPrank();
- }
-
- function test_setTrackBeneficiary_emits_event() public {
- registerTrack_setUp();
- vm.expectEmit(true, true, true, true);
- emit TrackUpdated(
- ITrackRegistration.TrackStatus.PENDING,
- artist,
- updateTrackData.newTrackBeneficiary,
- trackRegistrationHash,
- "",
- "",
- address(0x0)
- );
- vm.startPrank(artist);
- catalog.setTrackBeneficiary(trackId, updateTrackData.newTrackBeneficiary);
- vm.stopPrank();
- }
-
- // setTrackMetadata
-
- function test_setTrackMetadata() public {
- registerTrack_setUp();
- vm.startPrank(artist);
- catalog.setTrackMetadata(trackId, updateTrackData.newTrackRegistrationHash);
- vm.stopPrank();
- assertEq(
- catalog.getTrack(trackId).trackRegistrationHash, updateTrackData.newTrackRegistrationHash
- );
- }
-
- function test_setTrackMetadata_RevertIf_track_not_registered() public {
- string memory unregisteredId = "unregisteredId";
- vm.expectRevert(Catalog.TrackIsNotRegistered.selector);
- vm.startPrank(artist);
- catalog.setTrackMetadata(unregisteredId, updateTrackData.newTrackRegistrationHash);
- vm.stopPrank();
- }
-
- function test_setTrackMetadata_RevertIf_not_track_owner() public {
- registerTrack_setUp();
- address nonOwner = address(0x9);
- vm.expectRevert(Catalog.MustBeTrackOwnerOrManager.selector);
- vm.startPrank(nonOwner);
- catalog.setTrackMetadata(trackId, updateTrackData.newTrackRegistrationHash);
- vm.stopPrank();
- }
-
- function test_setTrackMetadata_emits_event() public {
- registerTrack_setUp();
- vm.expectEmit(true, true, true, true);
- emit TrackUpdated(
- ITrackRegistration.TrackStatus.PENDING,
- artist,
- trackBeneficiary,
- "newTrackHash",
- "",
- "",
- address(0x0)
- );
- vm.startPrank(artist);
- catalog.setTrackMetadata(trackId, "newTrackHash");
- vm.stopPrank();
- }
-
- // setTrackFingerprintHash
-
- function test_setTrackFingerprintHash() public {
- registerTrack_setUp();
- vm.startPrank(artist);
- catalog.setTrackFingerprintHash(trackId, updateTrackData.fingerprintHash);
- vm.stopPrank();
- assertEq(catalog.getTrack(trackId).fingerprintHash, updateTrackData.fingerprintHash);
- }
-
- function test_setTrackFingerprintHash_RevertIf_track_not_registered() public {
- string memory unregisteredId = "unregisteredId";
- vm.expectRevert(Catalog.TrackIsNotRegistered.selector);
- vm.startPrank(artist);
- catalog.setTrackFingerprintHash(unregisteredId, updateTrackData.fingerprintHash);
- vm.stopPrank();
- }
-
- function test_setTrackFingerprintHash_RevertIf_not_track_owner() public {
- registerTrack_setUp();
- address nonOwner = address(0x9);
- vm.expectRevert(Catalog.MustBeTrackOwnerOrManager.selector);
- vm.startPrank(nonOwner);
- catalog.setTrackFingerprintHash(trackId, updateTrackData.fingerprintHash);
- vm.stopPrank();
- }
-
- function test_setTrackFingerprintHash_emits_event() public {
- registerTrack_setUp();
- vm.expectEmit(true, true, true, true);
- emit TrackUpdated(
- ITrackRegistration.TrackStatus.PENDING,
- artist,
- trackBeneficiary,
- trackRegistrationHash,
- "fingerprintHash",
- "",
- address(0x0)
- );
- vm.startPrank(artist);
- catalog.setTrackFingerprintHash(trackId, "fingerprintHash");
- vm.stopPrank();
- }
-
- // setTrackValidationHash
-
- function test_setTrackValidationHash() public {
- registerTrack_setUp();
- vm.startPrank(artist);
- catalog.setTrackValidationHash(trackId, updateTrackData.validationHash);
- vm.stopPrank();
- assertEq(catalog.getTrack(trackId).validationHash, updateTrackData.validationHash);
- }
-
- function test_setTrackValidationHash_RevertIf_track_not_registered() public {
- string memory unregisteredId = "unregisteredId";
- vm.expectRevert(Catalog.TrackIsNotRegistered.selector);
- vm.startPrank(artist);
- catalog.setTrackValidationHash(unregisteredId, updateTrackData.validationHash);
- vm.stopPrank();
- }
-
- function test_setTrackValidationHash_RevertIf_not_track_owner() public {
- registerTrack_setUp();
- address nonOwner = address(0x9);
- vm.expectRevert(Catalog.MustBeTrackOwnerOrManager.selector);
- vm.startPrank(nonOwner);
- catalog.setTrackValidationHash(trackId, updateTrackData.validationHash);
- vm.stopPrank();
- }
-
- function test_setTrackValidationHash_emits_event() public {
- registerTrack_setUp();
- vm.expectEmit(true, true, true, true);
- emit TrackUpdated(
- ITrackRegistration.TrackStatus.PENDING,
- artist,
- trackBeneficiary,
- trackRegistrationHash,
- "",
- "validationHash",
- address(0x0)
- );
- vm.startPrank(artist);
- catalog.setTrackValidationHash(trackId, "validationHash");
- vm.stopPrank();
- }
-
- /// registerReleasesContract
-
- function test_registerReleasesContract() public {
- assertEq(catalog.getReleasesOwner(address(releases)), artist);
- }
-
- function test_registerReleasesContract_reverts_if_no_releases_registrar_role() public {
- address maliciousAccount = address(0x9);
- membership.addMember(maliciousAccount);
-
- vm.expectRevert(Catalog.ReleasesRegistrarRoleRequired.selector);
- vm.startPrank(maliciousAccount);
- catalog.registerReleasesContract(address(0x69), maliciousAccount);
- vm.stopPrank();
- }
-
- function test_registerReleasesContract_reverts_if_releases_contract_already_registered() public {
- vm.expectRevert(Catalog.ReleasesContractIsAlreadyRegistered.selector);
- vm.startPrank(address(releasesFactory));
- catalog.registerReleasesContract(address(releases), artist);
- vm.stopPrank();
- }
-
- function test_registerReleasesContract_emits_event() public {
- address registrar = address(0x999);
- registry.grantRole(registry.RELEASES_REGISTRAR_ROLE(), registrar);
- address newReleases = address(0x123);
- address releasesOwner = address(0x444);
- membership.addMember(releasesOwner);
-
- vm.startPrank(registrar);
- vm.expectEmit(true, true, true, true);
- emit ReleasesRegistered(newReleases, releasesOwner);
- catalog.registerReleasesContract(newReleases, releasesOwner);
- vm.stopPrank();
- }
-
- // unregisterReleasesContract
-
- function test_unregisterReleasesContract() public {
- vm.startPrank(catalogAdmin);
- catalog.unregisterReleasesContract(address(releases));
- assertEq(catalog.getReleasesOwner(address(releases)), address(0));
- vm.stopPrank();
- }
-
- function test_revert_if_unregistering_unregistered_releases_contract() public {
- vm.expectRevert(Catalog.ReleasesContractIsNotRegistered.selector);
-
- vm.startPrank(catalogAdmin);
- catalog.unregisterReleasesContract(address(0x69));
- vm.stopPrank();
- }
-
- function test_unregisterReleasesContract_emits_event() public {
- vm.expectEmit(true, true, true, true);
- emit ReleasesUnregistered(address(releases), address(artist));
-
- vm.startPrank(catalogAdmin);
- catalog.unregisterReleasesContract(address(releases));
- vm.stopPrank();
- }
-
- // setReleasesApproval
-
- function test_setReleasesApproval() public {
- registerTrack_setUp();
- vm.startPrank(artist);
- catalog.setReleasesApproval(trackId, address(releases), true);
- vm.stopPrank();
- assertEq(catalog.getReleasesApproval(trackId, address(releases)), true);
- }
-
- function test_setReleasesApproval_RevertIf_setting_approval_not_artist() public {
- registerTrack_setUp();
- address nonArtist = address(0x9);
- vm.expectRevert(Catalog.MustBeTrackOwnerOrManager.selector);
- vm.startPrank(nonArtist);
- catalog.setReleasesApproval(trackId, address(releases), true);
- vm.stopPrank();
- }
-
- function test_setReleasesApproval_RevertIf_setting_approval_with_unregistered_releases_contract()
- public
- {
- registerTrack_setUp();
- address nonRegisteredReleasesContract = address(0x9);
- vm.expectRevert(Catalog.ReleasesContractIsNotRegistered.selector);
- vm.startPrank(artist);
- catalog.setReleasesApproval(trackId, nonRegisteredReleasesContract, true);
- vm.stopPrank();
- }
-
- function test_setReleasesApproval_emits_event() public {
- registerTrack_setUp();
- vm.expectEmit(true, true, true, true);
- emit TrackApprovalChanged(trackId, address(releases), true);
- vm.startPrank(artist);
- catalog.setReleasesApproval(trackId, address(releases), true);
- vm.stopPrank();
- }
-
- function test_remove_releases_approval() public {
- registerTrack_setUp();
- vm.startPrank(artist);
- catalog.setReleasesApproval(trackId, address(releases), true);
- catalog.setReleasesApproval(trackId, address(releases), false);
- vm.stopPrank();
- assertEq(catalog.getReleasesApproval(trackId, address(releases)), false);
- }
-
- // setReleasesApprovalForAll
-
- function test_setReleasesApprovalForAll() public {
- registerTrack_setUp();
- vm.startPrank(artist);
- catalog.setReleasesApprovalForAll(artist, address(releases), true);
- vm.stopPrank();
- assertEq(catalog.getReleasesApprovalForAll(artist, address(releases)), true);
- }
-
- function test_setReleasesApprovalForAll_RevertIf_setting_approval_not_artist() public {
- registerTrack_setUp();
- address nonArtist = address(0x9);
- vm.expectRevert(Catalog.MustBeTrackOwnerOrManager.selector);
- vm.startPrank(nonArtist);
- catalog.setReleasesApprovalForAll(artist, address(releases), true);
- vm.stopPrank();
- }
-
- function test_setReleasesApprovalForAll_RevertIf_setting_approval_with_unregistered_releases_contract(
- ) public {
- registerTrack_setUp();
- address nonRegisteredReleasesContract = address(0x9);
- vm.expectRevert(Catalog.ReleasesContractIsNotRegistered.selector);
- vm.startPrank(artist);
- catalog.setReleasesApprovalForAll(artist, nonRegisteredReleasesContract, true);
- vm.stopPrank();
- }
-
- function test_setReleasesApprovalForAll_emits_event() public {
- registerTrack_setUp();
- vm.expectEmit(true, true, true, true);
- emit AllTracksApprovalChanged(artist, address(releases), true);
- vm.startPrank(artist);
- catalog.setReleasesApprovalForAll(artist, address(releases), true);
- vm.stopPrank();
- }
-
- /// registerRelease
-
- struct RegisteringReleaseData {
- string[] trackIds;
- string uri;
- uint256 tokenId;
- }
-
- RegisteringReleaseData registeringReleaseData;
-
- function registeringRelease_setUp() public {
- string memory trackRegistrationHashOne = "trackHashOne";
- string memory trackRegistrationHashTwo = "trackHashTwo";
-
- vm.startPrank(artist);
- catalog.registerTrack(artist, trackBeneficiary, trackRegistrationHashOne);
- catalog.registerTrack(artist, trackBeneficiary, trackRegistrationHashTwo);
- vm.stopPrank();
-
- string memory trackIdOne = catalog.getTrackId(trackRegistrationHashOne);
- string memory trackIdTwo = catalog.getTrackId(trackRegistrationHashTwo);
- string[] memory trackIds = new string[](2);
- trackIds[0] = trackIdOne;
- trackIds[1] = trackIdTwo;
- registeringReleaseData = RegisteringReleaseData({trackIds: trackIds, uri: "uri", tokenId: 1});
- }
-
- // registerRelease using setReleasesApproval
-
- function test_registerRelease_with_setReleasesApproval() public {
- registeringRelease_setUp();
- vm.startPrank(artist);
- catalog.setReleasesApproval(registeringReleaseData.trackIds[0], address(releases), true);
- catalog.setReleasesApproval(registeringReleaseData.trackIds[1], address(releases), true);
- vm.stopPrank();
- vm.startPrank(address(releases));
- catalog.registerRelease(
- registeringReleaseData.trackIds, registeringReleaseData.uri, registeringReleaseData.tokenId
- );
- vm.stopPrank();
- bytes32 releaseHash = catalog.getReleaseHash(address(releases), registeringReleaseData.tokenId);
- Catalog.RegisteredRelease memory registeredRelease = catalog.getRegisteredRelease(releaseHash);
-
- assertEq(registeredRelease.releases, address(releases));
- assertEq(registeredRelease.tokenId, registeringReleaseData.tokenId);
- assertEq(registeredRelease.trackIds[0], registeringReleaseData.trackIds[0]);
- assertEq(registeredRelease.trackIds[1], registeringReleaseData.trackIds[1]);
- }
-
- function test_registerRelease_RevertIf_releases_contract_not_registered() public {
- registeringRelease_setUp();
- address nonRegisteredReleasesContract = address(0x9);
- vm.expectRevert(Catalog.ReleasesContractIsNotRegistered.selector);
- vm.startPrank(nonRegisteredReleasesContract);
- catalog.registerRelease(
- registeringReleaseData.trackIds, registeringReleaseData.uri, registeringReleaseData.tokenId
- );
- vm.stopPrank();
- }
-
- function test_registerRelease_RevertIf_tracks_unregistered() public {
- registeringRelease_setUp();
- string memory unregisteredTrackId = "unregisteredTrackId";
- string[] memory unregisteredTrackIds = new string[](2);
- unregisteredTrackIds[0] = unregisteredTrackId;
- unregisteredTrackIds[1] = unregisteredTrackId;
- vm.expectRevert(Catalog.TrackIsNotRegistered.selector);
- vm.startPrank(address(releases));
- catalog.registerRelease(
- unregisteredTrackIds, registeringReleaseData.uri, registeringReleaseData.tokenId
- );
- vm.stopPrank();
- }
-
- function test_registerRelease_RevertIf_track_not_validated() public {
- registeringRelease_setUp();
- registry.grantRole(keccak256("VERIFIER_ROLE"), address(this));
- catalog.setTrackStatus(
- registeringReleaseData.trackIds[0], ITrackRegistration.TrackStatus.INVALIDATED
- );
-
- vm.expectRevert(Catalog.TrackIsInvalid.selector);
- vm.startPrank(address(releases));
- catalog.registerRelease(
- registeringReleaseData.trackIds, registeringReleaseData.uri, registeringReleaseData.tokenId
- );
- vm.stopPrank();
- }
-
- function test_registerRelease_RevertIf_releases_contract_has_no_approval() public {
- registeringRelease_setUp();
- vm.expectRevert(Catalog.ReleasesContractDoesNotHavePermission.selector);
- vm.startPrank(address(releases));
- catalog.registerRelease(
- registeringReleaseData.trackIds, registeringReleaseData.uri, registeringReleaseData.tokenId
- );
- vm.stopPrank();
- }
-
- function test_registerRelease_RevertIf_release_duplicate() public {
- registeringRelease_setUp();
- vm.startPrank(artist);
- catalog.setReleasesApprovalForAll(artist, address(releases), true);
- vm.stopPrank();
- vm.startPrank(address(releases));
- catalog.registerRelease(
- registeringReleaseData.trackIds, registeringReleaseData.uri, registeringReleaseData.tokenId
- );
- vm.stopPrank();
- vm.expectRevert(Catalog.ReleaseAlreadyCreated.selector);
- vm.startPrank(address(releases));
- catalog.registerRelease(
- registeringReleaseData.trackIds, registeringReleaseData.uri, registeringReleaseData.tokenId
- );
- vm.stopPrank();
- }
-
- function test_registerRelease_emits_event() public {
- registeringRelease_setUp();
- vm.startPrank(artist);
- catalog.setReleasesApproval(registeringReleaseData.trackIds[0], address(releases), true);
- catalog.setReleasesApproval(registeringReleaseData.trackIds[1], address(releases), true);
- vm.stopPrank();
- vm.expectEmit(true, true, true, true);
- emit ReleaseRegistered(
- registeringReleaseData.trackIds, address(releases), registeringReleaseData.tokenId
- );
- vm.startPrank(address(releases));
- catalog.registerRelease(
- registeringReleaseData.trackIds, registeringReleaseData.uri, registeringReleaseData.tokenId
- );
- vm.stopPrank();
- }
-
- // registerRelease using setReleasesApprovalForAll
-
- function test_registerRelease_with_setReleasesApprovalForAll() public {
- registeringRelease_setUp();
- vm.startPrank(artist);
- catalog.setReleasesApprovalForAll(artist, address(releases), true);
- vm.stopPrank();
- vm.startPrank(address(releases));
-
- catalog.registerRelease(
- registeringReleaseData.trackIds, registeringReleaseData.uri, registeringReleaseData.tokenId
- );
- vm.stopPrank();
-
- bytes32 releaseHash = catalog.getReleaseHash(address(releases), registeringReleaseData.tokenId);
- Catalog.RegisteredRelease memory registeredRelease = catalog.getRegisteredRelease(releaseHash);
- assertEq(registeredRelease.releases, address(releases));
- assertEq(registeredRelease.tokenId, registeringReleaseData.tokenId);
- assertEq(registeredRelease.trackIds[0], registeringReleaseData.trackIds[0]);
- assertEq(registeredRelease.trackIds[1], registeringReleaseData.trackIds[1]);
- }
-
- // unregisterRelease
-
- function test_unregisterRelease() public {
- registeringRelease_setUp();
- vm.startPrank(artist);
- catalog.setReleasesApprovalForAll(artist, address(releases), true);
- vm.stopPrank();
- vm.startPrank(address(releases));
- catalog.registerRelease(
- registeringReleaseData.trackIds, registeringReleaseData.uri, registeringReleaseData.tokenId
- );
- vm.stopPrank();
-
- bytes32 releaseHash = catalog.getReleaseHash(address(releases), registeringReleaseData.tokenId);
- vm.startPrank(catalogAdmin);
- catalog.unregisterRelease(releaseHash);
- assertEq(catalog.getRegisteredRelease(releaseHash).releases, address(0));
- vm.stopPrank();
- }
-
- function test_unregisterRelease_emits_event() public {
- registeringRelease_setUp();
- vm.startPrank(artist);
- catalog.setReleasesApprovalForAll(artist, address(releases), true);
- vm.stopPrank();
- vm.startPrank(address(releases));
- catalog.registerRelease(
- registeringReleaseData.trackIds, registeringReleaseData.uri, registeringReleaseData.tokenId
- );
- vm.stopPrank();
-
- bytes32 releaseHash = catalog.getReleaseHash(address(releases), registeringReleaseData.tokenId);
- vm.expectEmit(true, true, true, true);
- emit ReleaseUnregistered(releaseHash);
- vm.startPrank(catalogAdmin);
- catalog.unregisterRelease(releaseHash);
- vm.stopPrank();
- }
-
- /// getReleaseTracks
-
- function test_getReleaseTracks() public {
- registeringRelease_setUp();
-
- vm.startPrank(artist);
- catalog.setReleasesApprovalForAll(artist, address(releases), true);
- vm.stopPrank();
-
- string[] memory trackIds =
- catalog.getReleaseTracks(address(releases), registeringReleaseData.tokenId);
-
- for (uint256 i = 0; i < trackIds.length; i++) {
- assertEq(trackIds[i], registeringReleaseData.trackIds[i]);
- }
- }
-}
diff --git a/moda-contracts/test/CatalogFactory.t.sol b/moda-contracts/test/CatalogFactory.t.sol
deleted file mode 100644
index 6bf2a1df..00000000
--- a/moda-contracts/test/CatalogFactory.t.sol
+++ /dev/null
@@ -1,59 +0,0 @@
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity ^0.8.13;
-
-import {Test, console2, Vm} from "forge-std/Test.sol";
-import "../src/CatalogFactory.sol";
-import "../src/Catalog.sol";
-import "../src/Registry.sol";
-import "../src/Management.sol";
-import "../src/SplitsFactory.sol";
-import {IMembership} from "../src/interfaces/IMembership.sol";
-import {Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol";
-
-contract CatalogFactoryTest is Test {
- CatalogFactory public catalogFactory;
- Catalog public catalog;
- Registry public registry;
- Management public management;
- SplitsFactory public splitsFactory;
-
- address catalogBeacon;
- address admin = address(0xa);
- address payable treasury = payable(address(0x1));
- uint32 treasuryFee = 1000;
- address membership = address(0x3);
-
- event CatalogCreated(address indexed owner, address indexed catalog, string name);
-
- function setUp() public {
- management = new Management();
- registry = new Registry(treasury, treasuryFee);
- registry.setManagement(management);
- registry.setSplitsFactory(splitsFactory);
- catalogBeacon = Upgrades.deployBeacon("Catalog.sol", admin);
-
- catalogFactory = new CatalogFactory(registry, catalogBeacon);
- registry.grantRole(keccak256("CATALOG_REGISTRAR_ROLE"), address(catalogFactory));
- }
-
- function test_create_catalog() public {
- address catalogProxy = catalogFactory.create("name", IMembership(membership));
- assertNotEq(catalogProxy, address(0x0));
- }
-
- function test_create_catalog_emits_event() public {
- vm.recordLogs();
- address catalogProxyAddress = catalogFactory.create("name", IMembership(membership));
- Vm.Log[] memory logs = vm.getRecordedLogs();
-
- console2.log(logs.length);
-
- bytes32 owner = logs[4].topics[1];
- bytes32 catalogProxy = logs[4].topics[2];
- string memory name = abi.decode(logs[4].data, (string));
-
- assertEq(owner, bytes32(uint256(uint160(address(this)))));
- assertEq(catalogProxy, bytes32(uint256(uint160(catalogProxyAddress))));
- assertEq(name, "name");
- }
-}
diff --git a/moda-contracts/test/Management.t.sol b/moda-contracts/test/Management.t.sol
deleted file mode 100644
index 5cac3a74..00000000
--- a/moda-contracts/test/Management.t.sol
+++ /dev/null
@@ -1,67 +0,0 @@
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity ^0.8.21;
-
-import {Test, console2} from "forge-std/Test.sol";
-import "../src/Management.sol";
-
-contract ManagementTest is Test {
- Management public management;
-
- address public artist = address(0x1);
- address[] managers;
- address managerOne = address(0x3);
- address managerTwo = address(0x4);
-
- function setUp() public {
- management = new Management();
- managers = new address[](2);
- managers[0] = managerOne;
- managers[1] = managerTwo;
- }
-
- function addManagers_setUp() public {
- vm.startPrank(artist);
- management.addManagers(managers);
- vm.stopPrank();
- }
-
- function test_addManagers() public {
- addManagers_setUp();
- bool isManagerOne = management.isManager(artist, managerOne);
- bool isManagerTwo = management.isManager(artist, managerTwo);
- uint256 managerCount = management.getManagerCount(artist);
- address managerOneAddress = management.getManager(artist, 0);
- address managerTwoAddress = management.getManager(artist, 1);
-
- assertTrue(isManagerOne);
- assertTrue(isManagerTwo);
- assertEq(managerCount, 2);
- assertEq(managerOneAddress, managerOne);
- assertEq(managerTwoAddress, managerTwo);
- }
-
- function test_removeManagers() public {
- addManagers_setUp();
- vm.startPrank(artist);
- management.removeManagers(managers);
- vm.stopPrank();
- bool isManagerOne = management.isManager(artist, managerOne);
- bool isManagerTwo = management.isManager(artist, managerTwo);
- uint256 managerCount = management.getManagerCount(artist);
-
- assertFalse(isManagerOne);
- assertFalse(isManagerTwo);
- assertEq(managerCount, 0);
- }
-
- function test_addManagers_does_not_add_duplicate_address() public {
- address[] memory duplicateManagers = new address[](2);
- duplicateManagers[0] = address(0x3);
- duplicateManagers[1] = address(0x3);
- vm.startPrank(artist);
- management.addManagers(duplicateManagers);
- vm.stopPrank();
- uint256 managerCount = management.getManagerCount(artist);
- assertEq(managerCount, 1);
- }
-}
diff --git a/moda-contracts/test/OpenReleases.t.sol b/moda-contracts/test/OpenReleases.t.sol
deleted file mode 100644
index da737b27..00000000
--- a/moda-contracts/test/OpenReleases.t.sol
+++ /dev/null
@@ -1,300 +0,0 @@
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity ^0.8.13;
-
-import {Test, console2} from "forge-std/Test.sol";
-import {Registry} from "../src/Registry.sol";
-import {Catalog} from "../src/Catalog.sol";
-import {OpenReleases} from "../src/OpenReleases.sol";
-import {IOpenReleases} from "../src/interfaces/Releases/IOpenReleases.sol";
-import {OpenReleasesFactory} from "../src/OpenReleasesFactory.sol";
-import {Management} from "../src/Management.sol";
-import {Membership} from "../test/mocks/MembershipMock.sol";
-import {IMembership} from "../src/interfaces/IMembership.sol";
-import {SplitsFactoryMock} from "../test/mocks/SplitsFactoryMock.sol";
-import {CatalogFactory} from "../src/CatalogFactory.sol";
-import {Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol";
-
-contract OpenReleasesTest is Test {
- Membership public membership;
- Management public management;
- Registry public registry;
- SplitsFactoryMock public splitsFactory;
- CatalogFactory public catalogFactory;
- Catalog public catalog;
- OpenReleases public openReleases;
- OpenReleases public releasesMaster;
- OpenReleasesFactory public releasesFactory;
-
- address public catalogBeacon;
- address public admin = address(0xa);
- string public catalogName = "DRIP Catalog";
- string public catalogVersion = "1";
-
- string name = "DRIP";
- string symbol = "DRIP";
- address organizationAdmin = address(0x6);
- address trackOwner = address(0x7);
-
- address payable treasury = payable(address(0x5));
-
- event ReleaseCreated(uint256 tokenId);
- event Burned(uint256 tokenId, address indexed tokenOwner);
- event URI(string value, uint256 indexed id);
-
- struct TrackRegistrationData {
- address trackBeneficiary;
- string trackRegistrationHash;
- string trackId;
- }
-
- TrackRegistrationData trackRegistrationDataOne = TrackRegistrationData({
- trackBeneficiary: address(0x8),
- trackRegistrationHash: "trackHash1",
- trackId: ""
- });
-
- TrackRegistrationData trackRegistrationDataTwo = TrackRegistrationData({
- trackBeneficiary: address(0x9),
- trackRegistrationHash: "trackHash2",
- trackId: ""
- });
-
- TrackRegistrationData trackRegistrationDataThree = TrackRegistrationData({
- trackBeneficiary: address(0x10),
- trackRegistrationHash: "trackHash3",
- trackId: ""
- });
-
- struct ReleaseData {
- uint96 royaltyAmount;
- string uri;
- uint256 totalSupply;
- string[] trackIds;
- }
-
- ReleaseData releaseData = ReleaseData(1000, "testURI", 100, new string[](0));
-
- function setUp() public {
- management = new Management();
- membership = new Membership();
- splitsFactory = new SplitsFactoryMock(address(0x3));
- registry = new Registry(treasury, 1000);
- registry.setManagement(management);
- registry.setSplitsFactory(splitsFactory);
-
- catalogBeacon = Upgrades.deployBeacon("Catalog.sol", admin);
- catalogFactory = new CatalogFactory(registry, catalogBeacon);
- registry.grantRole(registry.CATALOG_REGISTRAR_ROLE(), address(catalogFactory));
-
- vm.startPrank(organizationAdmin);
- catalog = Catalog(catalogFactory.create(catalogName, IMembership(membership)));
- vm.stopPrank();
- membership.addMember(trackOwner);
-
- releasesMaster = new OpenReleases();
- releasesFactory = new OpenReleasesFactory(registry, address(releasesMaster));
- registry.grantRole(registry.RELEASES_REGISTRAR_ROLE(), address(releasesFactory));
-
- vm.startPrank(organizationAdmin);
- releasesFactory.create(name, symbol, catalog);
- vm.stopPrank();
-
- openReleases = OpenReleases(catalog.getReleasesContract(organizationAdmin));
- }
-
- // Initialization
-
- function test_constructor() public {
- uint256 numberOfReleases = openReleases.numberOfReleases();
-
- assertEq(openReleases.name(), name);
- assertEq(openReleases.symbol(), symbol);
- assertEq(numberOfReleases, 0);
- console2.logBytes4(type(IOpenReleases).interfaceId);
- }
-
- // create with a curated Releases contract
-
- function registerTrack_setUp() public {
- vm.startPrank(trackOwner);
- catalog.registerTrack(
- trackOwner,
- trackRegistrationDataOne.trackBeneficiary,
- trackRegistrationDataOne.trackRegistrationHash
- );
- catalog.registerTrack(
- trackOwner,
- trackRegistrationDataTwo.trackBeneficiary,
- trackRegistrationDataTwo.trackRegistrationHash
- );
- catalog.registerTrack(
- trackOwner,
- trackRegistrationDataThree.trackBeneficiary,
- trackRegistrationDataThree.trackRegistrationHash
- );
-
- trackRegistrationDataOne.trackId =
- catalog.getTrackId(trackRegistrationDataOne.trackRegistrationHash);
- trackRegistrationDataTwo.trackId =
- catalog.getTrackId(trackRegistrationDataTwo.trackRegistrationHash);
- trackRegistrationDataThree.trackId =
- catalog.getTrackId(trackRegistrationDataThree.trackRegistrationHash);
-
- releaseData.trackIds.push(trackRegistrationDataOne.trackId);
- releaseData.trackIds.push(trackRegistrationDataTwo.trackId);
- releaseData.trackIds.push(trackRegistrationDataThree.trackId);
-
- vm.stopPrank();
- }
-
- function createRelease_setUp() public {
- registerTrack_setUp();
- vm.startPrank(trackOwner);
- openReleases.create(
- trackOwner,
- releaseData.royaltyAmount,
- releaseData.uri,
- releaseData.totalSupply,
- releaseData.trackIds
- );
- vm.stopPrank();
- }
-
- function test_create_release() public {
- createRelease_setUp();
- bytes32 releaseHash = catalog.getReleaseHash(address(openReleases), 1);
- uint256 numberOfReleases = openReleases.numberOfReleases();
-
- Catalog.RegisteredRelease memory registeredRelease = catalog.getRegisteredRelease(releaseHash);
-
- assertEq(numberOfReleases, 1);
- assertEq(registeredRelease.releases, address(openReleases));
- assertEq(registeredRelease.tokenId, 1);
- assertEq(registeredRelease.trackIds[0], trackRegistrationDataOne.trackId);
- assertEq(registeredRelease.trackIds[1], trackRegistrationDataTwo.trackId);
- assertEq(registeredRelease.trackIds[2], trackRegistrationDataThree.trackId);
- }
-
- function test_create_RevertIf_caller_is_not_track_owner() public {
- registerTrack_setUp();
-
- address nonTrackOwner = address(0x2);
- vm.expectRevert(OpenReleases.CallerDoesNotHaveAccess.selector);
- vm.startPrank(nonTrackOwner);
- openReleases.create(
- nonTrackOwner,
- releaseData.royaltyAmount,
- releaseData.uri,
- releaseData.totalSupply,
- releaseData.trackIds
- );
- vm.stopPrank();
- }
-
- function test_create_RevertIf_royalty_amount_is_over_2000() public {
- registerTrack_setUp();
-
- vm.expectRevert(OpenReleases.InvalidRoyaltyAmount.selector);
- vm.startPrank(trackOwner);
- openReleases.create(
- trackOwner, 2001, releaseData.uri, releaseData.totalSupply, releaseData.trackIds
- );
- vm.stopPrank();
- }
-
- function test_create_emits_event() public {
- registerTrack_setUp();
-
- vm.expectEmit(true, true, true, true);
- emit ReleaseCreated(1);
- vm.startPrank(trackOwner);
- openReleases.create(
- trackOwner,
- releaseData.royaltyAmount,
- releaseData.uri,
- releaseData.totalSupply,
- releaseData.trackIds
- );
- vm.stopPrank();
- }
-
- // setUri
-
- function test_setUri() public {
- createRelease_setUp();
- vm.startPrank(trackOwner);
- openReleases.setUri(1, "newURI");
- vm.stopPrank();
- string memory newURI = openReleases.uri(1);
-
- assertEq(newURI, "newURI");
- }
-
- function test_setUri_RevertIf_tokenId_is_invalid() public {
- createRelease_setUp();
- vm.expectRevert(OpenReleases.InvalidTokenId.selector);
- vm.startPrank(trackOwner);
- openReleases.setUri(2, "newURI");
- vm.stopPrank();
- }
-
- function test_setUri_RevertIf_caller_is_not_release_owner() public {
- createRelease_setUp();
- address nonReleaseOwner = address(0x2);
- vm.expectRevert(OpenReleases.CallerDoesNotHaveAccess.selector);
- vm.startPrank(nonReleaseOwner);
- openReleases.setUri(1, "newURI");
- vm.stopPrank();
- }
-
- function test_setUri_emits_event() public {
- createRelease_setUp();
- vm.expectEmit(true, true, true, true);
- emit URI("newURI", 1);
- vm.startPrank(trackOwner);
- openReleases.setUri(1, "newURI");
- vm.stopPrank();
- }
-
- // burn
-
- function test_removeRelease() public {
- createRelease_setUp();
-
- vm.startPrank(organizationAdmin);
- openReleases.burn(1);
- vm.stopPrank();
-
- uint256 userBalance = openReleases.balanceOf(trackOwner, 1);
-
- assertEq(openReleases.uri(1), "");
- assertEq(userBalance, 0);
- }
-
- function test_burn_emits_event() public {
- createRelease_setUp();
-
- vm.expectEmit(true, true, true, true);
- emit Burned(1, trackOwner);
- vm.startPrank(organizationAdmin);
- openReleases.burn(1);
- vm.stopPrank();
- }
-
- // royaltyInfo
-
- function test_royaltyInfo() public {
- createRelease_setUp();
- (address receiver, uint256 royaltyAmount) = openReleases.royaltyInfo(1, 100);
- address splitsAddress = splitsFactory.mockSplit();
- assertEq(receiver, splitsAddress);
- assertEq(royaltyAmount, 10);
- }
-
- // supportsInterface
-
- function test_supportsInterface() public {
- bool supportsInterface = openReleases.supportsInterface(type(IOpenReleases).interfaceId);
- assertEq(supportsInterface, true);
- }
-}
diff --git a/moda-contracts/test/OpenReleasesFactory.t.sol b/moda-contracts/test/OpenReleasesFactory.t.sol
deleted file mode 100644
index dd08f429..00000000
--- a/moda-contracts/test/OpenReleasesFactory.t.sol
+++ /dev/null
@@ -1,75 +0,0 @@
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity ^0.8.13;
-
-import {Test, console2} from "forge-std/Test.sol";
-import {OpenReleasesFactory} from "../src/OpenReleasesFactory.sol";
-import {OpenReleases} from "../src/OpenReleases.sol";
-import {Registry} from "../src/Registry.sol";
-import {CatalogFactory} from "../src/CatalogFactory.sol";
-import {Catalog} from "../src/Catalog.sol";
-import {Management} from "../src/Management.sol";
-import {Membership} from "./mocks/MembershipMock.sol";
-import {ISplitsFactory} from "../src/interfaces/ISplitsFactory.sol";
-
-import {Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol";
-
-contract OpenReleasesFactoryTest is Test {
- Registry public registry;
- Membership public membership;
- Management public management;
- OpenReleases public releasesMaster;
- OpenReleasesFactory public openReleasesFactory;
- CatalogFactory public catalogFactory;
- Catalog public catalog;
-
- ISplitsFactory public splitsFactory = ISplitsFactory(address(0x3));
- address public daoAdmin = address(0xa);
- address public organizationAdmin = address(0xa);
- address public releaseAdmin = address(0x6);
- address catalogBeacon;
-
- address payable public treasury = payable(address(0x4));
- string name = "DRIP";
- string symbol = "DRIP";
-
- function setUp() public {
- vm.startPrank(daoAdmin);
-
- management = new Management();
- registry = new Registry(treasury, 1000);
- registry.setManagement(management);
- registry.setSplitsFactory(splitsFactory);
-
- catalogBeacon = Upgrades.deployBeacon("Catalog.sol", daoAdmin);
- catalogFactory = new CatalogFactory(registry, catalogBeacon);
- registry.grantRole(registry.CATALOG_REGISTRAR_ROLE(), address(catalogFactory));
- releasesMaster = new OpenReleases();
- openReleasesFactory = new OpenReleasesFactory(registry, address(releasesMaster));
- registry.grantRole(registry.RELEASES_REGISTRAR_ROLE(), address(openReleasesFactory));
- vm.stopPrank();
-
- vm.startPrank(organizationAdmin);
- membership = new Membership();
- membership.addMember(releaseAdmin);
- catalog = Catalog(catalogFactory.create("DRIP", membership));
- vm.stopPrank();
- }
-
- function test_constructor() public {
- assertEq(address(openReleasesFactory.registry()), address(registry));
- assertEq(openReleasesFactory.releasesMaster(), address(releasesMaster));
- }
-
- function test_create() public {
- vm.startPrank(releaseAdmin);
-
- openReleasesFactory.create(name, symbol, catalog);
- OpenReleases releases = OpenReleases(catalog.getReleasesContract(releaseAdmin));
-
- assertEq(releases.name(), name);
- assertEq(releases.symbol(), symbol);
- assertTrue(releases.hasRole(releases.DEFAULT_ADMIN_ROLE(), releaseAdmin));
-
- vm.stopPrank();
- }
-}
diff --git a/moda-contracts/test/Registry.t.sol b/moda-contracts/test/Registry.t.sol
deleted file mode 100644
index dd169394..00000000
--- a/moda-contracts/test/Registry.t.sol
+++ /dev/null
@@ -1,205 +0,0 @@
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity ^0.8.21;
-
-import {Test, console2} from "forge-std/Test.sol";
-import {IRegistry} from "../src/interfaces/Registry/IRegistry.sol";
-import {IMembership} from "../src/interfaces/IMembership.sol";
-import {ISplitsFactory} from "../src/interfaces/ISplitsFactory.sol";
-import {Registry} from "../src/Registry.sol";
-import "../src/Management.sol";
-import {Membership} from "../test/mocks/MembershipMock.sol";
-import {ERC165Mock} from "../test/mocks/ERC165Mock.sol";
-import {ICatalog} from "../src/interfaces/Catalog/ICatalog.sol";
-
-contract RegistryTest is Test {
- Membership public membership;
- Management public management;
- Registry public registry;
-
- address public catalogRegistrar = address(0x8);
- address public artist = address(0x1);
- ICatalog public catalogAddress = ICatalog(address(0x2));
- address payable public treasuryAddress = payable(address(0x5));
- ISplitsFactory public splitsFactory = ISplitsFactory(address(0x3));
- string public catalogName = "The ACME Catalog";
- uint32 _treasuryFee = 1_000;
-
- event CatalogRegistered(address indexed catalog, address registrar);
- event CatalogUnregistered(address indexed catalog, address registrar);
- event TreasuryFeeChanged(uint32 oldFee, uint32 newFee);
- event TreasuryChanged(address oldTreasury, address newTreasury);
-
- function setUp() public {
- membership = new Membership();
- management = new Management();
- registry = new Registry(treasuryAddress, _treasuryFee);
- registry.setManagement(management);
- registry.setSplitsFactory(splitsFactory);
-
- registry.grantRole(keccak256("CATALOG_REGISTRAR_ROLE"), catalogRegistrar);
- }
-
- // registerCatalog
-
- function test_registerCatalog() public {
- vm.startPrank(catalogRegistrar);
- registry.registerCatalog(catalogAddress);
- vm.stopPrank();
-
- assertTrue(registry.isRegisteredCatalog(address(catalogAddress)));
- }
-
- function test_registerCatalog_RevertIf_catalog_already_registered() public {
- vm.startPrank(catalogRegistrar);
- registry.registerCatalog(catalogAddress);
- vm.stopPrank();
-
- vm.expectRevert(Registry.CatalogAlreadyRegistered.selector);
-
- vm.startPrank(catalogRegistrar);
- registry.registerCatalog(catalogAddress);
- vm.stopPrank();
- }
-
- function test_registerCatalog_RevertIf_registering_registered_catalog() public {
- vm.startPrank(catalogRegistrar);
- registry.registerCatalog(catalogAddress);
- vm.stopPrank();
-
- vm.expectRevert(Registry.CatalogAlreadyRegistered.selector);
-
- vm.startPrank(catalogRegistrar);
- registry.registerCatalog(catalogAddress);
- vm.stopPrank();
- }
-
- function test_registerCatalog_emits_event() public {
- vm.expectEmit(true, true, true, true);
- emit CatalogRegistered(address(catalogAddress), catalogRegistrar);
-
- vm.startPrank(catalogRegistrar);
- registry.registerCatalog(catalogAddress);
- vm.stopPrank();
- }
-
- // unregisterCatalog
-
- function test_unregisterCatalog() public {
- vm.startPrank(catalogRegistrar);
- registry.registerCatalog(catalogAddress);
- vm.stopPrank();
-
- assertTrue(registry.isRegisteredCatalog(address(catalogAddress)));
-
- registry.unregisterCatalog(address(catalogAddress));
- assertFalse(registry.isRegisteredCatalog(address(catalogAddress)));
- }
-
- function test_unregisterCatalog_RevertIf_unregister_unregistered_catalog() public {
- vm.expectRevert(Registry.CatalogIsNotRegistered.selector);
- registry.unregisterCatalog(address(catalogAddress));
- }
-
- function test_unregisterCatalog_emits_event() public {
- vm.startPrank(catalogRegistrar);
- registry.registerCatalog(catalogAddress);
- vm.stopPrank();
-
- vm.expectEmit(true, true, true, true);
- emit CatalogUnregistered(address(catalogAddress), address(this));
- registry.unregisterCatalog(address(catalogAddress));
- }
-
- // getTreasuryInfo
-
- function test_getTreasury() public {
- (address treasury, uint32 fee, uint32 feeScale) = registry.getTreasuryInfo();
-
- assertEq(treasury, treasuryAddress);
- assertEq(fee, _treasuryFee);
- assertEq(feeScale, 10_000);
- }
-
- // setTreasury
-
- function test_setTreasury() public {
- address newTreasury = address(0x6);
- registry.setTreasury(newTreasury);
-
- (address treasury,,) = registry.getTreasuryInfo();
-
- assertEq(treasury, newTreasury);
- }
-
- function test_setTreasury_RevertIf_setTreasury_with_zero_address() public {
- vm.expectRevert(Registry.AddressCannotBeZero.selector);
- registry.setTreasury(address(0));
- }
-
- function test_setTreasury_emits_event() public {
- address newTreasury = address(0x6);
- vm.expectEmit(true, true, true, true);
- emit TreasuryChanged(treasuryAddress, newTreasury);
- registry.setTreasury(newTreasury);
- }
-
- // setTreasuryFee
-
- function test_setTreasuryFee() public {
- uint32 newTreasuryFee = 500;
-
- registry.setTreasuryFee(newTreasuryFee);
- (, uint32 fee,) = registry.getTreasuryInfo();
-
- assertEq(fee, newTreasuryFee);
- }
-
- function test_setTreasuryFee_RevertIf_invalid_amount() public {
- vm.expectRevert("Fee too high");
- registry.setTreasuryFee(1_001);
- }
-
- function test_setTreasuryFee_emits_event() public {
- uint32 newTreasuryFee = 500;
- vm.expectEmit(true, true, true, true);
- emit TreasuryFeeChanged(1000, newTreasuryFee);
- registry.setTreasuryFee(newTreasuryFee);
- }
-
- // getSplitsFactory
-
- function test_getSplitsFactory() public {
- ISplitsFactory splits = registry.getSplitsFactory();
-
- assertEq(address(splits), address(splitsFactory));
- }
-
- // setSplitsFactory
-
- function test_setSplitsFactory() public {
- ISplitsFactory newSplitsFactory = ISplitsFactory(address(0x7));
-
- registry.setSplitsFactory(newSplitsFactory);
- ISplitsFactory splitsFactoryAddress = registry.getSplitsFactory();
-
- assertEq(address(splitsFactoryAddress), address(newSplitsFactory));
- }
-
- // getManagement
-
- function test_getManagement() public {
- IManagement managementAddress = registry.getManagement();
-
- assertEq(address(managementAddress), address(management));
- }
-
- // setManagement
-
- function test_setManagement() public {
- IManagement newManagement = new Management();
- registry.setManagement(newManagement);
- address managementAddress = address(registry.getManagement());
-
- assertEq(managementAddress, address(newManagement));
- }
-}
diff --git a/moda-contracts/test/Releases.t.sol b/moda-contracts/test/Releases.t.sol
deleted file mode 100644
index d17133fd..00000000
--- a/moda-contracts/test/Releases.t.sol
+++ /dev/null
@@ -1,333 +0,0 @@
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity ^0.8.13;
-
-import {Test, console2} from "forge-std/Test.sol";
-import "../src/Registry.sol";
-import "../src/CatalogFactory.sol";
-import "../src/Catalog.sol";
-import "../src/Releases.sol";
-import "../src/Management.sol";
-import "../test/mocks/MembershipMock.sol";
-import "../test/mocks/SplitsFactoryMock.sol";
-import "../src/ReleasesFactory.sol";
-import {Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol";
-
-contract ReleasesTest is Test {
- Membership public membership;
- Management public management;
- Registry public registry;
- SplitsFactoryMock public splitsFactory;
- CatalogFactory public catalogFactory;
- Catalog public catalog;
- Releases public releasesMaster;
- ReleasesFactory public releasesFactory;
- Releases public releases;
-
- address catalogBeacon;
- string public catalogName = "TestCatalog";
-
- string name = "TestReleases";
- string symbol = "TEST";
- address organizationAdmin = address(0x6);
- address releaseAdmin = address(0x1);
-
- address[] releaseAdmins = [releaseAdmin];
- address payable treasury = payable(address(0x5));
-
- error InvalidInitialization();
-
- event ReleaseCreated(uint256 tokenId);
- event ReleaseWithdrawn(address indexed receiver, uint256 tokenId, uint256 amount);
- event URI(string value, uint256 indexed id);
-
- struct TrackRegistrationData {
- address trackBeneficiary;
- string trackRegistrationHash;
- string trackId;
- }
-
- TrackRegistrationData trackRegistrationDataOne = TrackRegistrationData({
- trackBeneficiary: address(0x8),
- trackRegistrationHash: "trackHash1",
- trackId: ""
- });
-
- TrackRegistrationData trackRegistrationDataTwo = TrackRegistrationData({
- trackBeneficiary: address(0x9),
- trackRegistrationHash: "trackHash2",
- trackId: ""
- });
-
- TrackRegistrationData trackRegistrationDataThree = TrackRegistrationData({
- trackBeneficiary: address(0x10),
- trackRegistrationHash: "trackHash3",
- trackId: ""
- });
-
- struct ReleaseData {
- uint96 royaltyAmount;
- string uri;
- uint256 amount;
- string[] trackIds;
- }
-
- ReleaseData releaseData = ReleaseData(1000, "testURI", 100, new string[](0));
-
- function setUp() public {
- management = new Management();
- membership = new Membership();
- splitsFactory = new SplitsFactoryMock(address(0x3));
- registry = new Registry(treasury, 1000);
- registry.setManagement(management);
- registry.setSplitsFactory(splitsFactory);
-
- catalogBeacon = Upgrades.deployBeacon("Catalog.sol", organizationAdmin);
- catalogFactory = new CatalogFactory(registry, catalogBeacon);
- registry.grantRole(registry.CATALOG_REGISTRAR_ROLE(), address(catalogFactory));
-
- vm.startPrank(releaseAdmin);
- catalog = Catalog(catalogFactory.create(catalogName, IMembership(membership)));
- vm.stopPrank();
- membership.addMember(releaseAdmin);
-
- releasesMaster = new Releases();
- releasesFactory = new ReleasesFactory(registry, address(releasesMaster));
- registry.grantRole(registry.RELEASES_REGISTRAR_ROLE(), address(releasesFactory));
-
- vm.startPrank(releaseAdmin);
- releasesFactory.create(releaseAdmins, name, symbol, catalog);
- vm.stopPrank();
-
- releases = Releases(catalog.getReleasesContract(releaseAdmin));
- }
-
- // Initialization
-
- function test_initialize() public {
- uint256 numberOfReleases = releases.numberOfReleases();
-
- assertEq(releases.name(), name);
- assertEq(releases.symbol(), symbol);
- assertEq(numberOfReleases, 0);
- }
-
- function test_initialize_RevertIf_already_initialized() public {
- vm.expectRevert(InvalidInitialization.selector);
-
- releases.initialize(organizationAdmin, releaseAdmins, name, symbol, catalog, splitsFactory);
- }
-
- // create with a curated Releases contract
-
- function registerTrack_setUp() public {
- vm.startPrank(releaseAdmin);
- catalog.registerTrack(
- releaseAdmin,
- trackRegistrationDataOne.trackBeneficiary,
- trackRegistrationDataOne.trackRegistrationHash
- );
- catalog.registerTrack(
- releaseAdmin,
- trackRegistrationDataTwo.trackBeneficiary,
- trackRegistrationDataTwo.trackRegistrationHash
- );
- catalog.registerTrack(
- releaseAdmin,
- trackRegistrationDataThree.trackBeneficiary,
- trackRegistrationDataThree.trackRegistrationHash
- );
-
- trackRegistrationDataOne.trackId =
- catalog.getTrackId(trackRegistrationDataOne.trackRegistrationHash);
- trackRegistrationDataTwo.trackId =
- catalog.getTrackId(trackRegistrationDataTwo.trackRegistrationHash);
- trackRegistrationDataThree.trackId =
- catalog.getTrackId(trackRegistrationDataThree.trackRegistrationHash);
-
- releaseData.trackIds.push(trackRegistrationDataOne.trackId);
- releaseData.trackIds.push(trackRegistrationDataTwo.trackId);
- releaseData.trackIds.push(trackRegistrationDataThree.trackId);
-
- vm.stopPrank();
- }
-
- function approve_Tracks_setUp() public {
- vm.startPrank(releaseAdmin);
- catalog.setReleasesApprovalForAll(releaseAdmin, address(releases), true);
-
- vm.stopPrank();
- }
-
- function createRelease_setUp() public {
- registerTrack_setUp();
- approve_Tracks_setUp();
- vm.startPrank(releaseAdmin);
- releases.create(
- releaseAdmin,
- releaseData.royaltyAmount,
- releaseData.uri,
- releaseData.amount,
- releaseData.trackIds
- );
- vm.stopPrank();
- }
-
- function test_create_release_curated() public {
- createRelease_setUp();
- bytes32 releaseHash = catalog.getReleaseHash(address(releases), 1);
- uint256 numberOfReleases = releases.numberOfReleases();
-
- Catalog.RegisteredRelease memory registeredRelease = catalog.getRegisteredRelease(releaseHash);
-
- assertEq(numberOfReleases, 1);
- assertEq(registeredRelease.releases, address(releases));
- assertEq(registeredRelease.tokenId, 1);
- assertEq(registeredRelease.trackIds[0], trackRegistrationDataOne.trackId);
- assertEq(registeredRelease.trackIds[1], trackRegistrationDataTwo.trackId);
- assertEq(registeredRelease.trackIds[2], trackRegistrationDataThree.trackId);
- }
-
- function test_create_RevertIf_caller_is_not_release_admin() public {
- registerTrack_setUp();
- approve_Tracks_setUp();
- address nonAdmin = address(0x2);
- vm.expectRevert(Releases.CallerIsNotReleaseAdmin.selector);
- vm.startPrank(nonAdmin);
- releases.create(
- address(0x1),
- releaseData.royaltyAmount,
- releaseData.uri,
- releaseData.amount,
- releaseData.trackIds
- );
- vm.stopPrank();
- }
-
- function test_create_RevertIf_royalty_amount_is_over_2000() public {
- registerTrack_setUp();
- approve_Tracks_setUp();
- vm.expectRevert(Releases.InvalidRoyaltyAmount.selector);
- vm.startPrank(releaseAdmin);
- releases.create(address(0x1), 2001, releaseData.uri, releaseData.amount, releaseData.trackIds);
- vm.stopPrank();
- }
-
- function test_create_emits_event() public {
- registerTrack_setUp();
- approve_Tracks_setUp();
-
- vm.expectEmit(true, true, true, true);
- emit ReleaseCreated(1);
- vm.startPrank(releaseAdmin);
- releases.create(
- releaseAdmin,
- releaseData.royaltyAmount,
- releaseData.uri,
- releaseData.amount,
- releaseData.trackIds
- );
- vm.stopPrank();
- }
-
- // withdrawRelease
-
- function test_withdrawRelease() public {
- registerTrack_setUp();
- approve_Tracks_setUp();
- vm.startPrank(releaseAdmin);
- releases.create(
- address(releases),
- releaseData.royaltyAmount,
- releaseData.uri,
- releaseData.amount,
- releaseData.trackIds
- );
-
- releases.withdrawRelease(releaseAdmin, 1, 100);
- vm.stopPrank();
- uint256 balance = releases.balanceOf(releaseAdmin, 1);
-
- assertEq(balance, 100);
- }
-
- function test_withdrawRelease_RevertIf_tokenId_is_invalid() public {
- registerTrack_setUp();
- approve_Tracks_setUp();
- vm.startPrank(releaseAdmin);
- releases.create(
- address(releases),
- releaseData.royaltyAmount,
- releaseData.uri,
- releaseData.amount,
- releaseData.trackIds
- );
-
- vm.expectRevert(Releases.InvalidTokenId.selector);
- releases.withdrawRelease(releaseAdmin, 2, 100);
- vm.stopPrank();
- }
-
- function test_withdrawRelease_emits_event() public {
- registerTrack_setUp();
- approve_Tracks_setUp();
- vm.startPrank(releaseAdmin);
- releases.create(
- address(releases),
- releaseData.royaltyAmount,
- releaseData.uri,
- releaseData.amount,
- releaseData.trackIds
- );
-
- vm.expectEmit(true, true, true, true);
- emit ReleaseWithdrawn(releaseAdmin, 1, 100);
- releases.withdrawRelease(releaseAdmin, 1, 100);
- vm.stopPrank();
- }
-
- // setUri
-
- function test_setUri() public {
- createRelease_setUp();
- vm.startPrank(releaseAdmin);
- releases.setUri(1, "newURI");
- vm.stopPrank();
- string memory newURI = releases.uri(1);
-
- assertEq(newURI, "newURI");
- }
-
- function test_setUri_RevertIf_tokenId_is_invalid() public {
- createRelease_setUp();
- vm.expectRevert(Releases.InvalidTokenId.selector);
- vm.startPrank(releaseAdmin);
- releases.setUri(2, "newURI");
- vm.stopPrank();
- }
-
- function test_setUri_emits_event() public {
- createRelease_setUp();
- vm.expectEmit(true, true, true, true);
- emit URI("newURI", 1);
- vm.startPrank(releaseAdmin);
- releases.setUri(1, "newURI");
- vm.stopPrank();
- }
-
- // royaltyInfo
-
- function test_royaltyInfo() public {
- createRelease_setUp();
- (address receiver, uint256 royaltyAmount) = releases.royaltyInfo(1, 100);
- address splitsAddress = splitsFactory.mockSplit();
- assertEq(receiver, splitsAddress);
- assertEq(royaltyAmount, 10);
- }
-
- // supportsInterface
-
- function test_supportsInterface() public {
- bool supportsInterface = releases.supportsInterface(type(IReleases).interfaceId);
- assertEq(supportsInterface, true);
- }
-}
diff --git a/moda-contracts/test/ReleasesFactory.t.sol b/moda-contracts/test/ReleasesFactory.t.sol
deleted file mode 100644
index 326cd43f..00000000
--- a/moda-contracts/test/ReleasesFactory.t.sol
+++ /dev/null
@@ -1,68 +0,0 @@
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity ^0.8.13;
-
-import {Test, console2} from "forge-std/Test.sol";
-import "../src/ReleasesFactory.sol";
-import "../src/Releases.sol";
-import "../src/Registry.sol";
-import "../src/CatalogFactory.sol";
-import "../src/Catalog.sol";
-import "../src/Management.sol";
-import "./mocks/MembershipMock.sol";
-import {ISplitsFactory} from "../src/interfaces/ISplitsFactory.sol";
-
-import {Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol";
-
-contract ReleasesFactoryTest is Test {
- Registry public registry;
- Membership public membership;
- Management public management;
- Releases public releasesMaster;
- ReleasesFactory public releasesFactory;
- CatalogFactory public catalogFactory;
- Catalog public catalog;
-
- ISplitsFactory public splitsFactory = ISplitsFactory(address(0x3));
- address public admin = address(0xa);
- address catalogBeacon;
-
- address payable public treasury = payable(address(0x4));
- string name = "DRIP";
- string symbol = "DRIP";
-
- address public releasesOwner = address(0x5);
- address public releaseAdmin = address(0x6);
-
- function setUp() public {
- management = new Management();
- registry = new Registry(treasury, 1000);
- registry.setManagement(management);
- registry.setSplitsFactory(splitsFactory);
-
- catalogBeacon = Upgrades.deployBeacon("Catalog.sol", admin);
- catalogFactory = new CatalogFactory(registry, catalogBeacon);
- registry.grantRole(registry.CATALOG_REGISTRAR_ROLE(), address(catalogFactory));
-
- catalog = Catalog(catalogFactory.create(name, IMembership(membership)));
-
- releasesMaster = new Releases();
- releasesFactory = new ReleasesFactory(registry, address(releasesMaster));
-
- registry.grantRole(keccak256("RELEASES_REGISTRAR_ROLE"), address(releasesFactory));
- }
-
- function test_constructor() public {
- assertEq(address(releasesFactory.registry()), address(registry));
- assertEq(releasesFactory.releasesMaster(), address(releasesMaster));
- }
-
- function test_create() public {
- address[] memory releaseAdmins = new address[](1);
- releaseAdmins[0] = releaseAdmin;
- releasesFactory.create(releaseAdmins, name, symbol, catalog);
- address releases = catalog.getReleasesContract(address(this));
-
- assertEq(Releases(releases).name(), name);
- assertEq(Releases(releases).symbol(), symbol);
- }
-}
diff --git a/packages/drop-sdk/package.json b/packages/drop-sdk/package.json
index 87b6a0be..ce4f05f4 100644
--- a/packages/drop-sdk/package.json
+++ b/packages/drop-sdk/package.json
@@ -1,22 +1,21 @@
{
"name": "drop-sdk",
"keywords": [
- "MODA",
"Music3",
"Music",
"Distribution",
"Royalties",
"NFTs",
- "Polygon",
+ "Base",
"Ethereum",
"Web3"
],
"version": "0.0.1",
"main": "dist/index.js",
"module": "dist/index.mjs",
- "description": "A JavaScript library for MODA protocol",
+ "description": "A JavaScript library for DROP protocol",
"license": "Apache-2.0",
- "repository": "github:modadao/moda-protocol",
+ "repository": "github:DROPcmdk/DROPcmdk",
"types": "dist/index.d.ts",
"scripts": {
"build": "wagmi generate && tsup",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 9e2d2fc1..602f577a 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -181,7 +181,11 @@ importers:
specifier: ^5
version: 5.4.2
- moda-contracts: {}
+ contracts:
+ dependencies:
+ '@openzeppelin/upgrades-core':
+ specifier: ^1.33.1
+ version: 1.33.1
packages/drop-sdk:
dependencies:
@@ -3362,6 +3366,22 @@ packages:
'@nodelib/fs.scandir': 2.1.5
fastq: 1.17.1
+ /@openzeppelin/upgrades-core@1.33.1:
+ resolution: {integrity: sha512-YRxIRhTY1b+j7+NUUu8Uuem5ugxKexEMVd8dBRWNgWeoN1gS1OCrhgUg0ytL+54vzQ+SGWZDfNnzjVuI1Cj1Zw==}
+ hasBin: true
+ dependencies:
+ cbor: 9.0.2
+ chalk: 4.1.2
+ compare-versions: 6.1.0
+ debug: 4.3.4
+ ethereumjs-util: 7.1.5
+ minimist: 1.2.8
+ proper-lockfile: 4.1.2
+ solidity-ast: 0.4.56
+ transitivePeerDependencies:
+ - supports-color
+ dev: false
+
/@parcel/watcher-android-arm64@2.4.1:
resolution: {integrity: sha512-LOi/WTbbh3aTn2RYddrO8pnapixAziFl6SMxHM69r3tvdSm94JtCenaKgk1GRg5FJ5wpMCpHeW+7yqPlvZv7kg==}
engines: {node: '>= 10.0.0'}
@@ -5926,7 +5946,6 @@ packages:
dependencies:
call-bind: 1.0.7
is-array-buffer: 3.0.4
- dev: true
/array-includes@3.1.7:
resolution: {integrity: sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==}
@@ -5964,7 +5983,6 @@ packages:
es-abstract: 1.22.5
es-errors: 1.3.0
es-shim-unscopables: 1.0.2
- dev: true
/array.prototype.findlastindex@1.2.4:
resolution: {integrity: sha512-hzvSHUshSpCflDR1QMUBLHGHP1VIEBegT4pix9H/Z92Xw3ySoy6c2qh7lJWTJnRJ8JCZ9bJNCgTyYaJGcJu6xQ==}
@@ -6028,7 +6046,6 @@ packages:
get-intrinsic: 1.2.4
is-array-buffer: 3.0.4
is-shared-array-buffer: 1.0.3
- dev: true
/arrify@1.0.1:
resolution: {integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==}
@@ -6458,6 +6475,13 @@ packages:
upper-case-first: 2.0.2
dev: true
+ /cbor@9.0.2:
+ resolution: {integrity: sha512-JPypkxsB10s9QOWwa6zwPzqE1Md3vqpPc+cai4sAecuCsRyAtAl/pMyhPlMbT/xtPnm2dznJZYRLui57qiRhaQ==}
+ engines: {node: '>=16'}
+ dependencies:
+ nofilter: 3.1.0
+ dev: false
+
/chalk@2.4.2:
resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
engines: {node: '>=4'}
@@ -6722,6 +6746,10 @@ packages:
resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==}
dev: false
+ /compare-versions@6.1.0:
+ resolution: {integrity: sha512-LNZQXhqUvqUTotpZ00qLSaify3b4VFD588aRr8MKFw4CMUr98ytzCW5wDH5qx/DEY5kCDXcbcRuCqL0szEf2tg==}
+ dev: false
+
/compressible@2.0.18:
resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==}
engines: {node: '>= 0.6'}
@@ -7023,7 +7051,6 @@ packages:
define-data-property: 1.1.4
has-property-descriptors: 1.0.2
object-keys: 1.1.1
- dev: true
/defu@6.1.4:
resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==}
@@ -7318,7 +7345,6 @@ packages:
typed-array-length: 1.0.5
unbox-primitive: 1.0.2
which-typed-array: 1.1.14
- dev: true
/es-array-method-boxes-properly@1.0.0:
resolution: {integrity: sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==}
@@ -7362,13 +7388,11 @@ packages:
get-intrinsic: 1.2.4
has-tostringtag: 1.0.2
hasown: 2.0.1
- dev: true
/es-shim-unscopables@1.0.2:
resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==}
dependencies:
hasown: 2.0.1
- dev: true
/es-to-primitive@1.2.1:
resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==}
@@ -7377,7 +7401,6 @@ packages:
is-callable: 1.2.7
is-date-object: 1.0.5
is-symbol: 1.0.4
- dev: true
/esbuild@0.19.12:
resolution: {integrity: sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==}
@@ -8157,11 +8180,9 @@ packages:
define-properties: 1.2.1
es-abstract: 1.22.5
functions-have-names: 1.2.3
- dev: true
/functions-have-names@1.2.3:
resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==}
- dev: true
/futoin-hkdf@1.5.3:
resolution: {integrity: sha512-SewY5KdMpaoCeh7jachEWFsh1nNlaDjNHZXWqL5IGwtpEYHTgkr2+AMCgNwKWkcc0wpSYrZfR7he4WdmHFtDxQ==}
@@ -8211,7 +8232,6 @@ packages:
call-bind: 1.0.7
es-errors: 1.3.0
get-intrinsic: 1.2.4
- dev: true
/get-tsconfig@4.7.3:
resolution: {integrity: sha512-ZvkrzoUA0PQZM6fy6+/Hce561s+faD1rsNwhnO5FelNjyy7EMGJ3Rz1AQ8GYDWjhRs/7dBLOEJvhK8MiEJOAFg==}
@@ -8284,7 +8304,6 @@ packages:
engines: {node: '>= 0.4'}
dependencies:
define-properties: 1.2.1
- dev: true
/globby@11.1.0:
resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==}
@@ -8349,7 +8368,6 @@ packages:
/has-bigints@1.0.2:
resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==}
- dev: true
/has-flag@3.0.0:
resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
@@ -8573,7 +8591,6 @@ packages:
es-errors: 1.3.0
hasown: 2.0.1
side-channel: 1.0.6
- dev: true
/invariant@2.2.4:
resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==}
@@ -8621,7 +8638,6 @@ packages:
dependencies:
call-bind: 1.0.7
get-intrinsic: 1.2.4
- dev: true
/is-arrayish@0.2.1:
resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==}
@@ -8637,7 +8653,6 @@ packages:
resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==}
dependencies:
has-bigints: 1.0.2
- dev: true
/is-binary-path@2.1.0:
resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
@@ -8651,7 +8666,6 @@ packages:
dependencies:
call-bind: 1.0.7
has-tostringtag: 1.0.2
- dev: true
/is-callable@1.2.7:
resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==}
@@ -8667,7 +8681,6 @@ packages:
engines: {node: '>= 0.4'}
dependencies:
has-tostringtag: 1.0.2
- dev: true
/is-directory@0.3.1:
resolution: {integrity: sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw==}
@@ -8765,14 +8778,12 @@ packages:
/is-negative-zero@2.0.3:
resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==}
engines: {node: '>= 0.4'}
- dev: true
/is-number-object@1.0.7:
resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==}
engines: {node: '>= 0.4'}
dependencies:
has-tostringtag: 1.0.2
- dev: true
/is-number@7.0.0:
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
@@ -8806,7 +8817,6 @@ packages:
dependencies:
call-bind: 1.0.7
has-tostringtag: 1.0.2
- dev: true
/is-set@2.0.2:
resolution: {integrity: sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==}
@@ -8817,7 +8827,6 @@ packages:
engines: {node: '>= 0.4'}
dependencies:
call-bind: 1.0.7
- dev: true
/is-stream@2.0.1:
resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
@@ -8832,7 +8841,6 @@ packages:
engines: {node: '>= 0.4'}
dependencies:
has-tostringtag: 1.0.2
- dev: true
/is-subdir@1.2.0:
resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==}
@@ -8846,7 +8854,6 @@ packages:
engines: {node: '>= 0.4'}
dependencies:
has-symbols: 1.0.3
- dev: true
/is-typed-array@1.1.13:
resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==}
@@ -8872,7 +8879,6 @@ packages:
resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==}
dependencies:
call-bind: 1.0.7
- dev: true
/is-weakset@2.0.2:
resolution: {integrity: sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==}
@@ -8918,7 +8924,6 @@ packages:
/isarray@2.0.5:
resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==}
- dev: true
/isexe@2.0.0:
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
@@ -10176,6 +10181,11 @@ packages:
engines: {node: '>=0.12.0'}
dev: false
+ /nofilter@3.1.0:
+ resolution: {integrity: sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g==}
+ engines: {node: '>=12.19'}
+ dev: false
+
/normalize-package-data@2.5.0:
resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==}
dependencies:
@@ -10241,12 +10251,10 @@ packages:
/object-inspect@1.13.1:
resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==}
- dev: true
/object-keys@1.1.1:
resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==}
engines: {node: '>= 0.4'}
- dev: true
/object.assign@4.1.5:
resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==}
@@ -10256,7 +10264,6 @@ packages:
define-properties: 1.2.1
has-symbols: 1.0.3
object-keys: 1.1.1
- dev: true
/object.entries@1.1.7:
resolution: {integrity: sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==}
@@ -10877,6 +10884,14 @@ packages:
object-assign: 4.1.1
react-is: 16.13.1
+ /proper-lockfile@4.1.2:
+ resolution: {integrity: sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==}
+ dependencies:
+ graceful-fs: 4.2.11
+ retry: 0.12.0
+ signal-exit: 3.0.7
+ dev: false
+
/proxy-compare@2.5.1:
resolution: {integrity: sha512-oyfc0Tx87Cpwva5ZXezSp5V9vht1c7dZBhvuV/y3ctkgMVUmiAGDVeeB0dKhGSyT0v1ZTEQYpe/RXlBVBNuCLA==}
dev: false
@@ -11347,7 +11362,6 @@ packages:
define-properties: 1.2.1
es-errors: 1.3.0
set-function-name: 2.0.2
- dev: true
/regexpu-core@5.3.2:
resolution: {integrity: sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==}
@@ -11426,12 +11440,18 @@ packages:
signal-exit: 3.0.7
dev: true
+ /retry@0.12.0:
+ resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==}
+ engines: {node: '>= 4'}
+ dev: false
+
/reusify@1.0.4:
resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
/rimraf@2.6.3:
resolution: {integrity: sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==}
+ deprecated: Rimraf versions prior to v4 are no longer supported
hasBin: true
dependencies:
glob: 7.2.3
@@ -11509,7 +11529,6 @@ packages:
get-intrinsic: 1.2.4
has-symbols: 1.0.3
isarray: 2.0.5
- dev: true
/safe-buffer@5.1.2:
resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==}
@@ -11525,7 +11544,6 @@ packages:
call-bind: 1.0.7
es-errors: 1.3.0
is-regex: 1.1.4
- dev: true
/safe-stable-stringify@2.4.3:
resolution: {integrity: sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==}
@@ -11655,7 +11673,6 @@ packages:
es-errors: 1.3.0
functions-have-names: 1.2.3
has-property-descriptors: 1.0.2
- dev: true
/setimmediate@1.0.5:
resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==}
@@ -11714,7 +11731,6 @@ packages:
es-errors: 1.3.0
get-intrinsic: 1.2.4
object-inspect: 1.13.1
- dev: true
/signal-exit@3.0.7:
resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
@@ -11789,6 +11805,12 @@ packages:
- supports-color
dev: false
+ /solidity-ast@0.4.56:
+ resolution: {integrity: sha512-HgmsA/Gfklm/M8GFbCX/J1qkVH0spXHgALCNZ8fA8x5X+MFdn/8CP2gr5OVyXjXw6RZTPC/Sxl2RUDQOXyNMeA==}
+ dependencies:
+ array.prototype.findlast: 1.2.4
+ dev: false
+
/sonic-boom@2.8.0:
resolution: {integrity: sha512-kuonw1YOYYNOve5iHdSahXPOK49GqwA+LZhI6Wz/l0rP57iKyXXIHaRagOBHAPmGwJC6od2Z9zgvZ5loSgMlVg==}
dependencies:
@@ -11970,7 +11992,6 @@ packages:
call-bind: 1.0.7
define-properties: 1.2.1
es-abstract: 1.22.5
- dev: true
/string.prototype.trimend@1.0.7:
resolution: {integrity: sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==}
@@ -11978,7 +11999,6 @@ packages:
call-bind: 1.0.7
define-properties: 1.2.1
es-abstract: 1.22.5
- dev: true
/string.prototype.trimstart@1.0.7:
resolution: {integrity: sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==}
@@ -11986,7 +12006,6 @@ packages:
call-bind: 1.0.7
define-properties: 1.2.1
es-abstract: 1.22.5
- dev: true
/string_decoder@1.0.3:
resolution: {integrity: sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==}
@@ -12441,7 +12460,6 @@ packages:
call-bind: 1.0.7
es-errors: 1.3.0
is-typed-array: 1.1.13
- dev: true
/typed-array-byte-length@1.0.1:
resolution: {integrity: sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==}
@@ -12452,7 +12470,6 @@ packages:
gopd: 1.0.1
has-proto: 1.0.3
is-typed-array: 1.1.13
- dev: true
/typed-array-byte-offset@1.0.2:
resolution: {integrity: sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==}
@@ -12464,7 +12481,6 @@ packages:
gopd: 1.0.1
has-proto: 1.0.3
is-typed-array: 1.1.13
- dev: true
/typed-array-length@1.0.5:
resolution: {integrity: sha512-yMi0PlwuznKHxKmcpoOdeLwxBoVPkqZxd7q2FgMkmD3bNwvF5VW0+UlUQ1k1vmktTu4Yu13Q0RIxEP8+B+wloA==}
@@ -12476,7 +12492,6 @@ packages:
has-proto: 1.0.3
is-typed-array: 1.1.13
possible-typed-array-names: 1.0.0
- dev: true
/typescript@5.4.2:
resolution: {integrity: sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==}
@@ -12500,7 +12515,6 @@ packages:
has-bigints: 1.0.2
has-symbols: 1.0.3
which-boxed-primitive: 1.0.2
- dev: true
/uncrypto@0.1.3:
resolution: {integrity: sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==}
@@ -12974,7 +12988,6 @@ packages:
is-number-object: 1.0.7
is-string: 1.0.7
is-symbol: 1.0.4
- dev: true
/which-builtin-type@1.1.3:
resolution: {integrity: sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==}
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index 8aea52c4..ed55fea3 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -1,4 +1,4 @@
packages:
- - "moda-contracts"
+ - "contracts"
- "packages/*"
- "apps/*"