diff --git a/.env.exemple b/.env.exemple new file mode 100644 index 0000000..ed1181c --- /dev/null +++ b/.env.exemple @@ -0,0 +1,15 @@ +# Current foundry profile +FOUNDRY_PROFILE="default" + +# Private key for deployment +DEPLOY_PRIV_KEY="DEPLOY_PRIV_KEY" + +# Etherscan scan api key +API_KEY_ETHERSCAN="API_KEY_ETHERSCAN" +API_KEY_POLYGONSCAN="API_KEY_POLYGONSCAN" + +# Api key's for rpc provider +API_KEY_ALCHEMY_MAINNET="API_KEY_ALCHEMY_MAINNET" +API_KEY_ALCHEMY_GOERLI="API_KEY_ALCHEMY_POLYGON" +API_KEY_ALCHEMY_POLYGON="API_KEY_ALCHEMY_POLYGON" +API_KEY_ALCHEMY_MUMBAI="API_KEY_ALCHEMY_MUMBAI" diff --git a/.gas-snapshot b/.gas-snapshot index 2ce90ff..7bfe5c1 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -3,34 +3,34 @@ ContentPoolTest:test_fail_InitTwice() (gas: 15855) ContentPoolTest:test_fail_addReward_ContractPaused() (gas: 44773) ContentPoolTest:test_fail_addReward_NoReward() (gas: 30849) ContentPoolTest:test_fail_addReward_NotAuthorized() (gas: 15442) -ContentPoolTest:test_fullProcess() (gas: 200143) -ContentPoolTest:test_transferFraktion() (gas: 858926) +ContentPoolTest:test_fullProcess() (gas: 200092) +ContentPoolTest:test_transferFraktion() (gas: 858494) ErrorGasUsageTest:testFail_RevertWithError() (gas: 5419) ErrorGasUsageTest:testFail_RevertWithErrorAssembly() (gas: 5363) ErrorGasUsageTest:testFail_RevertWithString() (gas: 5451) EventGasUsageTest:test_event() (gas: 7243) EventGasUsageTest:test_eventAssembly() (gas: 7183) -FrakTreasuryWalletTest:testFuzz_multicall(uint256,address,address) (runs: 2048, μ: 172897, ~: 173740) -FrakTreasuryWalletTest:testFuzz_transfer(address,uint96) (runs: 2048, μ: 143377, ~: 143100) -FrakTreasuryWalletTest:testFuzz_transferBatch(address,uint96) (runs: 2048, μ: 144834, ~: 144557) +FrakTreasuryWalletTest:testFuzz_multicall(uint256,address,address) (runs: 1000, μ: 172665, ~: 173359) +FrakTreasuryWalletTest:testFuzz_transfer(address,uint96) (runs: 1000, μ: 143100, ~: 142860) +FrakTreasuryWalletTest:testFuzz_transferBatch(address,uint96) (runs: 1000, μ: 144557, ~: 144317) FrakTreasuryWalletTest:test_fail_initialize_CantInitTwice() (gas: 15876) FrakTreasuryWalletTest:test_fail_multicall_NotAuthorized() (gas: 21060) FrakTreasuryWalletTest:test_fail_transferBatch_ContractPaused() (gas: 43432) FrakTreasuryWalletTest:test_fail_transferBatch_InvalidArray() (gas: 21612) FrakTreasuryWalletTest:test_fail_transferBatch_InvalidArray_Empty() (gas: 21363) FrakTreasuryWalletTest:test_fail_transferBatch_NoReward() (gas: 34254) -FrakTreasuryWalletTest:test_fail_transferBatch_NotEnoughTreasury() (gas: 11582305) +FrakTreasuryWalletTest:test_fail_transferBatch_NotEnoughTreasury() (gas: 11481721) FrakTreasuryWalletTest:test_fail_transferBatch_NotMinter() (gas: 18581) FrakTreasuryWalletTest:test_fail_transferBatch_RewardTooLarge() (gas: 34213) FrakTreasuryWalletTest:test_fail_transfer_ContractPaused() (gas: 42500) FrakTreasuryWalletTest:test_fail_transfer_InvalidAddress() (gas: 20503) FrakTreasuryWalletTest:test_fail_transfer_NoReward() (gas: 20486) -FrakTreasuryWalletTest:test_fail_transfer_NotEnoughTreasury() (gas: 11581282) +FrakTreasuryWalletTest:test_fail_transfer_NotEnoughTreasury() (gas: 11480698) FrakTreasuryWalletTest:test_fail_transfer_NotMinter() (gas: 17674) FrakTreasuryWalletTest:test_fail_transfer_RewardTooLarge() (gas: 20556) -FrakTreasuryWalletTest:test_multicall() (gas: 146737) -FrakTreasuryWalletTest:test_transfer() (gas: 140674) -FrakTreasuryWalletTest:test_transferBatch() (gas: 142135) +FrakTreasuryWalletTest:test_multicall() (gas: 146356) +FrakTreasuryWalletTest:test_transfer() (gas: 140434) +FrakTreasuryWalletTest:test_transferBatch() (gas: 141895) FraktionTokensTest:test_fail_initialize_CantInitTwice() (gas: 16052) FraktionTokensTest:test_fail_mintNewContent_ContractPaused() (gas: 50399) FraktionTokensTest:test_fail_mintNewContent_InvalidAddress() (gas: 121515) @@ -41,31 +41,31 @@ FraktionTokensTest:test_fail_mint_NotAuthorized() (gas: 15570) FraktionTokensTest:test_mint() (gas: 49772) FraktionTokensTest:test_mintNewContent() (gas: 152698) FraktionTokensTest:test_transfer() (gas: 113412) -FrkTokenL2Test:invariant_metadata() (runs: 256, calls: 3840, reverts: 3302) -FrkTokenL2Test:invariant_supplyCap() (runs: 256, calls: 3840, reverts: 3287) -FrkTokenL2Test:testFuzz_mint(uint256) (runs: 2048, μ: 65596, ~: 68609) -FrkTokenL2Test:testFuzz_transfer(address,uint256) (runs: 2048, μ: 73360, ~: 76781) -FrkTokenL2Test:test_approve_increase_decrease() (gas: 49570) -FrkTokenL2Test:test_balanceOf() (gas: 55824) +FrkTokenL2Test:invariant_metadata() (runs: 256, calls: 3840, reverts: 3327) +FrkTokenL2Test:invariant_supplyCap() (runs: 256, calls: 3840, reverts: 3319) +FrkTokenL2Test:testFuzz_mint(uint256) (runs: 1000, μ: 65644, ~: 68510) +FrkTokenL2Test:testFuzz_transfer(address,uint256) (runs: 1000, μ: 73237, ~: 76541) +FrkTokenL2Test:test_approve_increase_decrease() (gas: 49277) +FrkTokenL2Test:test_balanceOf() (gas: 55674) FrkTokenL2Test:test_decimals() (gas: 10426) -FrkTokenL2Test:test_fail_approve_InvalidAddress() (gas: 13680) -FrkTokenL2Test:test_fail_decreaseAllowance_BelowZero() (gas: 15939) -FrkTokenL2Test:test_fail_increaseAllowance_InvalidAddress() (gas: 16102) -FrkTokenL2Test:test_fail_initialize_CantInitTwice() (gas: 15898) -FrkTokenL2Test:test_fail_mint_Addr0() (gas: 23001) -FrkTokenL2Test:test_fail_mint_NotMinter() (gas: 15681) -FrkTokenL2Test:test_fail_mint_TooLarge() (gas: 22838) +FrkTokenL2Test:test_fail_approve_InvalidAddress() (gas: 13584) +FrkTokenL2Test:test_fail_decreaseAllowance_BelowZero() (gas: 15859) +FrkTokenL2Test:test_fail_increaseAllowance_InvalidAddress() (gas: 15985) +FrkTokenL2Test:test_fail_initialize_CantInitTwice() (gas: 15803) +FrkTokenL2Test:test_fail_mint_Addr0() (gas: 22902) +FrkTokenL2Test:test_fail_mint_NotMinter() (gas: 15582) +FrkTokenL2Test:test_fail_mint_TooLarge() (gas: 22739) FrkTokenL2Test:test_fail_permit_InvalidAddress() (gas: 44471) FrkTokenL2Test:test_fail_permit_InvalidSigner() (gas: 44261) FrkTokenL2Test:test_fail_permit_PermitDelayExpired() (gas: 18484) -FrkTokenL2Test:test_fail_transfer_InvalidAddress() (gas: 68746) -FrkTokenL2Test:test_fail_transfer_NotEnoughBalance() (gas: 68938) +FrkTokenL2Test:test_fail_transfer_InvalidAddress() (gas: 68506) +FrkTokenL2Test:test_fail_transfer_NotEnoughBalance() (gas: 68698) FrkTokenL2Test:test_name() (gas: 14755) FrkTokenL2Test:test_permit() (gas: 69802) -FrkTokenL2Test:test_symbol() (gas: 14796) -FrkTokenL2Test:test_totalSupply() (gas: 55308) -FrkTokenL2Test:test_transfer() (gas: 83142) -FrkTokenL2Test:test_transferFrom(uint256) (runs: 2048, μ: 90524, ~: 94316) +FrkTokenL2Test:test_symbol() (gas: 14818) +FrkTokenL2Test:test_totalSupply() (gas: 55159) +FrkTokenL2Test:test_transfer() (gas: 82761) +FrkTokenL2Test:test_transferFrom(uint256) (runs: 1000, μ: 90425, ~: 94084) IsZeroGasUsageTest:test_isZero() (gas: 5440) IsZeroGasUsageTest:test_isZeroAssembly() (gas: 5419) LoopUsageTest:test() (gas: 15621) @@ -79,74 +79,76 @@ MinterTest:test_fail_increaseSupply_ContractPaused() (gas: 44811) MinterTest:test_fail_increaseSupply_NotAuthorized() (gas: 15536) MinterTest:test_fail_increaseSupply_RemainingSupply() (gas: 285155) MinterTest:test_fail_increaseSupply_SupplyUpdateNotAllowed() (gas: 288632) -MinterTest:test_fail_mintFractionForUser_InvalidSigner() (gas: 414553) -MinterTest:test_fail_mintFraktionForUser_ContractPaused() (gas: 402386) -MinterTest:test_fail_mintFraktionForUser_InsuficiantSupply() (gas: 448799) -MinterTest:test_fail_mintFraktionForUser_InvalidFraktionType() (gas: 383555) -MinterTest:test_fail_mintFraktionForUser_NotAuthorized() (gas: 378904) -MinterTest:test_fail_mintFraktion_ContractPaused() (gas: 401832) -MinterTest:test_fail_mintFraktion_TooManyFraktion() (gas: 481343) +MinterTest:test_fail_mintFractionForUser_InvalidSigner() (gas: 414454) +MinterTest:test_fail_mintFraktionForUser_ContractPaused() (gas: 402287) +MinterTest:test_fail_mintFraktionForUser_InsuficiantSupply() (gas: 448604) +MinterTest:test_fail_mintFraktionForUser_InvalidFraktionType() (gas: 383456) +MinterTest:test_fail_mintFraktionForUser_NotAuthorized() (gas: 378805) +MinterTest:test_fail_mintFraktion_ContractPaused() (gas: 401733) +MinterTest:test_fail_mintFraktion_TooManyFraktion() (gas: 481148) MinterTest:test_fail_mintFreeFraktionForUser_AlreadyHaveFreeFraktion() (gas: 337978) MinterTest:test_fail_mintFreeFraktionForUser_ContractPaused() (gas: 44913) MinterTest:test_fail_mintFreeFraktionForUser_ExpectingOnlyFreeFraktion() (gas: 303290) MinterTest:test_fail_mintFreeFraktionForUser_NotAuthorized() (gas: 15637) MinterTest:test_fail_multicall_NotAuthorized() (gas: 22840) MinterTest:test_increaseSupply() (gas: 306392) -MinterTest:test_mintFraktion() (gas: 478270) -MinterTest:test_mintFraktionForUser() (gas: 478824) +MinterTest:test_mintFraktion() (gas: 478075) +MinterTest:test_mintFraktionForUser() (gas: 478629) MinterTest:test_mintFreeFraktionForUser() (gas: 334215) MinterTest:test_multicall() (gas: 1270178) -MultiVestingWalletsTest:test_availableReserve() (gas: 89161) -MultiVestingWalletsTest:test_createVest() (gas: 213391) -MultiVestingWalletsTest:test_createVestBatch() (gas: 214754) +MultiVestingWalletsTest:test_availableReserve() (gas: 89062) +MultiVestingWalletsTest:test_createVest() (gas: 213292) +MultiVestingWalletsTest:test_createVestBatch() (gas: 214655) MultiVestingWalletsTest:test_decimals() (gas: 10458) MultiVestingWalletsTest:test_fail_TransferAvailableReserve_NoReserve() (gas: 32796) -MultiVestingWalletsTest:test_fail_createVestBatch_ArrayInvalidLength() (gas: 85377) -MultiVestingWalletsTest:test_fail_createVestBatch_ContractPaused() (gas: 107680) -MultiVestingWalletsTest:test_fail_createVestBatch_EmptyArray() (gas: 85271) -MultiVestingWalletsTest:test_fail_createVestBatch_NotEnoughReserve() (gas: 89544) -MultiVestingWalletsTest:test_fail_createVestBatch_NotManager() (gas: 84796) -MultiVestingWalletsTest:test_fail_createVest_ContractPaused() (gas: 106743) -MultiVestingWalletsTest:test_fail_createVest_InvalidAddress() (gas: 88428) -MultiVestingWalletsTest:test_fail_createVest_InvalidDuration() (gas: 84752) -MultiVestingWalletsTest:test_fail_createVest_InvalidReward() (gas: 88474) -MultiVestingWalletsTest:test_fail_createVest_InvalidStartDate() (gas: 84799) -MultiVestingWalletsTest:test_fail_createVest_InvalidStartDateTooFar() (gas: 84683) -MultiVestingWalletsTest:test_fail_createVest_NotEnoughReserve() (gas: 88424) -MultiVestingWalletsTest:test_fail_createVest_NotManager() (gas: 83829) -MultiVestingWalletsTest:test_fail_createVest_TooLargeReward() (gas: 88477) +MultiVestingWalletsTest:test_fail_createVestBatch_ArrayInvalidLength() (gas: 85278) +MultiVestingWalletsTest:test_fail_createVestBatch_ContractPaused() (gas: 107581) +MultiVestingWalletsTest:test_fail_createVestBatch_EmptyArray() (gas: 85172) +MultiVestingWalletsTest:test_fail_createVestBatch_NotEnoughReserve() (gas: 89445) +MultiVestingWalletsTest:test_fail_createVestBatch_NotManager() (gas: 84697) +MultiVestingWalletsTest:test_fail_createVest_ContractPaused() (gas: 106644) +MultiVestingWalletsTest:test_fail_createVest_InvalidAddress() (gas: 88329) +MultiVestingWalletsTest:test_fail_createVest_InvalidDuration() (gas: 84653) +MultiVestingWalletsTest:test_fail_createVest_InvalidReward() (gas: 88375) +MultiVestingWalletsTest:test_fail_createVest_InvalidStartDate() (gas: 84700) +MultiVestingWalletsTest:test_fail_createVest_InvalidStartDateTooFar() (gas: 84584) +MultiVestingWalletsTest:test_fail_createVest_NotEnoughReserve() (gas: 88325) +MultiVestingWalletsTest:test_fail_createVest_NotManager() (gas: 83730) +MultiVestingWalletsTest:test_fail_createVest_TooLargeReward() (gas: 88378) MultiVestingWalletsTest:test_fail_initialize_CantInitTwice() (gas: 15900) -MultiVestingWalletsTest:test_fail_transferAvailableReserve_ContractPaused() (gas: 106084) -MultiVestingWalletsTest:test_fail_transferAvailableReserve_NotAdmin() (gas: 83539) +MultiVestingWalletsTest:test_fail_transferAvailableReserve_ContractPaused() (gas: 105985) +MultiVestingWalletsTest:test_fail_transferAvailableReserve_NotAdmin() (gas: 83440) MultiVestingWalletsTest:test_name() (gas: 12064) MultiVestingWalletsTest:test_symbol() (gas: 12073) -MultiVestingWalletsTest:test_transfer() (gas: 226663) -MultiVestingWalletsTest:test_transferAvailableReserve() (gas: 98509) -RewarderPayTest:testFuzz_payUser(uint16) (runs: 2048, μ: 202481, ~: 202740) -RewarderPayTest:testFuzz_payUser_WithFraktions(uint16) (runs: 2048, μ: 806063, ~: 806338) -RewarderPayTest:testFuzz_payUser_WithFraktionsAndLoadOfState(uint16) (runs: 2048, μ: 3074002, ~: 3074279) -RewarderPayTest:testFuzz_payUser_WithFraktions_ClaimRewards(uint16) (runs: 2048, μ: 864763, ~: 865012) -RewarderPayTest:test_fail_payCreatorDirectlyBatch_ContractPaused() (gas: 111777) -RewarderPayTest:test_fail_payCreatorDirectlyBatch_EmptyAmount() (gas: 87549) -RewarderPayTest:test_fail_payCreatorDirectlyBatch_InvalidArray() (gas: 85953) -RewarderPayTest:test_fail_payCreatorDirectlyBatch_InvalidRole() (gas: 84440) -RewarderPayTest:test_fail_payCreatorDirectlyBatch_TooLargeAmount() (gas: 87539) -RewarderPayTest:test_fail_payCreatorDirectlyBatch_TooLargeArray() (gas: 89296) -RewarderPayTest:test_fail_payUserDirectly_ContractPaused() (gas: 108657) -RewarderPayTest:test_fail_payUserDirectly_InvalidAddress() (gas: 84327) -RewarderPayTest:test_fail_payUserDirectly_InvalidReward() (gas: 84317) -RewarderPayTest:test_fail_payUserDirectly_InvalidRole() (gas: 81347) -RewarderPayTest:test_fail_payUserDirectly_NotEnoughBalance() (gas: 110235) -RewarderPayTest:test_fail_payUserDirectly_TooLargeReward() (gas: 84318) -RewarderPayTest:test_fail_payUser_ArrayNotSameSize() (gas: 86147) -RewarderPayTest:test_fail_payUser_ContractPaused() (gas: 112208) -RewarderPayTest:test_fail_payUser_InexistantContent() (gas: 121322) -RewarderPayTest:test_fail_payUser_NotRewarder() (gas: 84859) -RewarderPayTest:test_fail_payUser_TooLargeArray() (gas: 89512) -RewarderPayTest:test_payCreatorDirectlyBatch() (gas: 145597) -RewarderPayTest:test_payUser() (gas: 192709) -RewarderPayTest:test_payUserDirectly() (gas: 114246) -RewarderPayTest:test_payUser_LargeReward() (gas: 801665) +MultiVestingWalletsTest:test_transfer() (gas: 226564) +MultiVestingWalletsTest:test_transferAvailableReserve() (gas: 98269) +RewarderPayTest:testFuzz_payUser(uint16) (runs: 1000, μ: 202037, ~: 202272) +RewarderPayTest:testFuzz_payUser_WithFraktions(uint16) (runs: 1000, μ: 805502, ~: 805746) +RewarderPayTest:testFuzz_payUser_WithFraktionsAndLoadOfState(uint16) (runs: 1000, μ: 3072432, ~: 3072684) +RewarderPayTest:testFuzz_payUser_WithFraktions_ClaimRewards(uint16) (runs: 1000, μ: 863676, ~: 863909) +RewarderPayTest:test_fail_payCreatorDirectlyBatch_ContractPaused() (gas: 111700) +RewarderPayTest:test_fail_payCreatorDirectlyBatch_EmptyAmount() (gas: 87450) +RewarderPayTest:test_fail_payCreatorDirectlyBatch_InvalidArray() (gas: 85876) +RewarderPayTest:test_fail_payCreatorDirectlyBatch_InvalidRole() (gas: 84341) +RewarderPayTest:test_fail_payCreatorDirectlyBatch_TooLargeAmount() (gas: 87462) +RewarderPayTest:test_fail_payCreatorDirectlyBatch_TooLargeArray() (gas: 89219) +RewarderPayTest:test_fail_payUserDirectly_ContractPaused() (gas: 108558) +RewarderPayTest:test_fail_payUserDirectly_InvalidAddress() (gas: 84228) +RewarderPayTest:test_fail_payUserDirectly_InvalidReward() (gas: 84218) +RewarderPayTest:test_fail_payUserDirectly_InvalidRole() (gas: 81248) +RewarderPayTest:test_fail_payUserDirectly_NotEnoughBalance() (gas: 109995) +RewarderPayTest:test_fail_payUserDirectly_TooLargeReward() (gas: 84219) +RewarderPayTest:test_fail_payUser_ArrayNotSameSize() (gas: 86048) +RewarderPayTest:test_fail_payUser_ContractPaused() (gas: 112109) +RewarderPayTest:test_fail_payUser_InexistantContent() (gas: 120867) +RewarderPayTest:test_fail_payUser_NotRewarder() (gas: 84760) +RewarderPayTest:test_fail_payUser_TooLargeArray() (gas: 89413) +RewarderPayTest:test_payCreatorDirectlyBatch() (gas: 145498) +RewarderPayTest:test_payUser() (gas: 192263) +RewarderPayTest:test_payUserDirectly() (gas: 114006) +RewarderPayTest:test_payUser_ContentTypeImpactReward() (gas: 256228) +RewarderPayTest:test_payUser_LargeReward() (gas: 801073) +RewarderPayTest:test_payUser_NoReward_ContentTypeNotKnown() (gas: 124252) RewarderTest:test_fail_InitTwice() (gas: 16263) RewarderTest:test_fail_multicall_NotAuthorized() (gas: 168325) RewarderTest:test_fail_updateContentBadge_BadgeCapReached() (gas: 168150) @@ -157,8 +159,8 @@ RewarderTest:test_fail_updateListenerBadge_ContractPaused() (gas: 44893) RewarderTest:test_fail_updateListenerBadge_NotAuthorized() (gas: 15626) RewarderTest:test_fail_updateTpu_NotAuthorized() (gas: 15445) RewarderTest:test_multicall_emptyData() (gas: 16590) -RewarderTest:test_multicall_multipleData() (gas: 328594) -RewarderTest:test_multicall_reallyLargeData() (gas: 7503826) +RewarderTest:test_multicall_multipleData() (gas: 328213) +RewarderTest:test_multicall_reallyLargeData() (gas: 7362727) RewarderTest:test_multicall_singleData() (gas: 194541) RewarderTest:test_updateContentBadge() (gas: 192874) RewarderTest:test_updateListenerBadge() (gas: 45351) diff --git a/.github/workflows/security-analysis.yml b/.github/workflows/security-analysis.yml new file mode 100644 index 0000000..5684ba0 --- /dev/null +++ b/.github/workflows/security-analysis.yml @@ -0,0 +1,56 @@ +name: 🔒️ Security analysis + +on: + workflow_dispatch: + pull_request: + push: + branches: ["main"] + +# Cancel previous runs on the same PR +concurrency: + group: ${{ github.ref }}-analysis + cancel-in-progress: true + +env: + FOUNDRY_PROFILE: "ci" + +jobs: + analyze: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + submodules: recursive + + - name: "🔨 Install Foundry" + uses: foundry-rs/foundry-toolchain@v1 + with: + version: nightly + + - name: "💾 Foundry fork cache" + uses: actions/cache@v3 + with: + path: "~/.foundry/cache" + key: foundry-${{ hashFiles('foundry.toml') }} + + - name: "💾 Foundry compilation cache" + uses: actions/cache@v3 + with: + path: | + cache + out + key: foundry-${{ github.repository_id }}-${{ hashFiles('foundry.toml') }} + + - name: "🔒️ Run Slither" + uses: crytic/slither-action@v0.3.0 + id: slither + with: + solc-version: 0.8.21 + sarif: results.sarif + fail-on: none + slither-config: 'tools/slither.config.json' + + - name: "📈 Upload SARIF file report" + uses: github/codeql-action/upload-sarif@v2 + with: + sarif_file: ${{ steps.slither.outputs.sarif }} \ No newline at end of file diff --git a/.github/workflows/storage-check.yml b/.github/workflows/storage-check.yml index f3da465..2298acf 100644 --- a/.github/workflows/storage-check.yml +++ b/.github/workflows/storage-check.yml @@ -1,16 +1,21 @@ -name: 🧪🗃️Smart storage tests +name: 🗃️ Storage tests on: push: branches: ["main"] pull_request: - types: [opened, synchronize, reopened] + workflow_dispatch: # cancel previous runs on the same PR concurrency: group: ${{ github.ref }}-storage cancel-in-progress: true +# All the env required for foundry +env: + FOUNDRY_PROFILE: "ci" + POLYGON_RPC_URL: "https://polygon-mainnet.g.alchemy.com/v2/${{ secrets.API_KEY_ALCHEMY_POLYGON }}" + jobs: storage-check: runs-on: ubuntu-latest @@ -42,7 +47,7 @@ jobs: uses: actions/cache@v3 with: path: "~/.foundry/cache" - key: foundry-${{ hashFiles('Makefile', 'foundry.toml') }} # where fork block numbers & RPC are stored + key: foundry-${{ hashFiles('foundry.toml') }} # where fork block numbers & RPC are stored - name: '💾 Foundry compilation cache' uses: actions/cache@v3 @@ -50,37 +55,37 @@ jobs: path: | cache out - key: ${{ github.base_ref || github.ref_name }}-foundry + key: foundry-${{ github.repository_id }}-${{ hashFiles('foundry.toml') }} - name: '🧪 Check FRK Token storage' uses: Rubilmax/foundry-storage-check@v3.5 with: - contract: contracts/tokens/FrakTokenL2.sol:FrakToken - rpcUrl: ${{ secrets.POLYGON_PROVIDER }} + contract: contracts/tokens/FrakToken.sol:FrakToken + rpcUrl: ${{ env.POLYGON_RPC_URL }} address: "0x6261E4a478C98419EaFa6289509C49058D21Df8c" failOnRemoval: true - name: '🧪 Check Fraktion Token storage' uses: Rubilmax/foundry-storage-check@v3.5 with: - contract: contracts/tokens/FraktionTokens.sol:FraktionTokens - rpcUrl: ${{ secrets.POLYGON_PROVIDER }} + contract: contracts/fraktions/FraktionTokens.sol:FraktionTokens + rpcUrl: ${{ env.POLYGON_RPC_URL }} address: "0x4B1611803687Ab821E1b670fE94CB93303D94F8a" failOnRemoval: true - name: '🧪 Check content pool storage' uses: Rubilmax/foundry-storage-check@v3.5 with: - contract: contracts/reward/pool/ContentPool.sol:ContentPool - rpcUrl: ${{ secrets.POLYGON_PROVIDER }} + contract: contracts/reward/contentPool/ContentPool.sol:ContentPool + rpcUrl: ${{ env.POLYGON_RPC_URL }} address: "0xDCB34659B83C4F8708fd7AcAA3755547BF8BBcA0" failOnRemoval: true - name: '🧪 Check referral pool storage' uses: Rubilmax/foundry-storage-check@v3.5 with: - contract: contracts/reward/pool/ReferralPool.sol:ReferralPool - rpcUrl: ${{ secrets.POLYGON_PROVIDER }} + contract: contracts/reward/referralPool/ReferralPool.sol:ReferralPool + rpcUrl: ${{ env.POLYGON_RPC_URL }} address: "0x166d8CFEe1919bC2e8c7AdBB34F1613194e9C599" failOnRemoval: true @@ -88,7 +93,7 @@ jobs: uses: Rubilmax/foundry-storage-check@v3.5 with: contract: contracts/reward/Rewarder.sol:Rewarder - rpcUrl: ${{ secrets.POLYGON_PROVIDER }} + rpcUrl: ${{ env.POLYGON_RPC_URL }} address: "0x8D9fa601DA1416b087E9db6B6EaD63D4920A4528" failOnRemoval: true @@ -96,6 +101,6 @@ jobs: uses: Rubilmax/foundry-storage-check@v3.5 with: contract: contracts/minter/Minter.sol:Minter - rpcUrl: ${{ secrets.POLYGON_PROVIDER }} + rpcUrl: ${{ env.POLYGON_RPC_URL }} address: "0x1adc8CAaA35551730eCd82e0eEA683Aa90dB6cf0" failOnRemoval: true \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4cdf98b..a557f60 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,23 +1,23 @@ -name: 🧪 Smart contract tests +name: 🧪 Foundry tests on: push: branches: ["main"] + workflow_dispatch: pull_request: - types: [opened, synchronize, reopened] -# cancel previous runs on the same PR +# Cancel previous runs on the same PR concurrency: group: ${{ github.ref }}-tests cancel-in-progress: true -# ll the env required for hardhat +# All the env required for foundry env: - MUMBAI_RPC_URL: ${{ secrets.MUMBAI_PROVIDER }} - GOERLI_RPC_URL: ${{ secrets.GOERLI_PROVIDER }} - POLYGON_RPC_URL: ${{ secrets.POLYGON_PROVIDER }} - POLYGON_SCAN_API_KEY: ${{ secrets.POLYGONSCAN_API_KEY }} - ETHERSCAN_SCAN_API_KEY: ${{ secrets.ETHER_SCAN_API_KEY }} + FOUNDRY_PROFILE: "ci" + API_KEY_ALCHEMY_MAINNET: ${{ secrets.API_KEY_ALCHEMY_MAINNET }} + API_KEY_ALCHEMY_GOERLI: ${{ secrets.API_KEY_ALCHEMY_GOERLI }} + API_KEY_ALCHEMY_POLYGON: ${{ secrets.API_KEY_ALCHEMY_POLYGON }} + API_KEY_ALCHEMY_MUMBAI: ${{ secrets.API_KEY_ALCHEMY_MUMBAI }} jobs: test: @@ -50,7 +50,7 @@ jobs: uses: actions/cache@v3 with: path: "~/.foundry/cache" - key: foundry-${{ hashFiles('Makefile', 'foundry.toml') }} # where fork block numbers & RPC are stored + key: foundry-${{ hashFiles('foundry.toml') }} # where fork block numbers & RPC are stored - name: '💾 Foundry compilation cache' uses: actions/cache@v3 @@ -58,7 +58,7 @@ jobs: path: | cache out - key: ${{ github.base_ref || github.ref_name }}-foundry + key: foundry-${{ github.repository_id }}-${{ hashFiles('foundry.toml') }} - name: '🎨 Run lint tests' run: forge fmt --check diff --git a/.github/workflows/upload-selectors.yml b/.github/workflows/upload-selectors.yml index e278bec..8b85937 100644 --- a/.github/workflows/upload-selectors.yml +++ b/.github/workflows/upload-selectors.yml @@ -1,13 +1,7 @@ -name: 🗃 Upload selectors +name: 📦️ Upload selectors on: - push: - branches: ["main"] - -# cancel previous runs on the same PR -concurrency: - group: ${{ github.ref }}-selector-upload - cancel-in-progress: true + workflow_dispatch: jobs: storage-check: @@ -17,19 +11,6 @@ jobs: - uses: actions/checkout@v3 with: submodules: recursive - - - name: '🔨 Setup pnpm' - uses: pnpm/action-setup@v2 - - - name: '🔨 Setup Node.js' - id: setup-node - uses: actions/setup-node@v3 - with: - node-version-file: '.nvmrc' - cache: 'pnpm' - - - name: '🔨 Install dependencies' - run: pnpm install --frozen-lockfile - name: '🔨 Install Foundry' uses: foundry-rs/foundry-toolchain@v1 diff --git a/README.md b/README.md index 9210a20..95339de 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,7 @@ utils | Contract name | Status | Audited | Deployment | | ------------------------------------------------------------------ | --------- | ------- | ----------------------------------------------------------------------------------- | -| [FrakToken](contracts/tokens/FrakTokenL2.sol) | Finalized | [^1] | [Proxy](https://polygonscan.com/token/0x6261E4a478C98419EaFa6289509C49058D21Df8c) | +| [FrakToken](contracts/tokens/FrakToken.sol) | Finalized | [^1] | [Proxy](https://polygonscan.com/token/0x6261E4a478C98419EaFa6289509C49058D21Df8c) | | [FraktionTokens](contracts/tokens/FraktionTokens.sol) | Finalized | [^2] | [Proxy](https://polygonscan.com/token/0x4B1611803687Ab821E1b670fE94CB93303D94F8a) | | [VestingWalletFactory](contracts/wallets/VestingWalletFactory.sol) | Finalized | [^2] | [Proxy](https://polygonscan.com/address/0xb8D79C7Bca3994dd5B4A80AD1c088CEBCd01f7F6) | | [MultiVestingWallets](contracts/wallets/MultiVestingWallets.sol) | Finalized | [^2] | [Proxy](https://polygonscan.com/address/0x4B1611803687Ab821E1b670fE94CB93303D94F8a) | diff --git a/contracts/tokens/FraktionTokens.sol b/contracts/fraktions/FraktionTokens.sol similarity index 85% rename from contracts/tokens/FraktionTokens.sol rename to contracts/fraktions/FraktionTokens.sol index ea70616..f896654 100644 --- a/contracts/tokens/FraktionTokens.sol +++ b/contracts/fraktions/FraktionTokens.sol @@ -1,22 +1,20 @@ // SPDX-License-Identifier: GNU GPLv3 pragma solidity 0.8.21; -import {ERC1155Upgradeable} from "@oz-upgradeable/token/ERC1155/ERC1155Upgradeable.sol"; -import {FraktionTransferCallback} from "./FraktionTransferCallback.sol"; -import {FrakMath} from "../utils/FrakMath.sol"; -import {FrakRoles} from "../utils/FrakRoles.sol"; -import {MintingAccessControlUpgradeable} from "../utils/MintingAccessControlUpgradeable.sol"; -import {InvalidArray} from "../utils/FrakErrors.sol"; - -/** - * @author @KONFeature - * @title FraktionTokens - * @dev ERC1155 for the Frak Fraktions tokens, used as ownership proof for a content, or investisment proof - * @custom:security-contact contact@frak.id - */ -contract FraktionTokens is MintingAccessControlUpgradeable, ERC1155Upgradeable { - using FrakMath for uint256; - +import { ERC1155Upgradeable } from "@oz-upgradeable/token/ERC1155/ERC1155Upgradeable.sol"; +import { FraktionTransferCallback } from "./FraktionTransferCallback.sol"; +import { ContentId } from "../libs/ContentId.sol"; +import { FraktionId } from "../libs/FraktionId.sol"; +import { ArrayLib } from "../libs/ArrayLib.sol"; +import { FrakRoles } from "../roles/FrakRoles.sol"; +import { FrakAccessControlUpgradeable } from "../roles/FrakAccessControlUpgradeable.sol"; +import { InvalidArray } from "../utils/FrakErrors.sol"; + +/// @author @KONFeature +/// @title FraktionTokens +/// @notice ERC1155 for the Frak Fraktions tokens, used as ownership proof for a content, or investisment proof +/// @custom:security-contact contact@frak.id +contract FraktionTokens is FrakAccessControlUpgradeable, ERC1155Upgradeable { /* -------------------------------------------------------------------------- */ /* Custom error's */ /* -------------------------------------------------------------------------- */ @@ -64,8 +62,8 @@ contract FraktionTokens is MintingAccessControlUpgradeable, ERC1155Upgradeable { /* Storage */ /* -------------------------------------------------------------------------- */ - /// @dev The current content token id - uint256 private _currentContentTokenId; + /// @dev The current content id + uint256 private _currentContentId; /// @dev The current callback FraktionTransferCallback private transferCallback; @@ -73,10 +71,10 @@ contract FraktionTokens is MintingAccessControlUpgradeable, ERC1155Upgradeable { /// @dev Id of content to owner of this content mapping(uint256 => address) private owners; - /// @dev Available supply of each tokens (classic, rare, epic and legendary only) by they id + /// @dev Available supply of each fraktion (classic, rare, epic and legendary only) by they id mapping(uint256 => uint256) private _availableSupplies; - /// @dev Tell us if that token is supply aware or not + /// @dev Tell us if that fraktion is supply aware or not mapping(uint256 => bool) private _isSupplyAware; /// @custom:oz-upgrades-unsafe-allow constructor @@ -86,9 +84,9 @@ contract FraktionTokens is MintingAccessControlUpgradeable, ERC1155Upgradeable { function initialize(string calldata metadatalUrl) external initializer { __ERC1155_init(metadatalUrl); - __MintingAccessControlUpgradeable_init(); + __FrakAccessControlUpgradeable_Minter_init(); // Set the initial content id - _currentContentTokenId = 1; + _currentContentId = 1; } /* -------------------------------------------------------------------------- */ @@ -99,12 +97,16 @@ contract FraktionTokens is MintingAccessControlUpgradeable, ERC1155Upgradeable { * @dev Mint a new content, return the id of the built content * We will store in memory -from 0x0 to 0x40 -> */ - function mintNewContent(address ownerAddress, uint256[] calldata fraktionTypes, uint256[] calldata supplies) + function mintNewContent( + address ownerAddress, + uint256[] calldata fraktionTypes, + uint256[] calldata supplies + ) external payable onlyRole(FrakRoles.MINTER) whenNotPaused - returns (uint256 id) + returns (ContentId id) { uint256 creatorTokenId; assembly { @@ -114,9 +116,9 @@ contract FraktionTokens is MintingAccessControlUpgradeable, ERC1155Upgradeable { revert(0x1c, 0x04) } - // Get the next content id and increment the current content token id - id := add(sload(_currentContentTokenId.slot), 1) - sstore(_currentContentTokenId.slot, id) + // Get the next content id and increment the current content id + id := add(sload(_currentContentId.slot), 1) + sstore(_currentContentId.slot, id) // Get the shifted id, to ease the fraktion id creation let shiftedId := shl(0x04, id) @@ -127,13 +129,13 @@ contract FraktionTokens is MintingAccessControlUpgradeable, ERC1155Upgradeable { // Current iterator offset let currentOffset := 0 // Infinite loop - for {} 1 {} { + for { } 1 { } { // Get the current id let fraktionType := calldataload(add(fraktionTypes.offset, currentOffset)) - // Ensure the supply update of this token type is allowed + // Ensure the supply update of this fraktion type is allowed if or(lt(fraktionType, 3), gt(fraktionType, 6)) { - // If token type lower than 3 -> free or owner + // If fraktion type lower than 3 -> free or owner mstore(0x00, _SUPPLY_UPDATE_NOT_ALLOWED_SELECTOR) revert(0x1c, 0x04) } @@ -184,7 +186,7 @@ contract FraktionTokens is MintingAccessControlUpgradeable, ERC1155Upgradeable { } /** - * @dev Set the supply for the given token id + * @dev Set the supply for the given fraktion id */ function setSupply(uint256 id, uint256 supply) external payable onlyRole(FrakRoles.MINTER) whenNotPaused { assembly { @@ -193,10 +195,10 @@ contract FraktionTokens is MintingAccessControlUpgradeable, ERC1155Upgradeable { mstore(0x00, _INVALID_ARRAY_SELECTOR) revert(0x1c, 0x04) } - // Ensure the supply update of this token type is allowed + // Ensure the supply update of this fraktion type is allowed let fraktionType := and(id, 0xF) if or(lt(fraktionType, 3), gt(fraktionType, 6)) { - // If token type lower than 3 -> free or owner, if greater than 6 -> not a content + // If fraktion type lower than 3 -> free or owner, if greater than 6 -> not a content mstore(0x00, _SUPPLY_UPDATE_NOT_ALLOWED_SELECTOR) revert(0x1c, 0x04) } @@ -229,12 +231,12 @@ contract FraktionTokens is MintingAccessControlUpgradeable, ERC1155Upgradeable { transferCallback = FraktionTransferCallback(callbackAddr); } - /// @dev Mint a new fraction of a nft + /// @dev Mint a new fraktion of a nft function mint(address to, uint256 id, uint256 amount) external payable onlyRole(FrakRoles.MINTER) whenNotPaused { _mint(to, id, amount, ""); } - /// @dev Burn a fraction of a nft + /// @dev Burn a fraktion of a nft function burn(uint256 id, uint256 amount) external payable whenNotPaused { _burn(msg.sender, id, amount); } @@ -253,14 +255,17 @@ contract FraktionTokens is MintingAccessControlUpgradeable, ERC1155Upgradeable { uint256[] memory ids, uint256[] memory amounts, bytes memory - ) internal override { + ) + internal + override + { assembly { // Base offset to access array element's let currOffset := 0x20 let offsetEnd := add(currOffset, shl(5, mload(ids))) // Infinite loop - for {} 1 {} { + for { } 1 { } { // Get the id and amount let id := mload(add(ids, currOffset)) let amount := mload(add(amounts, currOffset)) @@ -308,7 +313,11 @@ contract FraktionTokens is MintingAccessControlUpgradeable, ERC1155Upgradeable { uint256[] memory ids, uint256[] memory amounts, bytes memory - ) internal override whenNotPaused { + ) + internal + override + whenNotPaused + { assembly { // Base offset to access array element's let currOffset := 0x20 @@ -318,7 +327,7 @@ contract FraktionTokens is MintingAccessControlUpgradeable, ERC1155Upgradeable { let hasOneFraktionForCallback := false // Infinite loop - for {} 1 {} { + for { } 1 { } { // Get the id and amount let id := mload(add(ids, currOffset)) @@ -352,7 +361,7 @@ contract FraktionTokens is MintingAccessControlUpgradeable, ERC1155Upgradeable { } // Call our callback - transferCallback.onFraktionsTransferred(from, to, ids, amounts); + transferCallback.onFraktionsTransferred(from, to, ArrayLib.asFraktionIds(ids), amounts); } /* -------------------------------------------------------------------------- */ @@ -362,7 +371,10 @@ contract FraktionTokens is MintingAccessControlUpgradeable, ERC1155Upgradeable { /** * @dev Batch balance of for single address */ - function balanceOfIdsBatch(address account, uint256[] calldata ids) + function balanceOfIdsBatch( + address account, + FraktionId[] calldata ids + ) public view virtual @@ -380,7 +392,7 @@ contract FraktionTokens is MintingAccessControlUpgradeable, ERC1155Upgradeable { // Current balance array offset let balanceOffset := add(batchBalances, 0x20) // Infinite loop - for {} 1 {} { + for { } 1 { } { // Get the slot for the current id mstore(0, calldataload(i)) mstore(0x20, 0xcb) // `_balances.slot` on the OZ contract @@ -402,12 +414,12 @@ contract FraktionTokens is MintingAccessControlUpgradeable, ERC1155Upgradeable { } /// @dev Find the owner of the given 'contentId' - function ownerOf(uint256 contentId) external view returns (address) { - return owners[contentId]; + function ownerOf(ContentId contentId) external view returns (address) { + return owners[ContentId.unwrap(contentId)]; } /// @dev Find the current supply of the given 'tokenId' - function supplyOf(uint256 tokenId) external view returns (uint256) { - return _availableSupplies[tokenId]; + function supplyOf(FraktionId tokenId) external view returns (uint256) { + return _availableSupplies[FraktionId.unwrap(tokenId)]; } } diff --git a/contracts/fraktions/FraktionTransferCallback.sol b/contracts/fraktions/FraktionTransferCallback.sol new file mode 100644 index 0000000..b7b3a6a --- /dev/null +++ b/contracts/fraktions/FraktionTransferCallback.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GNU GPLv3 +pragma solidity 0.8.21; + +import { FraktionId } from "../libs/FraktionId.sol"; + +/// @author @KONFeature +/// @title FraktionTransferCallback +/// @notice Interface for contract who want to listen of the fraktion transfer (ERC1155 tokens transfer) +/// @custom:security-contact contact@frak.id +interface FraktionTransferCallback { + /** + * @dev Function called when a fraktion is transfered between two person + */ + function onFraktionsTransferred( + address from, + address to, + FraktionId[] memory ids, + uint256[] memory amount + ) + external + payable; +} diff --git a/contracts/libs/ArrayLib.sol b/contracts/libs/ArrayLib.sol new file mode 100644 index 0000000..2ea3ea0 --- /dev/null +++ b/contracts/libs/ArrayLib.sol @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GNU GPLv3 +pragma solidity 0.8.21; + +import { FraktionId } from "./FraktionId.sol"; + +/// @author @KONFeature +/// @title ArrayLib +/// @notice Lib to help us manage array +/// @custom:security-contact contact@frak.id +library ArrayLib { + /// @dev Wrap a uint256 to a FraktionId type + function asFraktionIds(uint256[] memory self) internal pure returns (FraktionId[] memory fraktionIds) { + assembly { + fraktionIds := self + } + } + + /// @dev Create a singleton array of the given element + function asSingletonArray(uint256 element) internal pure returns (uint256[] memory array) { + assembly { + // Get free memory space for our array, and update the free mem space index + array := mload(0x40) + mstore(0x40, add(array, 0x40)) + + // Store our array (1st = length, 2nd = element) + mstore(array, 0x01) + mstore(add(array, 0x20), element) + } + } + + /// @dev Create a singleton array of the given element + function asSingletonArray(address element) internal pure returns (address[] memory array) { + assembly { + // Get free memory space for our array, and update the free mem space index + array := mload(0x40) + mstore(0x40, add(array, 0x40)) + + // Store our array (1st = length, 2nd = element) + mstore(array, 0x01) + mstore(add(array, 0x20), element) + } + } +} diff --git a/contracts/libs/ContentId.sol b/contracts/libs/ContentId.sol new file mode 100644 index 0000000..41c9ac9 --- /dev/null +++ b/contracts/libs/ContentId.sol @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: GNU GPLv3 +pragma solidity 0.8.21; + +import { FraktionId } from "./FraktionId.sol"; + +/// @dev Define the ContentId type +type ContentId is uint256; + +/// @dev Tell to use the lib below for every ContentId instance +using ContentIdLib for ContentId global; + +/// @author @KONFeature +/// @title ContentIdLib +/// @notice This contract is used to help us with the manipulation of the ContentId +/// @custom:security-contact contact@frak.id +library ContentIdLib { + /* -------------------------------------------------------------------------- */ + /* Constant's */ + /* -------------------------------------------------------------------------- */ + + /// @dev The offset of the id we use to store the fraktion type + uint256 internal constant ID_OFFSET = 4; + /// @dev The mask we use to store the fraktion type in the fraktion id + uint256 internal constant TYPE_MASK = 0xF; + + /// @dev NFT Token type mask + uint256 internal constant FRAKTION_TYPE_CREATOR = 1; + /// @dev Free Token type mask + uint256 internal constant FRAKTION_TYPE_FREE = 2; + /// @dev Common Token type mask + uint256 internal constant FRAKTION_TYPE_COMMON = 3; + /// @dev Premium Token type mask + uint256 internal constant FRAKTION_TYPE_PREMIUM = 4; + /// @dev Gold Token type mask + uint256 internal constant FRAKTION_TYPE_GOLD = 5; + /// @dev Diamond Token type mask + uint256 internal constant FRAKTION_TYPE_DIAMOND = 6; + + /* -------------------------------------------------------------------------- */ + /* Helper method's */ + /* -------------------------------------------------------------------------- */ + + /// @dev Build the id for a creator NFT id + function creatorFraktionId(ContentId self) internal pure returns (FraktionId id) { + assembly { + id := or(shl(ID_OFFSET, self), FRAKTION_TYPE_CREATOR) + } + } + + /// @dev Build the id for a free NFT id + function freeFraktionId(ContentId self) internal pure returns (FraktionId id) { + assembly { + id := or(shl(ID_OFFSET, self), FRAKTION_TYPE_FREE) + } + } + + /// @dev Build the id for a common NFT id + function commonFraktionId(ContentId self) internal pure returns (FraktionId id) { + assembly { + id := or(shl(ID_OFFSET, self), FRAKTION_TYPE_COMMON) + } + } + + /// @dev Build the id for a premium NFT id + function premiumFraktionId(ContentId self) internal pure returns (FraktionId id) { + assembly { + id := or(shl(ID_OFFSET, self), FRAKTION_TYPE_PREMIUM) + } + } + + /// @dev Build the id for a gold NFT id + function goldFraktionId(ContentId self) internal pure returns (FraktionId id) { + assembly { + id := or(shl(ID_OFFSET, self), FRAKTION_TYPE_GOLD) + } + } + + /// @dev Build the id for a diamond NFT id + function diamondFraktionId(ContentId self) internal pure returns (FraktionId id) { + assembly { + id := or(shl(ID_OFFSET, self), FRAKTION_TYPE_DIAMOND) + } + } + + /// @dev Build an array of all the payable fraktion types + function payableFraktionIds(ContentId self) internal pure returns (FraktionId[] memory ids) { + assembly { + // Store each types + ids := mload(0x40) + mstore(ids, 4) + mstore(add(ids, 0x20), or(shl(ID_OFFSET, self), FRAKTION_TYPE_COMMON)) + mstore(add(ids, 0x40), or(shl(ID_OFFSET, self), FRAKTION_TYPE_PREMIUM)) + mstore(add(ids, 0x60), or(shl(ID_OFFSET, self), FRAKTION_TYPE_GOLD)) + mstore(add(ids, 0x80), or(shl(ID_OFFSET, self), FRAKTION_TYPE_DIAMOND)) + // Update our free mem space + mstore(0x40, add(ids, 0xA0)) + } + } + + /// @dev Create a new array with the given element + function asSingletonArray(ContentId self) internal pure returns (ContentId[] memory array) { + assembly { + // Get free memory space for our array, and update the free mem space index + array := mload(0x40) + mstore(0x40, add(array, 0x40)) + + // Store our array (1st = length, 2nd = element) + mstore(array, 0x01) + mstore(add(array, 0x20), self) + } + } +} diff --git a/contracts/libs/FraktionId.sol b/contracts/libs/FraktionId.sol new file mode 100644 index 0000000..37f7c60 --- /dev/null +++ b/contracts/libs/FraktionId.sol @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GNU GPLv3 +pragma solidity 0.8.21; + +/// @dev Define the FraktionId type +type FraktionId is uint256; + +/// @dev Tell to use the lib below for every FraktionId instance +using FraktionIdLib for FraktionId global; + +/// @author @KONFeature +/// @title FraktionIdLib +/// @notice This contract is used to help us with the manipulation of the FraktionId +/// @custom:security-contact contact@frak.id +library FraktionIdLib { + /// @dev The offset of the id we use to store the fraktion type + uint256 internal constant ID_OFFSET = 4; + /// @dev The mask we use to store the fraktion type in the fraktion id + uint256 internal constant TYPE_MASK = 0xF; + + /// @dev Get the `contentId` from the `self` fraktion id + function getContentId(FraktionId self) internal pure returns (uint256 contentId) { + assembly { + contentId := shr(ID_OFFSET, self) + } + } + + /// @dev Get the `fraktionType` from the `self` fraktion id + function getFraktionType(FraktionId self) internal pure returns (uint256 fraktionType) { + assembly { + fraktionType := and(self, TYPE_MASK) + } + } + + /// @dev Get the `contentId` and `fraktionType` from the `self` fraktion id + function extractAll(FraktionId self) internal pure returns (uint256 contentId, uint256 fraktionType) { + assembly { + contentId := shr(ID_OFFSET, self) + fraktionType := and(self, TYPE_MASK) + } + } + + /// @dev Create a new array with the given element + function asSingletonArray(FraktionId self) internal pure returns (FraktionId[] memory array) { + assembly { + // Get free memory space for our array, and update the free mem space index + array := mload(0x40) + mstore(0x40, add(array, 0x40)) + + // Store our array (1st = length, 2nd = element) + mstore(array, 0x01) + mstore(add(array, 0x20), self) + } + } +} diff --git a/contracts/minter/IMinter.sol b/contracts/minter/IMinter.sol index 9347012..826397a 100644 --- a/contracts/minter/IMinter.sol +++ b/contracts/minter/IMinter.sol @@ -1,19 +1,42 @@ // SPDX-License-Identifier: GNU GPLv3 pragma solidity 0.8.21; -import {IPausable} from "../utils/IPausable.sol"; +import { IPausable } from "../utils/IPausable.sol"; +import { ContentId } from "../libs/ContentId.sol"; +import { FraktionId } from "../libs/FraktionId.sol"; -/** - * @author @KONFeature - * @title Minter interface - * @notice This contract describe the method exposed by the Minter contract - * @dev Just an interface to ease the development and upgradeability - * @custom:security-contact contact@frak.id - */ +/// @author @KONFeature +/// @title IMinter +/// @notice Interface for the Minter contract +/// @custom:security-contact contact@frak.id interface IMinter is IPausable { + /* -------------------------------------------------------------------------- */ + /* Error's */ + /* -------------------------------------------------------------------------- */ + + /// @dev Error emitted when the input supply is invalid + error InvalidSupply(); + + /// @dev Error emitted when we only want to mint a free fraktion, and that's not a free fraktion + error ExpectingOnlyFreeFraktion(); + + /// @dev Error emitted when the have more than one fraktions of the given type + error TooManyFraktion(); + + /* -------------------------------------------------------------------------- */ + /* Event's */ + /* -------------------------------------------------------------------------- */ + + /// @dev Event emitted when a new content is minted + event ContentMinted(uint256 baseId, address indexed owner); + + /// @dev Event emitted when a new fraktion for a content is minted + event FractionMinted(uint256 indexed fraktionId, address indexed user, uint256 amount, uint256 cost); + /** * @notice Mint a new content to the FrkEcosystem - * @dev Will ensure the role and contract state, then the param, and finally call the FraktionTokens contract to mint the new content + * @dev Will ensure the role and contract state, then the param, and finally call the FraktionTokens contract to + * mint the new content * @param contentOwnerAddress The address of the owner of the given content * @param commonSupply The supply desired for each common fraktion of this content * @param premiumSupply The supply desired for each premium fraktion of this content @@ -27,11 +50,15 @@ interface IMinter is IPausable { uint256 premiumSupply, uint256 goldSupply, uint256 diamondSupply - ) external payable returns (uint256 contentId); + ) + external + payable + returns (ContentId contentId); /** * @notice Mint a new fraktion for the given amount and user - * @dev Will compute the fraktion price, ensure the user have enough Frk to buy it, if try, perform the transfer and mint the fraktion + * @dev Will compute the fraktion price, ensure the user have enough Frk to buy it, if try, perform the transfer + * and mint the fraktion * @param id The id of the fraktion to be minted for the user * @param to The address on which we will mint the fraktion * @param deadline The deadline for the permit of the allowance tx @@ -39,41 +66,52 @@ interface IMinter is IPausable { * @param r Signature spec secp256k1 * @param s Signature spec secp256k1 */ - function mintFraktionForUser(uint256 id, address to, uint256 deadline, uint8 v, bytes32 r, bytes32 s) + function mintFraktionForUser( + FraktionId id, + address to, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) external payable; /** * @notice Mint a new fraktion for the given amount to the caller - * @dev Will compute the fraktion price, ensure the user have enough Frk to buy it, if try, perform the transfer and mint the fraktion + * @dev Will compute the fraktion price, ensure the user have enough Frk to buy it, if try, perform the transfer + * and mint the fraktion * @param id The id of the fraktion to be minted for the user * @param deadline The deadline for the permit of the allowance tx * @param v Signature spec secp256k1 * @param r Signature spec secp256k1 * @param s Signature spec secp256k1 */ - function mintFraktion(uint256 id, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external payable; + function mintFraktion(FraktionId id, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external payable; /** * @notice Mint a free fraktion for the given user - * @dev Will mint a new free FraktionToken for the user, by first ensuring the user doesn't have any fraktion, only performed when contract not paused and by the right person + * @dev Will mint a new free FraktionToken for the user, by first ensuring the user doesn't have any fraktion, + * only performed when contract not paused and by the right person * @param id Id of the free fraktion * @param to Address of the user */ - function mintFreeFraktionForUser(uint256 id, address to) external payable; + function mintFreeFraktionForUser(FraktionId id, address to) external payable; /** * @notice Mint a free fraktion for the given user - * @dev Will mint a new free FraktionToken for the user, by first ensuring the user doesn't have any fraktion, only performed when contract not paused and by the right person + * @dev Will mint a new free FraktionToken for the user, by first ensuring the user doesn't have any fraktion, + * only performed when contract not paused and by the right person * @param id Id of the free fraktion */ - function mintFreeFraktion(uint256 id) external payable; + function mintFreeFraktion(FraktionId id) external payable; /** * @notice Increase the total supply for the given fraktion id - * @dev Will call our FraktionTokens contract and increase the supply for the given fraktion, only if all of it have been minted + * @dev Will call our FraktionTokens contract and increase the supply for the given fraktion, only if all of it + * have been minted * @param id The id of the fraktion for which we want to increase the supply * @param newSupply The supply we wan't to append for this fraktion */ - function increaseSupply(uint256 id, uint256 newSupply) external; + function increaseSupply(FraktionId id, uint256 newSupply) external; } diff --git a/contracts/minter/Minter.sol b/contracts/minter/Minter.sol index 0e47245..8abcecc 100644 --- a/contracts/minter/Minter.sol +++ b/contracts/minter/Minter.sol @@ -1,39 +1,26 @@ // SPDX-License-Identifier: GNU GPLv3 pragma solidity 0.8.21; -import {IMinter} from "./IMinter.sol"; -import {FractionCostBadges} from "./badges/FractionCostBadges.sol"; -import {FrakMath} from "../utils/FrakMath.sol"; -import {FrakRoles} from "../utils/FrakRoles.sol"; -import {FraktionTokens} from "../tokens/FraktionTokens.sol"; -import {IFrakToken} from "../tokens/IFrakToken.sol"; -import {MintingAccessControlUpgradeable} from "../utils/MintingAccessControlUpgradeable.sol"; -import {InvalidAddress} from "../utils/FrakErrors.sol"; -import {Multicallable} from "solady/utils/Multicallable.sol"; - -/** - * @author @KONFeature - * @title Minter - * @notice This contract will mint new content on the ecosytem, and mint fraktions for the user - * @dev Communicate with the FrkToken and FraktionTokens contract to handle minting of content and fraktions - * @custom:security-contact contact@frak.id - */ -contract Minter is IMinter, MintingAccessControlUpgradeable, FractionCostBadges, Multicallable { - using FrakMath for uint256; - +import { IMinter } from "./IMinter.sol"; +import { FraktionCostBadges } from "./badges/FraktionCostBadges.sol"; +import { ContentId } from "../libs/ContentId.sol"; +import { FraktionId } from "../libs/FraktionId.sol"; +import { FrakRoles } from "../roles/FrakRoles.sol"; +import { FraktionTokens } from "../fraktions/FraktionTokens.sol"; +import { IFrakToken } from "../tokens/IFrakToken.sol"; +import { FrakAccessControlUpgradeable } from "../roles/FrakAccessControlUpgradeable.sol"; +import { InvalidAddress } from "../utils/FrakErrors.sol"; +import { Multicallable } from "solady/utils/Multicallable.sol"; + +/// @author @KONFeature +/// @title Minter +/// @notice This contract will mint new content on the ecosytem, and mint fraktions for the user +/// @custom:security-contact contact@frak.id +contract Minter is IMinter, FrakAccessControlUpgradeable, FraktionCostBadges, Multicallable { /* -------------------------------------------------------------------------- */ /* Error's */ /* -------------------------------------------------------------------------- */ - /// @dev Error emitted when the input supply is invalid - error InvalidSupply(); - - /// @dev Error emitted when we only want to mint a free fraktion, and that's not a free fraktion - error ExpectingOnlyFreeFraktion(); - - /// @dev Error emitted when the have more than one fraktions of the given type - error TooManyFraktion(); - /// @dev 'bytes4(keccak256(bytes("InvalidAddress()")))' uint256 private constant _INVALID_ADDRESS_SELECTOR = 0xe6c4247b; @@ -50,12 +37,6 @@ contract Minter is IMinter, MintingAccessControlUpgradeable, FractionCostBadges, /* Event's */ /* -------------------------------------------------------------------------- */ - /// @dev Event emitted when a new content is minted - event ContentMinted(uint256 baseId, address indexed owner); - - /// @dev Event emitted when a new fraktion for a content is minted - event FractionMinted(uint256 indexed fractionId, address indexed user, uint256 amount, uint256 cost); - /// @dev 'keccak256("ContentMinted(uint256,address)")' uint256 private constant _CONTENT_MINTED_EVENT_SELECTOR = 0x15d512bd00e3acbb8a53b8fd503e98977b1af7618af12cbf83e463aefe880c1b; @@ -93,7 +74,11 @@ contract Minter is IMinter, MintingAccessControlUpgradeable, FractionCostBadges, * @param fraktionTokensAddr The address of the FraktionTokens contract * @param foundationAddr The foundation wallet address */ - function initialize(address frkTokenAddr, address fraktionTokensAddr, address foundationAddr) + function initialize( + address frkTokenAddr, + address fraktionTokensAddr, + address foundationAddr + ) external initializer { @@ -102,7 +87,7 @@ contract Minter is IMinter, MintingAccessControlUpgradeable, FractionCostBadges, } // Only for v1 deployment - __MintingAccessControlUpgradeable_init(); + __FrakAccessControlUpgradeable_Minter_init(); fraktionTokens = FraktionTokens(fraktionTokensAddr); frakToken = IFrakToken(frkTokenAddr); @@ -119,7 +104,8 @@ contract Minter is IMinter, MintingAccessControlUpgradeable, FractionCostBadges, /** * @notice Mint a new content to the FrkEcosystem - * @dev Will ensure the role and contract state, then the param, and finally call the FraktionTokens contract to mint the new content + * @dev Will ensure the role and contract state, then the param, and finally call the FraktionTokens contract to + * mint the new content * @param contentOwnerAddress The address of the owner of the given content * @param commonSupply The supply desired for each common fraktion of this content * @param premiumSupply The supply desired for each premium fraktion of this content @@ -133,7 +119,14 @@ contract Minter is IMinter, MintingAccessControlUpgradeable, FractionCostBadges, uint256 premiumSupply, uint256 goldSupply, uint256 diamondSupply - ) external payable override onlyRole(FrakRoles.MINTER) whenNotPaused returns (uint256 contentId) { + ) + external + payable + override + onlyRole(FrakRoles.MINTER) + whenNotPaused + returns (ContentId contentId) + { assembly { // Check owner address if iszero(contentOwnerAddress) { @@ -149,7 +142,7 @@ contract Minter is IMinter, MintingAccessControlUpgradeable, FractionCostBadges, revert(0x1c, 0x04) } } - // Then set the supply for each token types + // Then set the supply for each fraktion types uint256[] memory fraktionTypes; uint256[] memory supplies; assembly { @@ -183,7 +176,8 @@ contract Minter is IMinter, MintingAccessControlUpgradeable, FractionCostBadges, /** * @notice Mint a new fraktion for the given amount and user - * @dev Will compute the fraktion price, ensure the user have enough Frk to buy it, if try, perform the transfer and mint the fraktion + * @dev Will compute the fraktion price, ensure the user have enough Frk to buy it, if try, perform the transfer + * and mint the fraktion * @param id The id of the fraktion to be minted for the user * @param to The address on which we will mint the fraktion * @param deadline The deadline for the permit of the allowance tx @@ -191,7 +185,14 @@ contract Minter is IMinter, MintingAccessControlUpgradeable, FractionCostBadges, * @param r Signature spec secp256k1 * @param s Signature spec secp256k1 */ - function mintFraktionForUser(uint256 id, address to, uint256 deadline, uint8 v, bytes32 r, bytes32 s) + function mintFraktionForUser( + FraktionId id, + address to, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) external payable onlyRole(FrakRoles.MINTER) @@ -202,24 +203,39 @@ contract Minter is IMinter, MintingAccessControlUpgradeable, FractionCostBadges, /** * @notice Mint a new fraktion for the given amount to the caller - * @dev Will compute the fraktion price, ensure the user have enough Frk to buy it, if try, perform the transfer and mint the fraktion + * @dev Will compute the fraktion price, ensure the user have enough Frk to buy it, if try, perform the transfer + * and mint the fraktion * @param id The id of the fraktion to be minted for the user * @param deadline The deadline for the permit of the allowance tx * @param v Signature spec secp256k1 * @param r Signature spec secp256k1 * @param s Signature spec secp256k1 */ - function mintFraktion(uint256 id, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external payable whenNotPaused { - _mintFraktionForUser(id, _msgSender(), deadline, v, r, s); + function mintFraktion( + FraktionId id, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) + external + payable + whenNotPaused + { + _mintFraktionForUser(id, msg.sender, deadline, v, r, s); } /** * @notice Mint a free fraktion for the given user - * @dev Will mint a new free FraktionToken for the user, by first ensuring the user doesn't have any fraktion, only performed when contract not paused and by the right person + * @dev Will mint a new free FraktionToken for the user, by first ensuring the user doesn't have any fraktion, + * only performed when contract not paused and by the right person * @param id Id of the free fraktion * @param to Address of the user */ - function mintFreeFraktionForUser(uint256 id, address to) + function mintFreeFraktionForUser( + FraktionId id, + address to + ) external payable override @@ -231,37 +247,43 @@ contract Minter is IMinter, MintingAccessControlUpgradeable, FractionCostBadges, /** * @notice Mint a free fraktion for the given user - * @dev Will mint a new free FraktionToken for the user, by first ensuring the user doesn't have any fraktion, only performed when contract not paused and by the right person + * @dev Will mint a new free FraktionToken for the user, by first ensuring the user doesn't have any fraktion, + * only performed when contract not paused and by the right person * @param id Id of the free fraktion */ - function mintFreeFraktion(uint256 id) external payable override whenNotPaused { - _mintFreeFraktionForUser(id, _msgSender()); + function mintFreeFraktion(FraktionId id) external payable override whenNotPaused { + _mintFreeFraktionForUser(id, msg.sender); } /** * @notice Increase the total supply for the given fraktion id - * @dev Will call our FraktionTokens contract and increase the supply for the given fraktion, only if all of it have been minted + * @dev Will call our FraktionTokens contract and increase the supply for the given fraktion, only if all of it + * have been minted * @param id The id of the fraktion for which we want to increase the supply * @param newSupply The supply we wan't to append for this fraktion */ - function increaseSupply(uint256 id, uint256 newSupply) external onlyRole(FrakRoles.MINTER) whenNotPaused { + function increaseSupply(FraktionId id, uint256 newSupply) external onlyRole(FrakRoles.MINTER) whenNotPaused { // Update the supply - fraktionTokens.setSupply(id, newSupply); + fraktionTokens.setSupply(FraktionId.unwrap(id), newSupply); } /** * @notice Update the cost badge for the given fraktion - * @dev Call to the FraktionCostBadges subclass to update the cost badge, need the right role and contract unpaused - * @param fractionId The id of the fraktion we will update the badge + * @dev Call to the FraktionCostBadges subclass to update the cost badge, need the right role and contract + * unpaused + * @param fraktionId The id of the fraktion we will update the badge * @param badge The new badge for the fraktion */ - function updateCostBadge(uint256 fractionId, uint96 badge) + function updateCostBadge( + FraktionId fraktionId, + uint96 badge + ) external override onlyRole(FrakRoles.BADGE_UPDATER) whenNotPaused { - _updateCostBadge(fractionId, badge); + _updateCostBadge(fraktionId, badge); } /* -------------------------------------------------------------------------- */ @@ -270,7 +292,8 @@ contract Minter is IMinter, MintingAccessControlUpgradeable, FractionCostBadges, /** * @notice Mint a new fraktion for the given amount and user - * @dev Will compute the fraktion price, ensure the user have enough Frk to buy it, if try, perform the transfer and mint the fraktion + * @dev Will compute the fraktion price, ensure the user have enough Frk to buy it, if try, perform the transfer + * and mint the fraktion * @param id The id of the fraktion to be minted for the user * @param to The address on which we will mint the fraktion * @param deadline The deadline for the permit of the allowance tx @@ -278,13 +301,13 @@ contract Minter is IMinter, MintingAccessControlUpgradeable, FractionCostBadges, * @param r Signature spec secp256k1 * @param s Signature spec secp256k1 */ - function _mintFraktionForUser(uint256 id, address to, uint256 deadline, uint8 v, bytes32 r, bytes32 s) private { + function _mintFraktionForUser(FraktionId id, address to, uint256 deadline, uint8 v, bytes32 r, bytes32 s) private { // Get the current user balance, and exit if he already got a fraktion of this type - uint256 balance = fraktionTokens.balanceOf(to, id); + uint256 balance = fraktionTokens.balanceOf(to, FraktionId.unwrap(id)); if (balance != 0) { revert TooManyFraktion(); } - // Get the cost of the fraction + // Get the cost of the fraktionId uint256 cost = getCostBadge(id); assembly { // Emit the event @@ -296,16 +319,17 @@ contract Minter is IMinter, MintingAccessControlUpgradeable, FractionCostBadges, frakToken.permit(to, address(this), cost, deadline, v, r, s); // Transfer the tokens frakToken.transferFrom(to, foundationWallet, cost); - // Mint his Fraction of NFT - fraktionTokens.mint(to, id, 1); + // Mint his fraktion + fraktionTokens.mint(to, FraktionId.unwrap(id), 1); } /** * @notice Mint a free fraktion for the given user - * @dev Will mint a new free FraktionToken for the user, by first ensuring the user doesn't have any fraktion, only performed when contract not paused and by the right person + * @dev Will mint a new free FraktionToken for the user, by first ensuring the user doesn't have any fraktion, + * only performed when contract not paused and by the right person * @param id Id of the free fraktion */ - function _mintFreeFraktionForUser(uint256 id, address to) private { + function _mintFreeFraktionForUser(FraktionId id, address to) private { assembly { // Check if it's a free fraktion if iszero(eq(and(id, 0xF), 0x2)) { @@ -315,12 +339,12 @@ contract Minter is IMinter, MintingAccessControlUpgradeable, FractionCostBadges, } // Get the current user balance, and exit if he already got a fraktion of this type - uint256 balance = fraktionTokens.balanceOf(to, id); + uint256 balance = fraktionTokens.balanceOf(to, FraktionId.unwrap(id)); if (balance != 0) { revert TooManyFraktion(); } // If we are all good, mint the free fraktion to the user - fraktionTokens.mint(to, id, 1); + fraktionTokens.mint(to, FraktionId.unwrap(id), 1); } } diff --git a/contracts/minter/badges/FractionCostBadges.sol b/contracts/minter/badges/FractionCostBadges.sol deleted file mode 100644 index 6bd6707..0000000 --- a/contracts/minter/badges/FractionCostBadges.sol +++ /dev/null @@ -1,110 +0,0 @@ -// SPDX-License-Identifier: GNU GPLv3 -pragma solidity 0.8.21; - -import {FrakMath} from "../../utils/FrakMath.sol"; -import {InvalidFraktionType} from "../../utils/FrakErrors.sol"; - -/** - * @author @KONFeature - * @title FractionCostBadges - * @dev Abstract contract for managing the badge costs of fractions. - * @notice This contract contains methods and variables for initializing, updating, and getting the badge costs of fractions. - * @custom:security-contact contact@frak.id - */ -abstract contract FractionCostBadges { - /* -------------------------------------------------------------------------- */ - /* Error's */ - /* -------------------------------------------------------------------------- */ - - /// @dev 'bytes4(keccak256("InvalidFraktionType()"))' - uint256 private constant _INVALID_FRAKTION_TYPE_SELECTOR = 0x3f126a45; - - /* -------------------------------------------------------------------------- */ - /* Event's */ - /* -------------------------------------------------------------------------- */ - - /** - * @dev Emitted when the badge cost of a fraction is updated. - * @param id The id of the updated fraction. - * @param badge The new badge cost of the fraction in wei. - */ - event FractionCostBadgeUpdated(uint256 indexed id, uint256 badge); - - /* -------------------------------------------------------------------------- */ - /* Storage */ - /* -------------------------------------------------------------------------- */ - - /** - * @dev Map f nft id to cost badge. - * @notice This variable is private and can only be accessed by the current contract. - */ - mapping(uint256 => uint96) private fractionBadges; - - /* -------------------------------------------------------------------------- */ - /* Abstract function's */ - /* -------------------------------------------------------------------------- */ - - /** - * @dev Update the badge cost of the specified fraction. - * @notice This function can be overridden by inheriting contracts. - * @param fractionId The id of the fraction to update the badge cost of. - * @param badge The new badge cost of the fraction in wei. - */ - function updateCostBadge(uint256 fractionId, uint96 badge) external virtual; - - /* -------------------------------------------------------------------------- */ - /* Internal write function's */ - /* -------------------------------------------------------------------------- */ - - /** - * @dev Update the badge cost of the specified fraction and emit an event. - * @param fractionId The id of the fraction to update the badge cost of. - * @param badge The new badge cost of the fraction in wei. - */ - function _updateCostBadge(uint256 fractionId, uint96 badge) internal { - fractionBadges[fractionId] = badge; - emit FractionCostBadgeUpdated(fractionId, badge); - } - - /* -------------------------------------------------------------------------- */ - /* Public read function's */ - /* -------------------------------------------------------------------------- */ - - /** - * @dev Returns the badge cost of the specified fraction. - * @notice If the badge of this fraction isn't set yet, it will be set to the default initial cost. - * @param fractionId The id of the fraction to get the badge cost of. - * @return fractionBadge The badge cost of the specified fraction in wei. - */ - function getCostBadge(uint256 fractionId) public view returns (uint96 fractionBadge) { - fractionBadge = fractionBadges[fractionId]; - if (fractionBadge == 0) { - // If the badge of this fraction isn't set yet, set it to default - uint256 tokenType = FrakMath.extractTokenType(fractionId); - fractionBadge = initialFractionCost(tokenType); - } - } - - /** - * @dev Returns the initial cost of a fraction of the specified token type in wei. - * @notice This method should only be called with valid token types as defined by the FrakMath contract. - * @param tokenType The type of token to get the initial cost of. - * @return initialCost The initial cost of the specified token type in wei. - */ - function initialFractionCost(uint256 tokenType) internal pure returns (uint96 initialCost) { - if (tokenType == FrakMath.TOKEN_TYPE_COMMON_MASK) { - initialCost = 90 ether; // 90 FRK - } else if (tokenType == FrakMath.TOKEN_TYPE_PREMIUM_MASK) { - initialCost = 500 ether; // 500 FRK - } else if (tokenType == FrakMath.TOKEN_TYPE_GOLD_MASK) { - initialCost = 1_200 ether; // 1.2k FRK - } else if (tokenType == FrakMath.TOKEN_TYPE_DIAMOND_MASK) { - initialCost = 3_000 ether; // 3k FRK - } else { - assembly { - mstore(0x00, _INVALID_FRAKTION_TYPE_SELECTOR) - revert(0x1c, 0x04) - } - } - } -} diff --git a/contracts/minter/badges/FraktionCostBadges.sol b/contracts/minter/badges/FraktionCostBadges.sol new file mode 100644 index 0000000..2306c97 --- /dev/null +++ b/contracts/minter/badges/FraktionCostBadges.sol @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GNU GPLv3 +pragma solidity 0.8.21; + +import { FraktionId } from "../../libs/FraktionId.sol"; +import { ContentIdLib } from "../../libs/ContentId.sol"; +import { InvalidFraktionType } from "../../utils/FrakErrors.sol"; + +/// @author @KONFeature +/// @title FraktionCostBadges +/// @notice Abstract contract for managing the badge costs of fraktions. +/// @custom:security-contact contact@frak.id +abstract contract FraktionCostBadges { + /* -------------------------------------------------------------------------- */ + /* Error's */ + /* -------------------------------------------------------------------------- */ + + /// @dev 'bytes4(keccak256("InvalidFraktionType()"))' + uint256 private constant _INVALID_FRAKTION_TYPE_SELECTOR = 0x3f126a45; + + /* -------------------------------------------------------------------------- */ + /* Event's */ + /* -------------------------------------------------------------------------- */ + + /** + * @dev Emitted when the badge cost of a fraktion is updated. + * @param id The id of the updated fraktion. + * @param badge The new badge cost of the fraktion in wei. + */ + event FraktionCostBadgeUpdated(uint256 indexed id, uint256 badge); + + /* -------------------------------------------------------------------------- */ + /* Storage */ + /* -------------------------------------------------------------------------- */ + + /** + * @dev Map f nft id to cost badge. + * @notice This variable is private and can only be accessed by the current contract. + */ + mapping(FraktionId frakionId => uint96 cost) private fraktionBadges; + + /* -------------------------------------------------------------------------- */ + /* Abstract function's */ + /* -------------------------------------------------------------------------- */ + + /** + * @dev Update the badge cost of the specified fraktion. + * @notice This function can be overridden by inheriting contracts. + * @param fraktionId The id of the fraktion to update the badge cost of. + * @param badge The new badge cost of the fraktion in wei. + */ + function updateCostBadge(FraktionId fraktionId, uint96 badge) external virtual; + + /* -------------------------------------------------------------------------- */ + /* Internal write function's */ + /* -------------------------------------------------------------------------- */ + + /** + * @dev Update the badge cost of the specified fraktionId and emit an event. + * @param fraktionId The id of the fraktionId to update the badge cost of. + * @param badge The new badge cost of the fraktionId in wei. + */ + function _updateCostBadge(FraktionId fraktionId, uint96 badge) internal { + fraktionBadges[fraktionId] = badge; + emit FraktionCostBadgeUpdated(FraktionId.unwrap(fraktionId), badge); + } + + /* -------------------------------------------------------------------------- */ + /* Public read function's */ + /* -------------------------------------------------------------------------- */ + + /** + * @dev Returns the badge cost of the specified fraktion. + * @notice If the badge of this fraktionId isn't set yet, it will be set to the default initial cost. + * @param fraktionId The id of the fraktionId to get the badge cost of. + * @return fraktionBadge The badge cost of the specified fraktionId in wei. + */ + function getCostBadge(FraktionId fraktionId) public view returns (uint96 fraktionBadge) { + fraktionBadge = fraktionBadges[fraktionId]; + if (fraktionBadge == 0) { + // If the badge of this fraktionId isn't set yet, set it to default + uint256 fraktionType = fraktionId.getFraktionType(); + fraktionBadge = initialFraktionCost(fraktionType); + } + } + + /** + * @dev Returns the initial cost of a fraktionId of the specified fraktion type in wei. + * @notice This method should only be called with valid fraktion types as defined by the FrakMath contract. + * @param fraktionType The type of fraktion to get the initial cost of. + * @return initialCost The initial cost of the specified fraktion type in wei. + */ + function initialFraktionCost(uint256 fraktionType) internal pure returns (uint96 initialCost) { + if (fraktionType == ContentIdLib.FRAKTION_TYPE_COMMON) { + initialCost = 90 ether; // 90 FRK + } else if (fraktionType == ContentIdLib.FRAKTION_TYPE_PREMIUM) { + initialCost = 500 ether; // 500 FRK + } else if (fraktionType == ContentIdLib.FRAKTION_TYPE_GOLD) { + initialCost = 1200 ether; // 1.2k FRK + } else if (fraktionType == ContentIdLib.FRAKTION_TYPE_DIAMOND) { + initialCost = 3000 ether; // 3k FRK + } else { + assembly { + mstore(0x00, _INVALID_FRAKTION_TYPE_SELECTOR) + revert(0x1c, 0x04) + } + } + } +} diff --git a/contracts/reward/IRewarder.sol b/contracts/reward/IRewarder.sol index 0d99374..d3f77a2 100644 --- a/contracts/reward/IRewarder.sol +++ b/contracts/reward/IRewarder.sol @@ -1,19 +1,58 @@ // SPDX-License-Identifier: GNU GPLv3 pragma solidity 0.8.21; -import {IPausable} from "../utils/IPausable.sol"; +import { IPausable } from "../utils/IPausable.sol"; +import { ContentId } from "../libs/ContentId.sol"; -/** - * @dev Represent our rewarder contract - */ +/// @author @KONFeature +/// @title IRewarder +/// @notice Interface for the rewarder contract +/// @custom:security-contact contact@frak.id interface IRewarder is IPausable { - /** - * @dev Pay a user for all the listening he have done on different badge - */ + /// @dev Error throwned when the reward is invalid + error InvalidReward(); + + /* -------------------------------------------------------------------------- */ + /* Event's */ + /* -------------------------------------------------------------------------- */ + + /// @dev Event emitted when a user is rewarded for his listen + event RewardOnContent( + address indexed user, uint256 indexed contentId, uint256 baseUserReward, uint256 earningFactor + ); + + /* -------------------------------------------------------------------------- */ + /* External write function's */ + /* -------------------------------------------------------------------------- */ + + /// @dev Directly pay a `listener` for the given frk `amount` (used for offchain to onchain wallet migration) + function payUserDirectly(address listener, uint256 amount) external payable; + + /// @dev Directly pay all the creators owner of `contentIds` for each given frk `amounts` (used for offchain reward + /// created by the user, thatis sent to the creator) + function payCreatorDirectlyBatch(ContentId[] calldata contentIds, uint256[] calldata amounts) external payable; + + /// @dev Compute the reward for a `listener`, given the `contentType`, `contentIds` and `listenCounts`, and pay him + /// and the owner function payUser( address listener, uint256 contentType, - uint256[] calldata contentIds, + ContentId[] calldata contentIds, uint256[] calldata listenCounts - ) external payable; + ) + external + payable; + + /// @dev Update the token generation factor to 'newTpu' + function updateTpu(uint256 newTpu) external; + + /* -------------------------------------------------------------------------- */ + /* External view function's */ + /* -------------------------------------------------------------------------- */ + + /// @dev Get the current TPU + function getTpu() external view returns (uint256); + + /// @dev Get the current number of FRK minted + function getFrkMinted() external view returns (uint256); } diff --git a/contracts/reward/RewardAccounter.sol b/contracts/reward/RewardAccounter.sol new file mode 100644 index 0000000..a189902 --- /dev/null +++ b/contracts/reward/RewardAccounter.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GNU GPLv3 +pragma solidity 0.8.21; + +/// @dev Struct representing the accounter for the reward +struct RewardAccounter { + uint256 user; + uint256 owners; + uint256 content; +} + +/// @dev Tell to use the lib below for every RewardAccounter instance +using RewardAccounterLib for RewardAccounter global; + +/// @dev 1 ether in WAD +uint256 constant WAD = 1 ether; + +/// @author @KONFeature +/// @title RewardAccounterLib +/// @notice Library to ease the usage of the RewardAccounter struct +/// @custom:security-contact contact@frak.id +library RewardAccounterLib { + /// @dev Apply the user `badge` booster on the user reward + function applyUserBadge(RewardAccounter memory self, uint256 badge) internal pure { + unchecked { + self.user = self.user * badge / WAD; + } + } + + /// @dev Get the total amount in the accounter + function getTotal(RewardAccounter memory self) internal pure returns (uint256 total) { + unchecked { + total = self.user + self.owners + self.content; + } + } +} diff --git a/contracts/reward/Rewarder.sol b/contracts/reward/Rewarder.sol index 57cbd0e..787b33b 100644 --- a/contracts/reward/Rewarder.sol +++ b/contracts/reward/Rewarder.sol @@ -1,23 +1,26 @@ // SPDX-License-Identifier: GNU GPLv3 pragma solidity 0.8.21; -import {IRewarder} from "./IRewarder.sol"; -import {ContentBadges} from "./badges/ContentBadges.sol"; -import {ListenerBadges} from "./badges/ListenerBadges.sol"; -import {ContentPool} from "./pool/ContentPool.sol"; -import {ReferralPool} from "./pool/ReferralPool.sol"; -import {FrakMath} from "../utils/FrakMath.sol"; -import {FrakRoles} from "../utils/FrakRoles.sol"; -import {FraktionTokens} from "../tokens/FraktionTokens.sol"; -import {IFrakToken} from "../tokens/IFrakToken.sol"; -import {FrakAccessControlUpgradeable} from "../utils/FrakAccessControlUpgradeable.sol"; -import {InvalidAddress, InvalidArray, RewardTooLarge} from "../utils/FrakErrors.sol"; -import {PushPullReward} from "../utils/PushPullReward.sol"; -import {Multicallable} from "solady/utils/Multicallable.sol"; - -/** - * @dev Represent our rewarder contract - */ +import { IRewarder } from "./IRewarder.sol"; +import { ContentBadges } from "./badges/ContentBadges.sol"; +import { ListenerBadges } from "./badges/ListenerBadges.sol"; +import { IContentPool } from "./contentPool/IContentPool.sol"; +import { IReferralPool } from "./referralPool/IReferralPool.sol"; +import { ArrayLib } from "../libs/ArrayLib.sol"; +import { ContentId } from "../libs/ContentId.sol"; +import { FraktionId } from "../libs/FraktionId.sol"; +import { FrakRoles } from "../roles/FrakRoles.sol"; +import { RewardAccounter } from "./RewardAccounter.sol"; +import { FraktionTokens } from "../fraktions/FraktionTokens.sol"; +import { IFrakToken } from "../tokens/IFrakToken.sol"; +import { FrakAccessControlUpgradeable } from "../roles/FrakAccessControlUpgradeable.sol"; +import { InvalidAddress, InvalidArray, RewardTooLarge } from "../utils/FrakErrors.sol"; +import { PushPullReward } from "../utils/PushPullReward.sol"; +import { Multicallable } from "solady/utils/Multicallable.sol"; + +/// @author @KONFeature +/// @title Rewarder +/// @notice Contract in charge of managing the reward for the user / creator /// @custom:security-contact contact@frak.id contract Rewarder is IRewarder, @@ -27,8 +30,6 @@ contract Rewarder is PushPullReward, Multicallable { - using FrakMath for uint256; - /* -------------------------------------------------------------------------- */ /* Constant's */ /* -------------------------------------------------------------------------- */ @@ -55,9 +56,6 @@ contract Rewarder is /* Custom error's */ /* -------------------------------------------------------------------------- */ - /// @dev Error throwned when the reward is invalid - error InvalidReward(); - /// @dev 'bytes4(keccak256(bytes("InvalidAddress()")))' uint256 private constant _INVALID_ADDRESS_SELECTOR = 0xe6c4247b; @@ -76,23 +74,13 @@ contract Rewarder is /// @dev Event emitted when a user is rewarded for his listen event RewardOnContent( - address indexed user, uint256 indexed contentId, uint256 baseUserReward, uint256 earningFactor + address indexed user, ContentId indexed contentId, uint256 baseUserReward, uint256 earningFactor ); /// @dev 'keccak256(bytes("RewardOnContent(address,uint256,uint256,uint256"))' uint256 private constant _REWARD_ON_CONTENT_EVENT_SELECTOR = 0x6396a2d965d9d843b0159912a8413f069bcce1830e320cd0b6cd5dc03d11eddf; - /* -------------------------------------------------------------------------- */ - /* Struct's */ - /* -------------------------------------------------------------------------- */ - - struct TotalRewards { - uint256 user; - uint256 owners; - uint256 content; - } - /* -------------------------------------------------------------------------- */ /* Storage */ /* -------------------------------------------------------------------------- */ @@ -113,10 +101,10 @@ contract Rewarder is IFrakToken private frakToken; /// @dev Access our referral system - ReferralPool private referralPool; + IReferralPool private referralPool; /// @dev Access our content pool - ContentPool private contentPool; + IContentPool private contentPool; /// @dev Address of the foundation wallet address private foundationWallet; @@ -132,7 +120,10 @@ contract Rewarder is address contentPoolAddr, address referralAddr, address foundationAddr - ) external initializer { + ) + external + initializer + { if ( frkTokenAddr == address(0) || fraktionTokensAddr == address(0) || contentPoolAddr == address(0) || referralAddr == address(0) || foundationAddr == address(0) @@ -144,8 +135,8 @@ contract Rewarder is fraktionTokens = FraktionTokens(fraktionTokensAddr); frakToken = IFrakToken(frkTokenAddr); - contentPool = ContentPool(contentPoolAddr); - referralPool = ReferralPool(referralAddr); + contentPool = IContentPool(contentPoolAddr); + referralPool = IReferralPool(referralAddr); foundationWallet = foundationAddr; @@ -161,10 +152,11 @@ contract Rewarder is /* External write function's */ /* -------------------------------------------------------------------------- */ - /** - * @dev Directly pay a user for the given frk amount (used for offchain to onchain wallet migration) - */ - function payUserDirectly(address listener, uint256 amount) + /// @dev Directly pay a `listener` for the given frk `amount` (used for offchain to onchain wallet migration) + function payUserDirectly( + address listener, + uint256 amount + ) external payable onlyRole(FrakRoles.REWARDER) @@ -195,10 +187,12 @@ contract Rewarder is token.transfer(listener, amount); } - /** - * @notice Directly pay all the creator for the given frk amount (used for offchain reward created by the user, that is sent to the creator) - */ - function payCreatorDirectlyBatch(uint256[] calldata contentIds, uint256[] calldata amounts) + /// @dev Directly pay all the creators owner of `contentIds` for each given frk `amounts` (used for offchain reward + /// created by the user, thatis sent to the creator) + function payCreatorDirectlyBatch( + ContentId[] calldata contentIds, + uint256[] calldata amounts + ) external payable onlyRole(FrakRoles.REWARDER) @@ -251,15 +245,19 @@ contract Rewarder is } } - /** - * @dev Compute the reward for a user, given the content and listens, and pay him and the owner - */ + /// @dev Compute the reward for a `listener`, given the `contentType`, `contentIds` and `listenCounts`, and pay him + /// and the owner function payUser( address listener, uint256 contentType, - uint256[] calldata contentIds, + ContentId[] calldata contentIds, uint256[] calldata listenCounts - ) external payable onlyRole(FrakRoles.REWARDER) whenNotPaused { + ) + external + payable + onlyRole(FrakRoles.REWARDER) + whenNotPaused + { // Ensure we got valid data assembly { if iszero(listener) { @@ -273,69 +271,56 @@ contract Rewarder is } // Get the data we will need in this level - TotalRewards memory totalRewards = TotalRewards(0, 0, 0); + RewardAccounter memory rewardsAccounter = RewardAccounter(0, 0, 0); // Get all the payed fraktion types - uint256[] memory fraktionTypes = FrakMath.payableTokenTypes(); uint256 rewardForContentType = baseRewardForContentType(contentType); // Iterate over each content the user listened uint256 length = contentIds.length; for (uint256 i; i < length;) { - computeRewardForContent( - contentIds[i], listenCounts[i], rewardForContentType, listener, fraktionTypes, totalRewards - ); + computeRewardForContent(contentIds[i], listenCounts[i], rewardForContentType, listener, rewardsAccounter); // Finally, increase the counter unchecked { ++i; } } - - // Then outside of our loop find the user badge - uint256 listenerBadge = getListenerBadge(listener); - // Compute the total amount to mint, and ensure we don't exceed our cap - assembly { - // Update the total mint for user with his listener badges - mstore(totalRewards, div(mul(listenerBadge, mload(totalRewards)), 1000000000000000000)) + unchecked { + // Apply the user badge + rewardsAccounter.applyUserBadge(getListenerBadge(listener)); - // Compute the total to be minted - let userAndOwner := add(mload(totalRewards), mload(add(totalRewards, 0x20))) - let referralAndContent := add(mload(add(totalRewards, 0x40)), mload(add(totalRewards, 0x60))) - let totalMint := add(userAndOwner, referralAndContent) + // Compute the new total frk minted for reward + uint256 newTotalMint = totalFrakMinted + rewardsAccounter.getTotal(); - // Compute the new total amount - let newTotalAmount := add(totalMint, sload(totalFrakMinted.slot)) - // Ensure it's good - if gt(newTotalAmount, REWARD_MINT_CAP) { - mstore(0x00, _REWARD_TOO_LARGE_SELECTOR) - revert(0x1c, 0x04) + // Ensure we don't go past the mint cap + if (newTotalMint > REWARD_MINT_CAP) { + revert RewardTooLarge(); } + // Increase our total frak minted - sstore(totalFrakMinted.slot, newTotalAmount) + totalFrakMinted = newTotalMint; } // Register the amount for listener - _addFoundsUnchecked(listener, totalRewards.user); + _addFoundsUnchecked(listener, rewardsAccounter.user); // If we got reward for the pool, transfer them - if (totalRewards.content > 0) { - token.transfer(address(contentPool), totalRewards.content); + if (rewardsAccounter.content > 0) { + token.transfer(address(contentPool), rewardsAccounter.content); } - /*if (totalRewards.referral > 0) { - token.transfer(address(referralPool), totalRewards.referral); + /*if (rewardsAccounter.referral > 0) { + token.transfer(address(referralPool), rewardsAccounter.referral); }*/ } - /** - * @dev Withdraw my pending founds - */ + /// @dev Withdraw the pending founds of the caller function withdrawFounds() external override whenNotPaused { _withdrawWithFee(msg.sender, FEE_PERCENT, foundationWallet); } - /// @dev Withdraw the pending founds for 'user' + /// @dev Withdraw the pending founds of `user` function withdrawFounds(address user) external override onlyRole(FrakRoles.ADMIN) whenNotPaused { _withdrawWithFee(user, FEE_PERCENT, foundationWallet); } @@ -346,7 +331,10 @@ contract Rewarder is } /// @dev Update the 'contentId' 'badge' - function updateContentBadge(uint256 contentId, uint256 badge) + function updateContentBadge( + ContentId contentId, + uint256 badge + ) external override onlyRole(FrakRoles.BADGE_UPDATER) @@ -356,7 +344,10 @@ contract Rewarder is } /// @dev Update the 'listener' 'badge' - function updateListenerBadge(address listener, uint256 badge) + function updateListenerBadge( + address listener, + uint256 badge + ) external override onlyRole(FrakRoles.BADGE_UPDATER) @@ -383,17 +374,21 @@ contract Rewarder is /* Internal write function's */ /* -------------------------------------------------------------------------- */ - /** - * @dev Compute the user and owner reward for the given content - */ + /// @dev Compute the reward of the given content + /// @param contentId The id of the content + /// @param listenCount The number of listen for the given content + /// @param rewardForContentType The base reward for the given content type + /// @param listener The listener address + /// @param rewardsAccounter The current total rewards in memory accounting (that will be updated) function computeRewardForContent( - uint256 contentId, + ContentId contentId, uint256 listenCount, uint256 rewardForContentType, address listener, - uint256[] memory fraktionTypes, - TotalRewards memory totalRewards - ) private { + RewardAccounter memory rewardsAccounter + ) + private + { // Ensure we don't exceed the max ccu / content assembly { if gt(listenCount, MAX_CCU_PER_CONTENT) { @@ -403,7 +398,7 @@ contract Rewarder is } // Boolean used to know if the user have one paied fraktion - (uint256 earningFactor, bool hasOnePaidFraktion) = earningFactorForListener(fraktionTypes, listener, contentId); + (uint256 earningFactor, bool hasOnePaidFraktion) = earningFactorForListener(listener, contentId); // Get the content badge uint256 contentBadge = getContentBadge(contentId); @@ -430,7 +425,7 @@ contract Rewarder is // User reward at 35% let userReward := div(mul(totalReward, 35), 100) - mstore(totalRewards, add(mload(totalRewards), userReward)) + mstore(rewardsAccounter, add(mload(rewardsAccounter), userReward)) // Check if the user has got paid fraktion switch hasOnePaidFraktion @@ -444,11 +439,11 @@ contract Rewarder is ownerReward := sub(sub(totalReward, userReward), contentPoolReward) // Store the content pool reward - mstore(add(totalRewards, 0x40), add(mload(add(totalRewards, 0x40)), contentPoolReward)) + mstore(add(rewardsAccounter, 0x40), add(mload(add(rewardsAccounter, 0x40)), contentPoolReward)) } // Store the owner reward in memory - mstore(add(totalRewards, 0x20), add(mload(add(totalRewards, 0x20)), ownerReward)) + mstore(add(rewardsAccounter, 0x20), add(mload(add(rewardsAccounter, 0x20)), ownerReward)) // Emit the reward on content event's mstore(0, userReward) @@ -479,16 +474,20 @@ contract Rewarder is /* Internal view function's */ /* -------------------------------------------------------------------------- */ - /** - * @dev Compute the earning factor for a listener on a given content - */ - function earningFactorForListener(uint256[] memory fraktionTypes, address listener, uint256 contentId) + /// @dev Compute the earning factor for the given listener + /// @param listener The listener address + /// @param contentId The content id + function earningFactorForListener( + address listener, + ContentId contentId + ) private view returns (uint256 earningFactor, bool hasOnePaidFraktion) { - // Build the ids for eachs fraktion that can generate reward, and get the user balance for each one if this fraktions - uint256[] memory fraktionIds = contentId.buildSnftIds(fraktionTypes); + // Build the ids for eachs fraktion that can generate reward, and get the user balance for each one if this + // fraktions + FraktionId[] memory fraktionIds = contentId.payableFraktionIds(); uint256[] memory tokenBalances = fraktionTokens.balanceOfIdsBatch(listener, fraktionIds); assembly { @@ -500,10 +499,10 @@ contract Rewarder is let offsetEnd := add(0x20, shl(0x05, mload(fraktionIds))) // Infinite loop - for {} 1 {} { + for { } 1 { } { // Get balance and fraktion type let tokenBalance := mload(add(tokenBalances, currOffset)) - let fraktionType := mload(add(fraktionTypes, currOffset)) + let fraktionType := and(mload(add(fraktionIds, currOffset)), 0xF) // Update the one paid fraktion value if not(hasOnePaidFraktion) { @@ -535,35 +534,7 @@ contract Rewarder is /* Internal pure function's */ /* -------------------------------------------------------------------------- */ - /** - * @dev Get the base reward to the given token type - * We use a pure function instead of a mapping to economise on storage read, - * and since this reawrd shouldn't evolve really fast - */ - function baseRewardForTokenType(uint256 tokenType) private pure returns (uint256 reward) { - if (tokenType == FrakMath.TOKEN_TYPE_FREE_MASK) { - // 0.01 FRK - reward = 0.01 ether; - } else if (tokenType == FrakMath.TOKEN_TYPE_COMMON_MASK) { - // 0.1 FRK - reward = 0.1 ether; - } else if (tokenType == FrakMath.TOKEN_TYPE_PREMIUM_MASK) { - // 0.5 FRK - reward = 0.5 ether; - } else if (tokenType == FrakMath.TOKEN_TYPE_GOLD_MASK) { - // 1 FRK - reward = 1 ether; - } else if (tokenType == FrakMath.TOKEN_TYPE_DIAMOND_MASK) { - // 2 FRK - reward = 2 ether; - } else { - reward = 0; - } - } - - /** - * @dev Get the base reward to the given content type - */ + /// @dev Compute the base reward for the given `contentType` function baseRewardForContentType(uint256 contentType) private pure returns (uint256 reward) { if (contentType == CONTENT_TYPE_VIDEO) { reward = 2 ether; diff --git a/contracts/reward/badges/ContentBadges.sol b/contracts/reward/badges/ContentBadges.sol index 3ce1048..5750edf 100644 --- a/contracts/reward/badges/ContentBadges.sol +++ b/contracts/reward/badges/ContentBadges.sol @@ -1,37 +1,35 @@ // SPDX-License-Identifier: GNU GPLv3 pragma solidity 0.8.21; -import {BadgeTooLarge} from "../../utils/FrakErrors.sol"; +import { ContentId } from "../../libs/ContentId.sol"; +import { BadgeTooLarge } from "../../utils/FrakErrors.sol"; -/** - * @author @KONFeature - * @title ContentBadges - * @dev Abstract contract for managing the content badge use as multiplier for earnings. - * @notice This contract contains methods and variables for initializing, updating, and getting the content badges. - * @custom:security-contact contact@frak.id - */ +/// @author @KONFeature +/// @title ContentBadges +/// @notice Abstract contract for managing the content badge use as multiplier for earnings. +/// @custom:security-contact contact@frak.id abstract contract ContentBadges { /// @dev Max badge possible for the content - uint256 private constant MAX_CONTENT_BADGE = 1_000 ether; + uint256 private constant MAX_CONTENT_BADGE = 1000 ether; /// @dev Event emitted when a badge is updated event ContentBadgeUpdated(uint256 indexed id, uint256 badge); /// @dev Mapping of content id to content badge - mapping(uint256 => uint256) private _contentBadges; + mapping(ContentId contentId => uint256 badge) private _contentBadges; /// @dev external function used to update the content badges - function updateContentBadge(uint256 contentId, uint256 badge) external virtual; + function updateContentBadge(ContentId contentId, uint256 badge) external virtual; /// @dev Update the content 'id' badge to 'badge' - function _updateContentBadge(uint256 contentId, uint256 badge) internal { + function _updateContentBadge(ContentId contentId, uint256 badge) internal { if (badge > MAX_CONTENT_BADGE) revert BadgeTooLarge(); _contentBadges[contentId] = badge; - emit ContentBadgeUpdated(contentId, badge); + emit ContentBadgeUpdated(ContentId.unwrap(contentId), badge); } /// @dev Get the content badges for the content 'id' - function getContentBadge(uint256 contentId) public view returns (uint256 badge) { + function getContentBadge(ContentId contentId) public view returns (uint256 badge) { assembly { // Get the current content badge // Kecak (contentId, _contentBadges.slot) diff --git a/contracts/reward/badges/ListenerBadges.sol b/contracts/reward/badges/ListenerBadges.sol index 09061c3..b53bbf0 100644 --- a/contracts/reward/badges/ListenerBadges.sol +++ b/contracts/reward/badges/ListenerBadges.sol @@ -1,24 +1,21 @@ // SPDX-License-Identifier: GNU GPLv3 pragma solidity 0.8.21; -import {BadgeTooLarge} from "../../utils/FrakErrors.sol"; +import { BadgeTooLarge } from "../../utils/FrakErrors.sol"; -/** - * @author @KONFeature - * @title ListenerBadges - * @dev Abstract contract for managing the listener badge use as multiplier for earnings. - * @notice This contract contains methods and variables for initializing, updating, and getting the listener badges. - * @custom:security-contact contact@frak.id - */ +/// @author @KONFeature +/// @title ListenerBadges +/// @notice Abstract contract for managing the listener badge use as multiplier for earnings. +/// @custom:security-contact contact@frak.id abstract contract ListenerBadges { /// @dev Max badge possible for the content - uint256 private constant MAX_LISTENER_BADGE = 1_000 ether; + uint256 private constant MAX_LISTENER_BADGE = 1000 ether; /// @dev Event emitted when a badge is updated event ListenerBadgeUpdated(address indexed listener, uint256 badge); /// @dev Mapping of listener to their badge - mapping(address => uint256) private _listenerBadges; + mapping(address user => uint256 badge) private _listenerBadges; /// @dev external function used to update the content badges function updateListenerBadge(address listener, uint256 badge) external virtual; diff --git a/contracts/reward/pool/ContentPool.sol b/contracts/reward/contentPool/ContentPool.sol similarity index 78% rename from contracts/reward/pool/ContentPool.sol rename to contracts/reward/contentPool/ContentPool.sol index 069a600..dcf6349 100644 --- a/contracts/reward/pool/ContentPool.sol +++ b/contracts/reward/contentPool/ContentPool.sol @@ -1,21 +1,20 @@ // SPDX-License-Identifier: GNU GPLv3 pragma solidity 0.8.21; -import {FrakMath} from "../../utils/FrakMath.sol"; -import {FrakRoles} from "../../utils/FrakRoles.sol"; -import {FraktionTransferCallback} from "../../tokens/FraktionTransferCallback.sol"; -import {PushPullReward} from "../../utils/PushPullReward.sol"; -import {FrakAccessControlUpgradeable} from "../../utils/FrakAccessControlUpgradeable.sol"; -import {InvalidAddress, NoReward} from "../../utils/FrakErrors.sol"; -import {EnumerableSet} from "openzeppelin/utils/structs/EnumerableSet.sol"; - -/** - * @author @KONFeature - * @title ContentPool - * @dev Represent our content pool contract - * @custom:security-contact contact@frak.id - */ -contract ContentPool is FrakAccessControlUpgradeable, PushPullReward, FraktionTransferCallback { +import { FraktionId } from "../../libs/FraktionId.sol"; +import { ContentId, ContentIdLib } from "../../libs/ContentId.sol"; +import { FrakRoles } from "../../roles/FrakRoles.sol"; +import { PushPullReward } from "../../utils/PushPullReward.sol"; +import { FrakAccessControlUpgradeable } from "../../roles/FrakAccessControlUpgradeable.sol"; +import { InvalidAddress, NoReward } from "../../utils/FrakErrors.sol"; +import { EnumerableSet } from "openzeppelin/utils/structs/EnumerableSet.sol"; +import { IContentPool } from "./IContentPool.sol"; + +/// @author @KONFeature +/// @title ContentPool +/// @notice Contract in charge of managing the content pool +/// @custom:security-contact contact@frak.id +contract ContentPool is IContentPool, FrakAccessControlUpgradeable, PushPullReward { // Add the library methods using EnumerableSet for EnumerableSet.UintSet; @@ -23,12 +22,6 @@ contract ContentPool is FrakAccessControlUpgradeable, PushPullReward, FraktionTr /* Custom error's */ /* -------------------------------------------------------------------------- */ - /// @dev The pool state is closed - error PoolStateClosed(); - - /// @dev When the user already claimed this pool state - error PoolStateAlreadyClaimed(); - /// @dev 'bytes4(keccak256(bytes("NoReward()")))' uint256 private constant _NO_REWARD_SELECTOR = 0x6e992686; @@ -42,15 +35,6 @@ contract ContentPool is FrakAccessControlUpgradeable, PushPullReward, FraktionTr /* Event's */ /* -------------------------------------------------------------------------- */ - /// @dev Event emitted when a reward is added to the pool - event PoolRewardAdded(uint256 indexed contentId, uint256 reward); - - /// @dev Event emitted when the pool shares are updated - event PoolSharesUpdated(uint256 indexed contentId, uint256 indexed poolId, uint256 totalShares); - - /// @dev Event emitted when participant share are updated - event ParticipantShareUpdated(address indexed user, uint256 indexed contentId, uint256 shares); - /// @dev 'keccak256(bytes("PoolRewardAdded(uint256,uint256)"))' uint256 private constant _POOL_REWARD_ADDED_EVENT_SELECTOR = 0xdb778ef6a08c77e60fdae7e0f8797546f4313672de2bafc3b582b6262916009e; @@ -70,73 +54,23 @@ contract ContentPool is FrakAccessControlUpgradeable, PushPullReward, FraktionTr /// @dev Maximum reward we can have in a pool uint256 private constant MAX_REWARD = 100_000 ether; - /* -------------------------------------------------------------------------- */ - /* Struct's */ - /* -------------------------------------------------------------------------- */ - - /** - * @dev Represent a pool reward state - */ - struct RewardState { - // First storage slot, remain 31 bytes (if bool encoded inside a single byte) - uint128 totalShares; // pos : 0x0 <-> 0x10 - uint96 currentPoolReward; // pos : 0x10 + 0x0C -> 0x10 <-> 0x1C - bool open; // pos : 0x01 + 0x1C -> 0x1C <-> 0x1D ? Or less since multiple value can be packed inside a single slot ? - } - - /** - * @dev Represent a pool participant - */ - struct Participant { - // First storage slot, remain 40 bytes - uint120 shares; // Number of shares in the content pool, pos : 0x0 <-> 0x0F - uint96 lastStateClaim; // The last state amount claimed, pos : 0x0F + 0x0C -> 0x0F <-> 0x1B - // Second storage slot - uint256 lastStateIndex; // What was the last state index he claimed in the pool ? -> TODO : 0x20 or Ox1B -> (0x0F + 0x0C) - } - - /** - * @dev Represent the participant state in a pool - * @dev Only used for view function! Heavy object that shouldn't be stored in storage - */ - struct ParticipantInPoolState { - // Pool info's - uint256 poolId; - uint256 totalShares; // The total shares of the pool at the last state - uint256 poolState; // The index of the current pool state - // Participant info's - uint256 shares; - uint256 lastStateClaimed; // The last state amount claimed for the given pool - uint256 lastStateIndex; // The index of the last state a participant claimed in a pool - uint256 lastStateClaimable; // The claimable amount for the participant in the given pool - } - - /** - * @dev In memory accounter for the balance claimable update post transfer - */ - struct FraktionTransferAccounter { - address from; - uint256 deltaFrom; - address to; - uint256 deltaTo; - } - /* -------------------------------------------------------------------------- */ /* Storage */ /* -------------------------------------------------------------------------- */ /// @dev The index of the current state index per content - /// TODO : This is unused now since we use the array length (more effecient since we perform a first sload on the mapping, and we need to do it anyway) + /// TODO : This is unused now since we use the array length (more effecient since we perform a first sload on the + /// mapping, and we need to do it anyway) mapping(uint256 => uint256) private currentStateIndex; /// @dev All the different reward states per content id - mapping(uint256 => RewardState[]) private rewardStates; + mapping(uint256 contentId => RewardState[] states) private rewardStates; /// @dev Mapping between content id, to address to participant - mapping(uint256 => mapping(address => Participant)) private participants; + mapping(uint256 contentId => mapping(address user => Participant participant)) private participants; /// @dev User address to list of content pool he is in - mapping(address => EnumerableSet.UintSet) private userContentPools; + mapping(address user => EnumerableSet.UintSet contentPoolIds) private userContentPools; /// @custom:oz-upgrades-unsafe-allow constructor constructor() { @@ -158,7 +92,10 @@ contract ContentPool is FrakAccessControlUpgradeable, PushPullReward, FraktionTr /* -------------------------------------------------------------------------- */ /// @dev Add a new `rewardAmount` to the pool for the content `contentId` - function addReward(uint256 contentId, uint256 rewardAmount) + function addReward( + ContentId contentId, + uint256 rewardAmount + ) external payable onlyRole(FrakRoles.REWARDER) @@ -172,7 +109,7 @@ contract ContentPool is FrakAccessControlUpgradeable, PushPullReward, FraktionTr } } // Get the current state - RewardState storage currentState = lastContentState(contentId); + RewardState storage currentState = lastContentState(ContentId.unwrap(contentId)); if (!currentState.open) revert PoolStateClosed(); // Increase the reward @@ -181,11 +118,16 @@ contract ContentPool is FrakAccessControlUpgradeable, PushPullReward, FraktionTr } // Send the event - emit PoolRewardAdded(contentId, rewardAmount); + emit PoolRewardAdded(ContentId.unwrap(contentId), rewardAmount); } /// @dev Callback from the erc1155 tokens when fraktion are transfered - function onFraktionsTransferred(address from, address to, uint256[] memory ids, uint256[] memory amount) + function onFraktionsTransferred( + address from, + address to, + FraktionId[] memory ids, + uint256[] memory amount + ) external payable override @@ -193,7 +135,7 @@ contract ContentPool is FrakAccessControlUpgradeable, PushPullReward, FraktionTr { // Create our update accounter FraktionTransferAccounter memory accounter = - FraktionTransferAccounter({from: from, deltaFrom: 0, to: to, deltaTo: 0}); + FraktionTransferAccounter({ from: from, deltaFrom: 0, to: to, deltaTo: 0 }); if (from != address(0) && to != address(0)) { // Handle share transfer between participant, with no update on the total pool rewards @@ -243,18 +185,24 @@ contract ContentPool is FrakAccessControlUpgradeable, PushPullReward, FraktionTr /* Internal write function's */ /* -------------------------------------------------------------------------- */ - /** - * @dev Update the participants of a pool after fraktion transfer - */ - function updateParticipants(FraktionTransferAccounter memory accounter, uint256 fraktionId, uint256 amountMoved) + /// @dev Update the participants of a pool after fraktion transfer + /// @dev Will only update the participants of a pool after a fraktion transfer + /// @param accounter The accounter to use to update the participants + /// @param fraktionId The fraktion id that was transfered + /// @param amountMoved The amount of fraktion that was transfered + function updateParticipants( + FraktionTransferAccounter memory accounter, + FraktionId fraktionId, + uint256 amountMoved + ) private { unchecked { - // Extract content id and token type from this tx - (uint256 contentId, uint256 tokenType) = FrakMath.extractContentIdAndTokenType(fraktionId); + // Extract content id and fraktion type from this tx + (uint256 contentId, uint256 tokenType) = fraktionId.extractAll(); // Get the initial share value of this token - uint256 sharesValue = getSharesForTokenType(tokenType); + uint256 sharesValue = getSharesForFraktionType(tokenType); if (sharesValue == 0) return; // Jump this iteration if this fraktions doesn't count for any shares // Get the last state index @@ -272,7 +220,8 @@ contract ContentPool is FrakAccessControlUpgradeable, PushPullReward, FraktionTr uint256 delta = _computeAllPendingParticipantStates(contentStates, sender, lastContentIndex); accounter.deltaFrom += delta; - // TODO: Find a way to compute the delta from the receiver, taking in account the edge case where the receiver has multiple content pool + // TODO: Find a way to compute the delta from the receiver, taking in account the edge case where the + // receiver has multiple content pool // TODO: In memory hashmap of the shares movement per content id? Participant storage receiver = contentParticipants[accounter.to]; @@ -285,19 +234,23 @@ contract ContentPool is FrakAccessControlUpgradeable, PushPullReward, FraktionTr } } - /** - * @dev Update participant and pool after fraktion transfer - */ + /// @dev Update participant and pool after fraktion transfer + /// @dev Will update the participants of a pool after a fraktion transfer, and update the pool state + /// @param accounter The accounter to use to update the participants + /// @param fraktionId The fraktion id that was transfered + /// @param amountMoved The amount of fraktion that was transfered function updateParticipantAndPool( FraktionTransferAccounter memory accounter, - uint256 fraktionId, + FraktionId fraktionId, uint256 amountMoved - ) private { + ) + private + { unchecked { - // Extract content id and token type from this tx - (uint256 contentId, uint256 tokenType) = FrakMath.extractContentIdAndTokenType(fraktionId); + // Extract content id and fraktion type from this tx + (uint256 contentId, uint256 tokenType) = fraktionId.extractAll(); // Get the total shares moved - uint256 sharesMoved = getSharesForTokenType(tokenType) * amountMoved; + uint256 sharesMoved = getSharesForFraktionType(tokenType) * amountMoved; if (sharesMoved == 0) return; // Jump this iteration if this fraktions doesn't count for any shares // Get the mapping and array concerned by this content (warm up further access) @@ -371,7 +324,10 @@ contract ContentPool is FrakAccessControlUpgradeable, PushPullReward, FraktionTr RewardState[] storage contentStates, Participant storage participant, uint256 toStateIndex - ) internal returns (uint256 claimable) { + ) + internal + returns (uint256 claimable) + { // Replicate our participant to memory Participant memory memParticipant = participant; @@ -406,7 +362,9 @@ contract ContentPool is FrakAccessControlUpgradeable, PushPullReward, FraktionTr if (i == memParticipant.lastStateIndex && memParticipant.lastStateClaim > 0) { if (userReward > memParticipant.lastStateClaim) { // If the reward is greater than the last claim, we can deduce it - // @warning: Since we are using the memory participant, it doesn't has been updated with the value on top, so we decrease only the last claim if we are in the case of lastIndextoClaim == lastParticipantIndex + // @warning: Since we are using the memory participant, it doesn't has been updated with the value + // on top, so we decrease only the last claim if we are in the case of lastIndextoClaim == + // lastParticipantIndex userReward -= memParticipant.lastStateClaim; } else { // Otherwise, the user has already claimed this state, so we can set the reward to 0 @@ -428,7 +386,12 @@ contract ContentPool is FrakAccessControlUpgradeable, PushPullReward, FraktionTr } /// @dev Increase the share the `user` got in a pool of `contentId`by `amount` - function _increaseParticipantShare(uint256 contentId, Participant storage participant, address user, uint120 amount) + function _increaseParticipantShare( + uint256 contentId, + Participant storage participant, + address user, + uint120 amount + ) private { // Add this pool to the user participating pool if he have 0 shares before @@ -444,7 +407,12 @@ contract ContentPool is FrakAccessControlUpgradeable, PushPullReward, FraktionTr } /// @dev Decrease the share the `user` got in a pool of `contentId`by `amount` - function _decreaseParticipantShare(uint256 contentId, Participant storage participant, address user, uint120 amount) + function _decreaseParticipantShare( + uint256 contentId, + Participant storage participant, + address user, + uint120 amount + ) private { // Decrease his share @@ -523,7 +491,10 @@ contract ContentPool is FrakAccessControlUpgradeable, PushPullReward, FraktionTr /** * @dev Compute the user reward at the given state */ - function computeUserReward(RewardState memory state, Participant memory participant) + function computeUserReward( + RewardState memory state, + Participant memory participant + ) internal pure returns (uint256 stateReward) @@ -532,25 +503,26 @@ contract ContentPool is FrakAccessControlUpgradeable, PushPullReward, FraktionTr uint256 totalShares = state.totalShares; if (totalShares == 0) return 0; - // We can safely do an unchecked operation here since the pool reward, participant shares and total shares are all verified before being stored + // We can safely do an unchecked operation here since the pool reward, participant shares and total shares are + // all verified before being stored unchecked { stateReward = (state.currentPoolReward * participant.shares) / totalShares; } } /** - * @dev Get the base reward to the given token type + * @dev Get the base reward to the given fraktion type * We use a pure function instead of a mapping to economise on storage read, * and since this reawrd shouldn't evolve really fast */ - function getSharesForTokenType(uint256 tokenType) private pure returns (uint256 shares) { - if (tokenType == FrakMath.TOKEN_TYPE_COMMON_MASK) { + function getSharesForFraktionType(uint256 tokenType) private pure returns (uint256 shares) { + if (tokenType == ContentIdLib.FRAKTION_TYPE_COMMON) { shares = 10; - } else if (tokenType == FrakMath.TOKEN_TYPE_PREMIUM_MASK) { + } else if (tokenType == ContentIdLib.FRAKTION_TYPE_PREMIUM) { shares = 50; - } else if (tokenType == FrakMath.TOKEN_TYPE_GOLD_MASK) { + } else if (tokenType == ContentIdLib.FRAKTION_TYPE_GOLD) { shares = 100; - } else if (tokenType == FrakMath.TOKEN_TYPE_DIAMOND_MASK) { + } else if (tokenType == ContentIdLib.FRAKTION_TYPE_DIAMOND) { shares = 200; } } @@ -569,7 +541,10 @@ contract ContentPool is FrakAccessControlUpgradeable, PushPullReward, FraktionTr /** * @dev Get the current participant state for the given content */ - function getParticipantForContent(uint256 contentId, address user) + function getParticipantForContent( + uint256 contentId, + address user + ) external view returns (Participant memory participant) diff --git a/contracts/reward/contentPool/IContentPool.sol b/contracts/reward/contentPool/IContentPool.sol new file mode 100644 index 0000000..8fb9008 --- /dev/null +++ b/contracts/reward/contentPool/IContentPool.sol @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: GNU GPLv3 +pragma solidity 0.8.21; + +import { FraktionTransferCallback } from "../../fraktions/FraktionTransferCallback.sol"; +import { ContentId } from "../../libs/ContentId.sol"; + +/// @author @KONFeature +/// @title IContentPool +/// @notice Interface for the content pool contract +/// @custom:security-contact contact@frak.id +interface IContentPool is FraktionTransferCallback { + /* -------------------------------------------------------------------------- */ + /* Custom error's */ + /* -------------------------------------------------------------------------- */ + + /// @dev The pool state is closed + error PoolStateClosed(); + + /// @dev When the user already claimed this pool state + error PoolStateAlreadyClaimed(); + + /* -------------------------------------------------------------------------- */ + /* Event's */ + /* -------------------------------------------------------------------------- */ + + /// @dev Event emitted when a reward is added to the pool + event PoolRewardAdded(uint256 indexed contentId, uint256 reward); + + /// @dev Event emitted when the pool shares are updated + event PoolSharesUpdated(uint256 indexed contentId, uint256 indexed poolId, uint256 totalShares); + + /// @dev Event emitted when participant share are updated + event ParticipantShareUpdated(address indexed user, uint256 indexed contentId, uint256 shares); + + /* -------------------------------------------------------------------------- */ + /* Struct's */ + /* -------------------------------------------------------------------------- */ + + /** + * @dev Represent a pool reward state + */ + struct RewardState { + // First storage slot, remain 31 bytes (if bool encoded inside a single byte) + uint128 totalShares; // pos : 0x0 <-> 0x10 + uint96 currentPoolReward; // pos : 0x10 + 0x0C -> 0x10 <-> 0x1C + bool open; // pos : 0x01 + 0x1C -> 0x1C <-> 0x1D ? Or less since multiple value can be packed inside a single + // slot ? + } + + /** + * @dev Represent a pool participant + */ + struct Participant { + // First storage slot, remain 40 bytes + uint120 shares; // Number of shares in the content pool, pos : 0x0 <-> 0x0F + uint96 lastStateClaim; // The last state amount claimed, pos : 0x0F + 0x0C -> 0x0F <-> 0x1B + // Second storage slot + uint256 lastStateIndex; // What was the last state index he claimed in the pool ? -> TODO : 0x20 or Ox1B -> + // (0x0F + 0x0C) + } + + /** + * @dev Represent the participant state in a pool + * @dev Only used for view function! Heavy object that shouldn't be stored in storage + */ + struct ParticipantInPoolState { + // Pool info's + uint256 poolId; + uint256 totalShares; // The total shares of the pool at the last state + uint256 poolState; // The index of the current pool state + // Participant info's + uint256 shares; + uint256 lastStateClaimed; // The last state amount claimed for the given pool + uint256 lastStateIndex; // The index of the last state a participant claimed in a pool + uint256 lastStateClaimable; // The claimable amount for the participant in the given pool + } + + /** + * @dev In memory accounter for the balance claimable update post transfer + */ + struct FraktionTransferAccounter { + address from; + uint256 deltaFrom; + address to; + uint256 deltaTo; + } + + /* -------------------------------------------------------------------------- */ + /* External write funtion's */ + /* -------------------------------------------------------------------------- */ + + /// @dev Add a new `rewardAmount` to the pool for the content `contentId` + function addReward(ContentId contentId, uint256 rewardAmount) external payable; + + /// @dev Compute all the pools balance of the user + function computeAllPoolsBalance(address user) external payable; + + /* -------------------------------------------------------------------------- */ + /* External view function's */ + /* -------------------------------------------------------------------------- */ + + /** + * @dev Get the current reward state for the given content + */ + function getAllRewardStates(uint256 contentId) external view returns (RewardState[] memory _rewardStates); + + /** + * @dev Get the current participant state for the given content + */ + function getParticipantForContent( + uint256 contentId, + address user + ) + external + view + returns (Participant memory participant); + + /** + * @dev Get all the user pools with the current state + */ + function getParticipantStates(address user) + external + view + returns (ParticipantInPoolState[] memory participantStateInPool); +} diff --git a/contracts/reward/referralPool/IReferralPool.sol b/contracts/reward/referralPool/IReferralPool.sol new file mode 100644 index 0000000..a20673e --- /dev/null +++ b/contracts/reward/referralPool/IReferralPool.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GNU GPLv3 +pragma solidity 0.8.21; + +/// @author @KONFeature +/// @title IReferralPool +/// @notice Interface for the referral pool contract +/// @custom:security-contact contact@frak.id +interface IReferralPool { + /// @dev Exception throwned when the user already got a referer + error AlreadyGotAReferer(); + /// @dev Exception throwned when the user is already in the referer chain + error AlreadyInRefererChain(); + + /** + * @dev Event emitted when a user is rewarded for his listen + */ + event UserReferred(uint256 indexed contentId, address indexed referer, address indexed referee); + + /** + * @dev Event emitted when a user is rewarded by the referral program + */ + event ReferralReward(uint256 contentId, address user, uint256 amount); + + /** + * @dev Update the listener snft amount + */ + function userReferred(uint256 contentId, address user, address referer) external payable; + + /** + * Pay all the user referer, and return the amount paid + */ + function payAllReferer( + uint256 contentId, + address user, + uint256 amount + ) + external + payable + returns (uint256 totalAmount); +} diff --git a/contracts/reward/pool/ReferralPool.sol b/contracts/reward/referralPool/ReferralPool.sol similarity index 71% rename from contracts/reward/pool/ReferralPool.sol rename to contracts/reward/referralPool/ReferralPool.sol index 5c28c43..d4397f4 100644 --- a/contracts/reward/pool/ReferralPool.sol +++ b/contracts/reward/referralPool/ReferralPool.sol @@ -1,42 +1,25 @@ // SPDX-License-Identifier: GNU GPLv3 pragma solidity 0.8.21; -import {FrakMath} from "../../utils/FrakMath.sol"; -import {FrakRoles} from "../../utils/FrakRoles.sol"; -import {PushPullReward} from "../../utils/PushPullReward.sol"; -import {FrakAccessControlUpgradeable} from "../../utils/FrakAccessControlUpgradeable.sol"; -import {InvalidAddress, NoReward} from "../../utils/FrakErrors.sol"; - -/// @dev Exception throwned when the user already got a referer -error AlreadyGotAReferer(); -/// @dev Exception throwned when the user is already in the referer chain -error AlreadyInRefererChain(); - -/** - * @dev Represent our referral contract - */ +import { FrakRoles } from "../../roles/FrakRoles.sol"; +import { PushPullReward } from "../../utils/PushPullReward.sol"; +import { FrakAccessControlUpgradeable } from "../../roles/FrakAccessControlUpgradeable.sol"; +import { InvalidAddress, NoReward } from "../../utils/FrakErrors.sol"; +import { IReferralPool } from "./IReferralPool.sol"; + +/// @author @KONFeature +/// @title ReferralPool +/// @notice Contract in charge of managing the referral program /// @custom:security-contact contact@frak.id -contract ReferralPool is FrakAccessControlUpgradeable, PushPullReward { - // The minimum reward is 1 mwei, to prevent iteration on really small amount +contract ReferralPool is IReferralPool, FrakAccessControlUpgradeable, PushPullReward { + /// @dev The minimum reward is 1 mwei, to prevent iteration on really small amount uint256 internal constant MINIMUM_REWARD = 1_000_000; - // The maximal referal depth we can pay + /// &dev The maximal referal depth we can pay uint256 internal constant MAX_DEPTH = 10; - /** - * @dev Event emitted when a user is rewarded for his listen - */ - event UserReferred(uint256 indexed contentId, address indexed referer, address indexed referee); - - /** - * @dev Event emitted when a user is rewarded by the referral program - */ - event ReferralReward(uint256 contentId, address user, uint256 amount); - - /** - * Mapping of content id to referee to referer - */ - mapping(uint256 => mapping(address => address)) private contentIdToRefereeToReferer; + /// @dev Mapping of content id, to referee, to referer + mapping(uint256 contentId => mapping(address referee => address referrer)) private contentIdToRefereeToReferer; /// @custom:oz-upgrades-unsafe-allow constructor constructor() { @@ -53,7 +36,11 @@ contract ReferralPool is FrakAccessControlUpgradeable, PushPullReward { /** * @dev Update the listener snft amount */ - function userReferred(uint256 contentId, address user, address referer) + function userReferred( + uint256 contentId, + address user, + address referer + ) external payable onlyRole(FrakRoles.ADMIN) @@ -84,8 +71,12 @@ contract ReferralPool is FrakAccessControlUpgradeable, PushPullReward { /** * Pay all the user referer, and return the amount paid */ - function payAllReferer(uint256 contentId, address user, uint256 amount) - public + function payAllReferer( + uint256 contentId, + address user, + uint256 amount + ) + external payable onlyRole(FrakRoles.REWARDER) whenNotPaused diff --git a/contracts/utils/FrakAccessControlUpgradeable.sol b/contracts/roles/FrakAccessControlUpgradeable.sol similarity index 81% rename from contracts/utils/FrakAccessControlUpgradeable.sol rename to contracts/roles/FrakAccessControlUpgradeable.sol index 0294bdd..cb3b072 100644 --- a/contracts/utils/FrakAccessControlUpgradeable.sol +++ b/contracts/roles/FrakAccessControlUpgradeable.sol @@ -1,12 +1,12 @@ // SPDX-License-Identifier: GNU GPLv3 pragma solidity 0.8.21; -import {Initializable} from "@oz-upgradeable/proxy/utils/Initializable.sol"; -import {UUPSUpgradeable} from "@oz-upgradeable/proxy/utils/UUPSUpgradeable.sol"; -import {ContextUpgradeable} from "@oz-upgradeable/utils/ContextUpgradeable.sol"; -import {IPausable} from "./IPausable.sol"; -import {FrakRoles} from "./FrakRoles.sol"; -import {NotAuthorized, ContractPaused, ContractNotPaused, RenounceForCallerOnly} from "./FrakErrors.sol"; +import { Initializable } from "@oz-upgradeable/proxy/utils/Initializable.sol"; +import { UUPSUpgradeable } from "@oz-upgradeable/proxy/utils/UUPSUpgradeable.sol"; +import { ContextUpgradeable } from "@oz-upgradeable/utils/ContextUpgradeable.sol"; +import { IPausable } from "../utils/IPausable.sol"; +import { FrakRoles } from "./FrakRoles.sol"; +import { NotAuthorized, ContractPaused, ContractNotPaused, RenounceForCallerOnly } from "../utils/FrakErrors.sol"; /** * @author @KONFeature @@ -56,24 +56,28 @@ abstract contract FrakAccessControlUpgradeable is Initializable, ContextUpgradea bool private _paused; /// @dev Mapping of roles -> user -> hasTheRight - mapping(bytes32 => mapping(address => bool)) private _roles; + mapping(bytes32 roles => mapping(address user => bool isAllowed)) private _roles; - /** - * @notice Initializes the contract, granting the ADMIN, PAUSER, and UPGRADER roles to the msg.sender. - * Also, set the contract as unpaused. - */ + /// @dev Initialise the contract and also grant the role to the msg sender function __FrakAccessControlUpgradeable_init() internal onlyInitializing { __Context_init(); __UUPSUpgradeable_init(); - _grantRole(FrakRoles.ADMIN, _msgSender()); - _grantRole(FrakRoles.PAUSER, _msgSender()); - _grantRole(FrakRoles.UPGRADER, _msgSender()); + _grantRole(FrakRoles.ADMIN, msg.sender); + _grantRole(FrakRoles.PAUSER, msg.sender); + _grantRole(FrakRoles.UPGRADER, msg.sender); // Tell we are not paused at start _paused = false; } + /// @dev Initialise the contract and also grant the role to the msg sender + function __FrakAccessControlUpgradeable_Minter_init() internal onlyInitializing { + __FrakAccessControlUpgradeable_init(); + + _grantRole(FrakRoles.MINTER, msg.sender); + } + /* -------------------------------------------------------------------------- */ /* External write function's */ /* -------------------------------------------------------------------------- */ @@ -98,19 +102,19 @@ abstract contract FrakAccessControlUpgradeable is Initializable, ContextUpgradea emit Unpaused(); } - /// @dev Grant the 'role' to the 'account' + /// @dev Grant the `role` to the `account` function grantRole(bytes32 role, address account) external onlyRole(FrakRoles.ADMIN) { _grantRole(role, account); } - /// @dev Revoke the 'role' to the 'account' + /// @dev Revoke the `role` to the `account` function revokeRole(bytes32 role, address account) external onlyRole(FrakRoles.ADMIN) { _revokeRole(role, account); } - /// @dev 'Account' renounce to the 'role' + /// @dev `Account` renounce to the `role` function renounceRole(bytes32 role, address account) external { - if (account != _msgSender()) revert RenounceForCallerOnly(); + if (account != msg.sender) revert RenounceForCallerOnly(); _revokeRole(role, account); } @@ -128,7 +132,7 @@ abstract contract FrakAccessControlUpgradeable is Initializable, ContextUpgradea /* Internal write function's */ /* -------------------------------------------------------------------------- */ - /// @dev Grant the 'role' to the 'account' + /// @dev Grant the `role` to the `account` function _grantRole(bytes32 role, address account) internal { if (!hasRole(role, account)) { _roles[role][account] = true; @@ -136,7 +140,7 @@ abstract contract FrakAccessControlUpgradeable is Initializable, ContextUpgradea } } - /// @dev Revoke the given 'role' to the 'account' + /// @dev Revoke the given `role` to the `account` function _revokeRole(bytes32 role, address account) private { if (hasRole(role, account)) { _roles[role][account] = false; @@ -148,19 +152,14 @@ abstract contract FrakAccessControlUpgradeable is Initializable, ContextUpgradea /* Internal view function's */ /* -------------------------------------------------------------------------- */ - /** - * @dev Returns true if the contract is paused, and false otherwise. - * @return bool representing whether the contract is paused. - */ + /// @dev Check if the contract is paused function paused() private view returns (bool) { return _paused; } - /** - * @notice Check that the calling user have the right role - */ + /// @dev Check that the calling user have the right `role` function _checkRole(bytes32 role) private view { - address sender = _msgSender(); + address sender = msg.sender; assembly { // Kecak (role, _roles.slot) mstore(0, role) @@ -214,5 +213,5 @@ abstract contract FrakAccessControlUpgradeable is Initializable, ContextUpgradea /** * @notice Authorize the upgrade of this contract */ - function _authorizeUpgrade(address newImplementation) internal override onlyRole(FrakRoles.UPGRADER) {} + function _authorizeUpgrade(address newImplementation) internal override onlyRole(FrakRoles.UPGRADER) { } } diff --git a/contracts/utils/FrakRoles.sol b/contracts/roles/FrakRoles.sol similarity index 88% rename from contracts/utils/FrakRoles.sol rename to contracts/roles/FrakRoles.sol index a76f625..98dddbb 100644 --- a/contracts/utils/FrakRoles.sol +++ b/contracts/roles/FrakRoles.sol @@ -1,12 +1,10 @@ // SPDX-License-Identifier: GNU GPLv3 pragma solidity 0.8.21; -/** - * @author @KONFeature - * @title FrakRoles - * @notice Contain all the roles for the Frak ecosystem - * @custom:security-contact contact@frak.id - */ +/// @author @KONFeature +/// @title FrakRoles +/// @notice All the roles of the frak ecosystem +/// @custom:security-contact contact@frak.id library FrakRoles { /// @dev Administrator role of a contra bytes32 internal constant ADMIN = 0x00; diff --git a/contracts/utils/EIP712Base.sol b/contracts/tokens/EIP712Base.sol similarity index 97% rename from contracts/utils/EIP712Base.sol rename to contracts/tokens/EIP712Base.sol index c2a782c..2b7c2dc 100644 --- a/contracts/utils/EIP712Base.sol +++ b/contracts/tokens/EIP712Base.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GNU GPLv3 pragma solidity 0.8.21; -import {Initializable} from "@oz-upgradeable/proxy/utils/Initializable.sol"; +import { Initializable } from "@oz-upgradeable/proxy/utils/Initializable.sol"; contract EIP712Base is Initializable { struct EIP712Domain { diff --git a/contracts/tokens/FrakTokenL2.sol b/contracts/tokens/FrakToken.sol similarity index 68% rename from contracts/tokens/FrakTokenL2.sol rename to contracts/tokens/FrakToken.sol index 271a10e..1d30d00 100644 --- a/contracts/tokens/FrakTokenL2.sol +++ b/contracts/tokens/FrakToken.sol @@ -1,29 +1,23 @@ // SPDX-License-Identifier: GNU GPLv3 pragma solidity 0.8.21; -import {ERC20Upgradeable} from "@oz-upgradeable/token/ERC20/ERC20Upgradeable.sol"; -import {FrakRoles} from "../utils/FrakRoles.sol"; -import {MintingAccessControlUpgradeable} from "../utils/MintingAccessControlUpgradeable.sol"; -import {ContextMixin} from "../utils/ContextMixin.sol"; -import {IFrakToken} from "./IFrakToken.sol"; -import {EIP712Base} from "../utils/EIP712Base.sol"; -import {ECDSA} from "solady/utils/ECDSA.sol"; - -/** - * @author @KONFeature - * @title FrakToken - * @dev ERC20 Contract for the FRAK token - * @notice Compliant with ERC20 - EIP712 - EIP2612 - * @custom:security-contact contact@frak.id - */ -contract FrakToken is ERC20Upgradeable, MintingAccessControlUpgradeable, EIP712Base, ContextMixin, IFrakToken { +import { ERC20Upgradeable } from "@oz-upgradeable/token/ERC20/ERC20Upgradeable.sol"; +import { FrakRoles } from "../roles/FrakRoles.sol"; +import { FrakAccessControlUpgradeable } from "../roles/FrakAccessControlUpgradeable.sol"; +import { IFrakToken } from "./IFrakToken.sol"; +import { EIP712Base } from "./EIP712Base.sol"; +import { ECDSA } from "solady/utils/ECDSA.sol"; + +/// @author @KONFeature +/// @title FrakToken +/// @notice ERC20 Contract for the FRAK token +/// @dev Compliant with ERC20 - EIP712 - EIP2612 +/// @custom:security-contact contact@frak.id +contract FrakToken is ERC20Upgradeable, FrakAccessControlUpgradeable, EIP712Base, IFrakToken { /* -------------------------------------------------------------------------- */ /* Constant's */ /* -------------------------------------------------------------------------- */ - /// @dev Role used by the polygon bridge to bridge token between L1 <-> L2 - bytes32 internal constant DEPOSITOR_ROLE = keccak256("DEPOSITOR_ROLE"); - /// @dev Maximum cap of token, at 3 billion FRK uint256 private constant _cap = 3_000_000_000 ether; @@ -31,15 +25,6 @@ contract FrakToken is ERC20Upgradeable, MintingAccessControlUpgradeable, EIP712B /* Custom error's */ /* -------------------------------------------------------------------------- */ - /// @dev error throwned when the signer is invalid - error InvalidSigner(); - - /// @dev error throwned when the contract cap is exceeded - error CapExceed(); - - /// @dev error throwned when the permit delay is expired - error PermitDelayExpired(); - /// @dev 'bytes4(keccak256(bytes("PermitDelayExpired()")))' uint256 private constant _PERMIT_DELAYED_EXPIRED_SELECTOR = 0x95fc6e60; @@ -55,23 +40,15 @@ contract FrakToken is ERC20Upgradeable, MintingAccessControlUpgradeable, EIP712B /* External write function's */ /* -------------------------------------------------------------------------- */ - function initialize(address childChainManager) external initializer { + function initialize() external initializer { string memory name = "Frak"; __ERC20_init(name, "FRK"); - __MintingAccessControlUpgradeable_init(); + __FrakAccessControlUpgradeable_Minter_init(); _initializeEIP712(name); - _grantRole(DEPOSITOR_ROLE, childChainManager); - // Current version is 2, since we use a version to reset the domain separator post EIP712 updates } - // This is to support Native meta transactions - // never use msg.sender directly, use _msgSender() instead - function _msgSender() internal view override returns (address sender) { - return ContextMixin.msgSender(); - } - /// @dev Mint some FRK function mint(address to, uint256 amount) external override onlyRole(FrakRoles.MINTER) whenNotPaused { if (totalSupply() + amount > _cap) revert CapExceed(); @@ -80,7 +57,7 @@ contract FrakToken is ERC20Upgradeable, MintingAccessControlUpgradeable, EIP712B /// @dev Burn some FRK function burn(uint256 amount) external override whenNotPaused { - _burn(_msgSender(), amount); + _burn(msg.sender, amount); } /// @dev Returns the cap on the token's total supply. @@ -93,7 +70,15 @@ contract FrakToken is ERC20Upgradeable, MintingAccessControlUpgradeable, EIP712B /* -------------------------------------------------------------------------- */ /// @dev EIP 2612, allow the owner to spend the given amount of FRK - function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) + function permit( + address owner, + address spender, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) external payable override diff --git a/contracts/tokens/FrakTokenL1.sol b/contracts/tokens/FrakTokenL1.sol deleted file mode 100644 index ac4120d..0000000 --- a/contracts/tokens/FrakTokenL1.sol +++ /dev/null @@ -1,47 +0,0 @@ -// SPDX-License-Identifier: GNU GPLv3 -pragma solidity 0.8.21; - -import {ERC20Upgradeable} from "@oz-upgradeable/token/ERC20/ERC20Upgradeable.sol"; -import {FrakRoles} from "../utils/FrakRoles.sol"; -import {FrakAccessControlUpgradeable} from "../utils/FrakAccessControlUpgradeable.sol"; -import {ContextMixin} from "../utils/ContextMixin.sol"; -import {EIP712Base} from "../utils/EIP712Base.sol"; - -// Error -/// @dev error throwned when the contract cap is exceeded -error CapExceed(); - -/** - * Frak token on the ethereum mainnet, simpler - */ -/// @custom:security-contact contact@frak.id -contract FrakTokenL1 is ERC20Upgradeable, FrakAccessControlUpgradeable, EIP712Base, ContextMixin { - bytes32 public constant PREDICATE_ROLE = keccak256("PREDICATE_ROLE"); - - uint256 private constant _cap = 3_000_000_000 ether; // 3 billion FRK - - /// @custom:oz-upgrades-unsafe-allow constructor - constructor() { - _disableInitializers(); - } - - function initialize() external initializer { - string memory name = "Frak"; - __ERC20_init(name, "FRK"); - _initializeEIP712(name); - __FrakAccessControlUpgradeable_init(); - - _grantRole(PREDICATE_ROLE, _msgSender()); - } - - function mint(address to, uint256 amount) public onlyRole(PREDICATE_ROLE) whenNotPaused { - if (totalSupply() + amount > _cap) revert CapExceed(); - _mint(to, amount); - } - - // This is to support Native meta transactions - // never use msg.sender directly, use _msgSender() instead - function _msgSender() internal view override returns (address sender) { - return ContextMixin.msgSender(); - } -} diff --git a/contracts/tokens/FraktionTransferCallback.sol b/contracts/tokens/FraktionTransferCallback.sol deleted file mode 100644 index 07cdef9..0000000 --- a/contracts/tokens/FraktionTransferCallback.sol +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-License-Identifier: GNU GPLv3 -pragma solidity 0.8.21; - -/** - * @author @KONFeature - * @title FraktionTransferCallback - * @dev Interface for contract who want to listen of the fraktion transfer (ERC1155 tokens transfer) - * @custom:security-contact contact@frak.id - */ -interface FraktionTransferCallback { - /** - * @dev Function called when a fraktion is transfered between two person - */ - function onFraktionsTransferred(address from, address to, uint256[] memory ids, uint256[] memory amount) - external - payable; -} diff --git a/contracts/tokens/IFrakToken.sol b/contracts/tokens/IFrakToken.sol index 6aba566..5295e55 100644 --- a/contracts/tokens/IFrakToken.sol +++ b/contracts/tokens/IFrakToken.sol @@ -1,26 +1,41 @@ // SPDX-License-Identifier: GNU GPLv3 pragma solidity 0.8.21; -import {IERC20Upgradeable} from "@oz-upgradeable/token/ERC20/IERC20Upgradeable.sol"; - -/** - * @author @KONFeature - * @title IFrakToken - * @dev Interface representing our frk token contract - * @custom:security-contact contact@frak.id - */ +import { IERC20Upgradeable } from "@oz-upgradeable/token/ERC20/IERC20Upgradeable.sol"; + +/// @author @KONFeature +/// @title IFrakToken +/// @notice Interface for the FrakToken +/// @custom:security-contact contact@frak.id interface IFrakToken is IERC20Upgradeable { - /// @dev Mint some FRK + /// @dev error throwned when the signer is invalid + error InvalidSigner(); + + /// @dev error throwned when the contract cap is exceeded + error CapExceed(); + + /// @dev error throwned when the permit delay is expired + error PermitDelayExpired(); + + /// @dev Mint `amount` of FRK to `to` function mint(address to, uint256 amount) external; - /// @dev Burn some FRK + /// @dev Burn `amount` of FRK function burn(uint256 amount) external; /// @dev Returns the cap on the token's total supply. function cap() external view returns (uint256); /// @dev EIP 2612, allow the owner to spend the given amount of FRK - function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) + function permit( + address owner, + address spender, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) external payable; } diff --git a/contracts/utils/ContextMixin.sol b/contracts/utils/ContextMixin.sol deleted file mode 100644 index 9076bce..0000000 --- a/contracts/utils/ContextMixin.sol +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: GNU GPLv3 -pragma solidity 0.8.21; - -abstract contract ContextMixin { - function msgSender() internal view returns (address sender) { - if (msg.sender == address(this)) { - bytes memory array = msg.data; - uint256 index = msg.data.length; - assembly { - // Load the 32 bytes word from memory with the address on the lower 20 bytes, and mask those. - sender := and(mload(add(array, index)), 0xffffffffffffffffffffffffffffffffffffffff) - } - } else { - sender = msg.sender; - } - return sender; - } -} diff --git a/contracts/utils/FrakMath.sol b/contracts/utils/FrakMath.sol deleted file mode 100644 index 880fd07..0000000 --- a/contracts/utils/FrakMath.sol +++ /dev/null @@ -1,183 +0,0 @@ -// SPDX-License-Identifier: GNU GPLv3 -pragma solidity 0.8.21; - -/** - * @author @KONFeature - * @title FrakMath - * @notice Contain some math utils for the Frak ecosystem (token ids, type extractor etc) - * @custom:security-contact contact@frak.id - */ -library FrakMath { - /// @dev The offset of the id we use to store the token type - uint8 internal constant ID_OFFSET = 4; - /// @dev The mask we use to store the token type in the token id - uint8 internal constant TYPE_MASK = 0xF; - - /// @dev NFT Token type mask - uint8 internal constant TOKEN_TYPE_NFT_MASK = 1; - /// @dev Free Token type mask - uint8 internal constant TOKEN_TYPE_FREE_MASK = 2; - /// @dev Common Token type mask - uint8 internal constant TOKEN_TYPE_COMMON_MASK = 3; - /// @dev Premium Token type mask - uint8 internal constant TOKEN_TYPE_PREMIUM_MASK = 4; - /// @dev Gold Token type mask - uint8 internal constant TOKEN_TYPE_GOLD_MASK = 5; - /// @dev Diamond Token type mask - uint8 internal constant TOKEN_TYPE_DIAMOND_MASK = 6; - /// @dev If a token type is <= to this value it's not a payed one - uint8 internal constant PAYED_TOKEN_TYPE_MAX = 7; - - /** - * @dev Build the id for a S FNT - */ - function buildSnftIds(uint256 id, uint256[] memory types) internal pure returns (uint256[] memory tokenIds) { - assembly { - // Create our array from free mem space - tokenIds := mload(0x40) - mstore(tokenIds, mload(types)) - // Current iteration offset - let offset := 0x20 - // End of our iteration - let end := add(0x20, shl(5, mload(types))) - // Build each nft id's - for {} 1 {} { - // Store the token id - mstore(add(tokenIds, offset), or(shl(0x04, id), mload(add(types, offset)))) - - // Increase our offset's - offset := add(offset, 0x20) - - // Exit if we reached the end - if iszero(lt(offset, end)) { break } - } - - // Update our free mem space - mstore(0x40, add(tokenIds, offset)) - } - } - - /** - * @dev Build the id for a NFT - */ - function buildNftId(uint256 id) internal pure returns (uint256) { - return (id << ID_OFFSET) | TOKEN_TYPE_NFT_MASK; - } - - /** - * @dev Build the id for a classic NFT id - */ - function buildFreeNftId(uint256 id) internal pure returns (uint256) { - return (id << ID_OFFSET) | TOKEN_TYPE_FREE_MASK; - } - - /** - * @dev Build the id for a classic NFT id - */ - function buildCommonNftId(uint256 id) internal pure returns (uint256) { - return (id << ID_OFFSET) | TOKEN_TYPE_COMMON_MASK; - } - - /** - * @dev Build the id for a rare NFT id - */ - function buildPremiumNftId(uint256 id) internal pure returns (uint256) { - return (id << ID_OFFSET) | TOKEN_TYPE_PREMIUM_MASK; - } - - /** - * @dev Build the id for a epic NFT id - */ - function buildGoldNftId(uint256 id) internal pure returns (uint256) { - return (id << ID_OFFSET) | TOKEN_TYPE_GOLD_MASK; - } - - /** - * @dev Build the id for a epic NFT id - */ - function buildDiamondNftId(uint256 id) internal pure returns (uint256) { - return (id << ID_OFFSET) | TOKEN_TYPE_DIAMOND_MASK; - } - - /** - * @dev Build a list of all the payable token types - */ - function payableTokenTypes() internal pure returns (uint256[] memory types) { - assembly { - // Store each types - types := mload(0x40) - mstore(types, 4) - mstore(add(types, 0x20), 3) - mstore(add(types, 0x40), 4) - mstore(add(types, 0x60), 5) - mstore(add(types, 0x80), 6) - // Update our free mem space - mstore(0x40, add(types, 0xA0)) - } - } - - /** - * @dev Return the id of a content without the token type mask - * @param id uint256 ID of the token tto exclude the mask of - * @return contentId uint256 The id without the type mask - */ - function extractContentId(uint256 id) internal pure returns (uint256 contentId) { - assembly { - contentId := shr(ID_OFFSET, id) - } - } - - /** - * @dev Return the token type - * @param id uint256 ID of the token to extract the mask from - * @return tokenType uint256 The token type - */ - function extractTokenType(uint256 id) internal pure returns (uint256 tokenType) { - assembly { - tokenType := and(id, TYPE_MASK) - } - } - - /** - * @dev Return the token type - * @param id uint256 ID of the token to extract the mask from - * @return contentId uint256 The content id - * @return tokenType uint256 The token type - */ - function extractContentIdAndTokenType(uint256 id) internal pure returns (uint256 contentId, uint256 tokenType) { - assembly { - contentId := shr(ID_OFFSET, id) - tokenType := and(id, TYPE_MASK) - } - } - - /** - * @dev Create a singleton array of the given element - */ - function asSingletonArray(uint256 element) internal pure returns (uint256[] memory array) { - assembly { - // Get free memory space for our array, and update the free mem space index - array := mload(0x40) - mstore(0x40, add(array, 0x40)) - - // Store our array (1st = length, 2nd = element) - mstore(array, 0x01) - mstore(add(array, 0x20), element) - } - } - - /** - * @dev Create a singleton array of the given element - */ - function asSingletonArray(address element) internal pure returns (address[] memory array) { - assembly { - // Get free memory space for our array, and update the free mem space index - array := mload(0x40) - mstore(0x40, add(array, 0x40)) - - // Store our array (1st = length, 2nd = element) - mstore(array, 0x01) - mstore(add(array, 0x20), element) - } - } -} diff --git a/contracts/utils/IPausable.sol b/contracts/utils/IPausable.sol index 32d7cbf..3b83406 100644 --- a/contracts/utils/IPausable.sol +++ b/contracts/utils/IPausable.sol @@ -1,17 +1,14 @@ // SPDX-License-Identifier: GNU GPLv3 pragma solidity 0.8.21; -/** - * @dev Represent a pausable contract - */ +/// @author @KONFeature +/// @title IPausable +/// @notice Interface for a pausable contract +/// @custom:security-contact contact@frak.id interface IPausable { - /** - * @dev Pause the contract - */ + /// @dev Pause the contract function pause() external; - /** - * @dev Resume the contract - */ + /// @dev Unpause the contract function unpause() external; } diff --git a/contracts/utils/IPushPullReward.sol b/contracts/utils/IPushPullReward.sol new file mode 100644 index 0000000..a10b793 --- /dev/null +++ b/contracts/utils/IPushPullReward.sol @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GNU GPLv3 +pragma solidity 0.8.21; + +import { Initializable } from "@oz-upgradeable/proxy/utils/Initializable.sol"; +import { IERC20Upgradeable } from "@oz-upgradeable/token/ERC20/IERC20Upgradeable.sol"; +import { NoReward, InvalidAddress, RewardTooLarge } from "./FrakErrors.sol"; + +/// @author @KONFeature +/// @title IPushPullReward +/// @notice Interface for the push pull reward contracts +/// @custom:security-contact contact@frak.id +interface IPushPullReward { + /* -------------------------------------------------------------------------- */ + /* Event's */ + /* -------------------------------------------------------------------------- */ + + /// @dev Event emitted when a reward is added + event RewardAdded(address indexed user, uint256 amount); + + /// @dev Event emitted when a user withdraw his pending reward + event RewardWithdrawed(address indexed user, uint256 amount, uint256 fees); + + /* -------------------------------------------------------------------------- */ + /* External virtual function's */ + /* -------------------------------------------------------------------------- */ + + /** + * @dev For a user to directly claim their founds + */ + function withdrawFounds() external; + + /** + * @dev For an admin to withdraw the founds of the given user + */ + function withdrawFounds(address user) external; + + /* -------------------------------------------------------------------------- */ + /* External view function's */ + /* -------------------------------------------------------------------------- */ + + /** + * @notice Get the available founds for the given user + */ + function getAvailableFounds(address user) external view returns (uint256); +} diff --git a/contracts/utils/MintingAccessControlUpgradeable.sol b/contracts/utils/MintingAccessControlUpgradeable.sol deleted file mode 100644 index 1cf78d6..0000000 --- a/contracts/utils/MintingAccessControlUpgradeable.sol +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: GNU GPLv3 -pragma solidity 0.8.21; - -import {FrakAccessControlUpgradeable} from "./FrakAccessControlUpgradeable.sol"; -import {FrakRoles} from "../utils/FrakRoles.sol"; - -/// @custom:security-contact contact@frak.id -abstract contract MintingAccessControlUpgradeable is FrakAccessControlUpgradeable { - function __MintingAccessControlUpgradeable_init() internal onlyInitializing { - __FrakAccessControlUpgradeable_init(); - - _grantRole(FrakRoles.MINTER, _msgSender()); - } -} diff --git a/contracts/utils/PushPullReward.sol b/contracts/utils/PushPullReward.sol index 9d2064a..27a308f 100644 --- a/contracts/utils/PushPullReward.sol +++ b/contracts/utils/PushPullReward.sol @@ -1,15 +1,16 @@ // SPDX-License-Identifier: GNU GPLv3 pragma solidity 0.8.21; -import {Initializable} from "@oz-upgradeable/proxy/utils/Initializable.sol"; -import {IERC20Upgradeable} from "@oz-upgradeable/token/ERC20/IERC20Upgradeable.sol"; -import {NoReward, InvalidAddress, RewardTooLarge} from "./FrakErrors.sol"; - -/** - * @dev Abstraction for contract that give a push / pull reward, address based - */ +import { Initializable } from "@oz-upgradeable/proxy/utils/Initializable.sol"; +import { IERC20Upgradeable } from "@oz-upgradeable/token/ERC20/IERC20Upgradeable.sol"; +import { NoReward, InvalidAddress, RewardTooLarge } from "./FrakErrors.sol"; +import { IPushPullReward } from "./IPushPullReward.sol"; + +/// @author @KONFeature +/// @title PushPullReward +/// @notice Abstract contract for managing the reward of a token /// @custom:security-contact contact@frak.id -abstract contract PushPullReward is Initializable { +abstract contract PushPullReward is IPushPullReward, Initializable { /* -------------------------------------------------------------------------- */ /* Custom error's */ /* -------------------------------------------------------------------------- */ @@ -27,12 +28,6 @@ abstract contract PushPullReward is Initializable { /* Event's */ /* -------------------------------------------------------------------------- */ - /// @dev Event emitted when a reward is added - event RewardAdded(address indexed user, uint256 amount); - - /// @dev Event emitted when a user withdraw his pending reward - event RewardWithdrawed(address indexed user, uint256 amount, uint256 fees); - /// @dev 'keccak256(bytes("RewardAdded(address,uint256)"))' uint256 private constant _REWARD_ADDED_EVENT_SELECTOR = 0xac24935fd910bc682b5ccb1a07b718cadf8cf2f6d1404c4f3ddc3662dae40e29; @@ -107,7 +102,8 @@ abstract contract PushPullReward is Initializable { } /** - * @dev Add founds for the given user, without checking the operation (gas gain, usefull when founds are checked before) + * @dev Add founds for the given user, without checking the operation (gas gain, usefull when founds are checked + * before) */ function _addFoundsUnchecked(address user, uint256 founds) internal { assembly { @@ -185,7 +181,8 @@ abstract contract PushPullReward is Initializable { /** * @dev Core logic of the withdraw method, but with fee this time - * @notice If that's the fee recipient performing the call, withdraw without fee's (otherwise, infinite loop required to get all the frk foundation fee's) + * @notice If that's the fee recipient performing the call, withdraw without fee's (otherwise, infinite loop + * required to get all the frk foundation fee's) */ function _withdrawWithFee(address user, uint256 feePercent, address feeRecipient) internal { uint256 feesAmount; diff --git a/contracts/wallets/FrakTreasuryWallet.sol b/contracts/wallets/FrakTreasuryWallet.sol index cf71f63..fcf38c9 100644 --- a/contracts/wallets/FrakTreasuryWallet.sol +++ b/contracts/wallets/FrakTreasuryWallet.sol @@ -1,16 +1,20 @@ // SPDX-License-Identifier: GNU GPLv3 pragma solidity 0.8.21; -import {IFrakToken} from "../tokens/IFrakToken.sol"; -import {MintingAccessControlUpgradeable} from "../utils/MintingAccessControlUpgradeable.sol"; -import {FrakRoles} from "../utils/FrakRoles.sol"; -import {InvalidAddress, RewardTooLarge, NoReward} from "../utils/FrakErrors.sol"; -import {Multicallable} from "solady/utils/Multicallable.sol"; +import { IFrakToken } from "../tokens/IFrakToken.sol"; +import { FrakAccessControlUpgradeable } from "../roles/FrakAccessControlUpgradeable.sol"; +import { FrakRoles } from "../roles/FrakRoles.sol"; +import { InvalidAddress, RewardTooLarge, NoReward } from "../utils/FrakErrors.sol"; +import { Multicallable } from "solady/utils/Multicallable.sol"; /// Error thrown when the contract havn't enough found to perform the withdraw error NotEnoughTreasury(); -contract FrakTreasuryWallet is MintingAccessControlUpgradeable, Multicallable { +/// @author @KONFeature +/// @title FrakTreasuryWallet +/// @notice This contract is used to store some frk token for the treasury +/// @custom:security-contact contact@frak.id +contract FrakTreasuryWallet is FrakAccessControlUpgradeable, Multicallable { /* -------------------------------------------------------------------------- */ /* Constant's */ /* -------------------------------------------------------------------------- */ @@ -72,14 +76,12 @@ contract FrakTreasuryWallet is MintingAccessControlUpgradeable, Multicallable { function initialize(address frkTokenAddr) external initializer { if (frkTokenAddr == address(0)) revert InvalidAddress(); - __MintingAccessControlUpgradeable_init(); + __FrakAccessControlUpgradeable_Minter_init(); frakToken = IFrakToken(frkTokenAddr); } - /** - * @dev Transfer the given number of token to the user - */ + /// @dev Transfer `amount` of token to `target` function transfer(address target, uint256 amount) external whenNotPaused onlyRole(FrakRoles.MINTER) { assembly { // Ensure the param are valid and not too much @@ -111,10 +113,11 @@ contract FrakTreasuryWallet is MintingAccessControlUpgradeable, Multicallable { frakToken.transfer(target, amount); } - /** - * @dev Transfer the given number of token to the user - */ - function transferBatch(address[] calldata targets, uint256[] calldata amounts) + /// @dev Transfer all `amounts` to each `targets` + function transferBatch( + address[] calldata targets, + uint256[] calldata amounts + ) external whenNotPaused onlyRole(FrakRoles.MINTER) @@ -175,9 +178,7 @@ contract FrakTreasuryWallet is MintingAccessControlUpgradeable, Multicallable { } } - /** - * @dev Mint some fresh token to this contract, and return the number of token minted - */ + /// @dev Mint some fresh token to this contract, and return the number of token minted function _mintNewToken() private returns (uint256 amountToMint) { if (totalFrakMinted + FRK_MINTING_AMOUNT < FRK_MINTING_CAP) { // In the case we have enough room, mint 1m token directly diff --git a/contracts/wallets/MultiVestingWallets.sol b/contracts/wallets/MultiVestingWallets.sol index 2469115..0fb4844 100644 --- a/contracts/wallets/MultiVestingWallets.sol +++ b/contracts/wallets/MultiVestingWallets.sol @@ -1,12 +1,12 @@ // SPDX-License-Identifier: GNU GPLv3 pragma solidity 0.8.21; -import {EnumerableSet} from "openzeppelin/utils/structs/EnumerableSet.sol"; -import {SafeERC20Upgradeable} from "@oz-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; -import {IERC20Upgradeable} from "@oz-upgradeable/token/ERC20/IERC20Upgradeable.sol"; -import {FrakAccessControlUpgradeable} from "../utils/FrakAccessControlUpgradeable.sol"; -import {FrakRoles} from "../utils/FrakRoles.sol"; -import {NotAuthorized, InvalidArray, InvalidAddress, NoReward, RewardTooLarge} from "../utils/FrakErrors.sol"; +import { EnumerableSet } from "openzeppelin/utils/structs/EnumerableSet.sol"; +import { SafeERC20Upgradeable } from "@oz-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; +import { IERC20Upgradeable } from "@oz-upgradeable/token/ERC20/IERC20Upgradeable.sol"; +import { FrakAccessControlUpgradeable } from "../roles/FrakAccessControlUpgradeable.sol"; +import { FrakRoles } from "../roles/FrakRoles.sol"; +import { NotAuthorized, InvalidArray, InvalidAddress, NoReward, RewardTooLarge } from "../utils/FrakErrors.sol"; /// @dev error emitted when the contract doesn't have enough founds error NotEnoughFounds(); @@ -21,6 +21,10 @@ error InexistantVesting(); /// @dev error when we encounter a computation error error ComputationError(); +/// @author @KONFeature +/// @title MultiVestingWallets +/// @notice This contract is used to store vesting for the frk token +/// @custom:security-contact contact@frak.id contract MultiVestingWallets is FrakAccessControlUpgradeable { using SafeERC20Upgradeable for IERC20Upgradeable; @@ -69,10 +73,10 @@ contract MultiVestingWallets is FrakAccessControlUpgradeable { uint24 private _idCounter; /// Vesting id to vesting - mapping(uint256 => Vesting) public vestings; + mapping(uint256 id => Vesting vesting) public vestings; /// User to list of vesting id owned - mapping(address => EnumerableSet.UintSet) private owned; + mapping(address investor => EnumerableSet.UintSet vestingIds) private owned; /// @custom:oz-upgrades-unsafe-allow constructor constructor() { @@ -136,7 +140,12 @@ contract MultiVestingWallets is FrakAccessControlUpgradeable { /** * @notice Create a new vesting. */ - function createVest(address beneficiary, uint256 amount, uint32 duration, uint48 startDate) + function createVest( + address beneficiary, + uint256 amount, + uint32 duration, + uint48 startDate + ) external whenNotPaused onlyRole(FrakRoles.VESTING_MANAGER) @@ -154,7 +163,11 @@ contract MultiVestingWallets is FrakAccessControlUpgradeable { uint256[] calldata amounts, uint32 duration, uint48 startDate - ) external whenNotPaused onlyRole(FrakRoles.VESTING_MANAGER) { + ) + external + whenNotPaused + onlyRole(FrakRoles.VESTING_MANAGER) + { if (beneficiaries.length == 0 || beneficiaries.length != amounts.length) { revert InvalidArray(); } @@ -422,7 +435,10 @@ contract MultiVestingWallets is FrakAccessControlUpgradeable { * @param beneficiary Address to get it from. * @return vesting struct stored in the storage. */ - function _getVestingForBeneficiary(uint24 vestingId, address beneficiary) + function _getVestingForBeneficiary( + uint24 vestingId, + address beneficiary + ) internal view returns (Vesting storage vesting) diff --git a/contracts/wallets/VestingWalletFactory.sol b/contracts/wallets/VestingWalletFactory.sol index e052140..d269151 100644 --- a/contracts/wallets/VestingWalletFactory.sol +++ b/contracts/wallets/VestingWalletFactory.sol @@ -1,11 +1,11 @@ // SPDX-License-Identifier: GNU GPLv3 pragma solidity 0.8.21; -import {IERC20Upgradeable} from "@oz-upgradeable/token/ERC20/IERC20Upgradeable.sol"; -import {MultiVestingWallets} from "./MultiVestingWallets.sol"; -import {FrakAccessControlUpgradeable} from "../utils/FrakAccessControlUpgradeable.sol"; -import {FrakRoles} from "../utils/FrakRoles.sol"; -import {InvalidArray, InvalidAddress, NoReward, RewardTooLarge} from "../utils/FrakErrors.sol"; +import { IERC20Upgradeable } from "@oz-upgradeable/token/ERC20/IERC20Upgradeable.sol"; +import { MultiVestingWallets } from "./MultiVestingWallets.sol"; +import { FrakAccessControlUpgradeable } from "../roles/FrakAccessControlUpgradeable.sol"; +import { FrakRoles } from "../roles/FrakRoles.sol"; +import { InvalidArray, InvalidAddress, NoReward, RewardTooLarge } from "../utils/FrakErrors.sol"; /// @dev error throwned when the creation param are invalid error InvalidCreationParam(); @@ -15,6 +15,10 @@ error InexistantGroup(); /// @dev error when the supply of the group is not sufficiant error InsuficiantGroupSupply(); +/// @author @KONFeature +/// @title VestingWalletFactory +/// @notice This contract is used to create vesting wallet for the investor's +/// @custom:security-contact contact@frak.id contract VestingWalletFactory is FrakAccessControlUpgradeable { // The cap of frk token propose to vester uint96 internal constant FRK_VESTING_CAP = 1_500_000_000 ether; @@ -40,7 +44,7 @@ contract VestingWalletFactory is FrakAccessControlUpgradeable { /** * @dev Map of id to vesting group */ - mapping(uint8 => VestingGroup) private vestingGroup; + mapping(uint8 id => VestingGroup group) private vestingGroup; /// @custom:oz-upgrades-unsafe-allow constructor constructor() { @@ -83,7 +87,7 @@ contract VestingWalletFactory is FrakAccessControlUpgradeable { // Increase the total group supply totalGroupCap += rewardCap; // Build and save this group - vestingGroup[id] = VestingGroup({rewardCap: rewardCap, supply: 0, duration: duration}); + vestingGroup[id] = VestingGroup({ rewardCap: rewardCap, supply: 0, duration: duration }); // Emit the event emit GroupAdded(id, rewardCap, duration); } @@ -91,7 +95,11 @@ contract VestingWalletFactory is FrakAccessControlUpgradeable { /** * @notice Transfer a group reserve to another one */ - function transferGroupReserve(uint8 initialId, uint8 targetId, uint96 amount) + function transferGroupReserve( + uint8 initialId, + uint8 targetId, + uint96 amount + ) external whenNotPaused onlyRole(FrakRoles.ADMIN) @@ -128,7 +136,12 @@ contract VestingWalletFactory is FrakAccessControlUpgradeable { /** * @dev Create a new vesting wallet */ - function addVestingWallet(address beneficiary, uint256 reward, uint8 groupId, uint48 startDate) + function addVestingWallet( + address beneficiary, + uint256 reward, + uint8 groupId, + uint48 startDate + ) external onlyRole(FrakRoles.VESTING_CREATOR) whenNotPaused @@ -158,7 +171,11 @@ contract VestingWalletFactory is FrakAccessControlUpgradeable { uint256[] calldata rewards, uint8 groupId, uint48 startDate - ) external onlyRole(FrakRoles.VESTING_CREATOR) whenNotPaused { + ) + external + onlyRole(FrakRoles.VESTING_CREATOR) + whenNotPaused + { // Ensure all the param are correct if (beneficiaries.length == 0 || beneficiaries.length != rewards.length) { revert InvalidArray(); diff --git a/foundry.toml b/foundry.toml index a1e7861..655c595 100644 --- a/foundry.toml +++ b/foundry.toml @@ -28,13 +28,15 @@ yul = true # Fuzzing config [fuzz] -runs = 2048 -max_test_rejects = 65536 -seed = '0xacab' -dictionary_weight = 40 +runs = 1000 include_storage = true include_push_bytes = true +# CI config +[profile.ci] + fuzz = { runs = 10_000, seed = '0xacab' } + verbosity = 4 + # Invariant config [invariant] runs = 256 @@ -45,15 +47,25 @@ dictionary_weight = 80 include_storage = true include_push_bytes = true -# Config for deployment -[rpc_endpoints] -goerli = "${GOERLI_RPC_URL}" -mumbai = "${MUMBAI_RPC_URL}" -polygon = "${POLYGON_RPC_URL}" - -# Config for verification [etherscan] -mumbai = { key = "${POLYGON_SCAN_API_KEY}", url = "https://api-testnet.polygonscan.com/api" } -polygon = { key = "${POLYGON_SCAN_API_KEY}", url = "https://api.polygonscan.com/api" } -goerli = { key = "${ETHERSCAN_SCAN_API_KEY}", url = "https://api-goerli.etherscan.io/api" } -mainnet = { key = "${ETHERSCAN_SCAN_API_KEY}" } + mainnet = { key = "${API_KEY_ETHERSCAN}" } + goerli = { key = "${API_KEY_ETHERSCAN}", url = "https://api-goerli.etherscan.io/api" } + polygon = { key = "${API_KEY_POLYGONSCAN}", url = "https://api.polygonscan.com/api" } + mumbai = { key = "${API_KEY_POLYGONSCAN}", url = "https://api-testnet.polygonscan.com/api" } + +[fmt] + bracket_spacing = true + int_types = "long" + line_length = 120 + multiline_func_header = "all" + number_underscore = "thousands" + quote_style = "double" + tab_width = 4 + wrap_comments = true + +[rpc_endpoints] + localhost = "http://localhost:8545" + mainnet = "https://eth-mainnet.g.alchemy.com/v2/${API_KEY_ALCHEMY_MAINNET}" + goerli = "https://eth-goerli.g.alchemy.com/v2/${API_KEY_ALCHEMY_GOERLI}" + polygon = "https://polygon-mainnet.g.alchemy.com/v2/${API_KEY_ALCHEMY_POLYGON}" + mumbai = "https://polygon-mumbai.g.alchemy.com/v2/${API_KEY_ALCHEMY_MUMBAI}" diff --git a/lib/solady b/lib/solady index efd6317..51cf845 160000 --- a/lib/solady +++ b/lib/solady @@ -1 +1 @@ -Subproject commit efd63173997c6e30a2e45cd889cdd3968598a4c2 +Subproject commit 51cf845a9df1e9a04898a435d187af3efe99b644 diff --git a/package.json b/package.json index cd18654..cab3aab 100644 --- a/package.json +++ b/package.json @@ -17,15 +17,15 @@ "test": "pnpm test:forge && pnpm test:storage", "test:forge": "forge test", "test:storage": "npx @openzeppelin/upgrades-core validate out/build-info", - "coverage": "forge coverage", + "coverage": "forge coverage --report lcov", "generate": "wagmi generate", "generate:react": "wagmi generate -c ./wagmi-react.config.ts" }, "devDependencies": { "@openzeppelin/upgrades-core": "^1.28.0", - "@wagmi/cli": "^1.3.0", + "@wagmi/cli": "^1.4.1", "ts-node": "^10.9.1", - "typescript": "^5.1.6" + "typescript": "^5.2.2" }, "private": true, "engines": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 142b082..51d814d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,14 +9,14 @@ devDependencies: specifier: ^1.28.0 version: 1.28.0 '@wagmi/cli': - specifier: ^1.3.0 - version: 1.3.0(typescript@5.1.6) + specifier: ^1.4.1 + version: 1.4.1(typescript@5.2.2) ts-node: specifier: ^10.9.1 - version: 10.9.1(@types/node@20.5.0)(typescript@5.1.6) + version: 10.9.1(@types/node@20.5.7)(typescript@5.2.2) typescript: - specifier: ^5.1.6 - version: 5.1.6 + specifier: ^5.2.2 + version: 5.2.2 packages: @@ -31,8 +31,17 @@ packages: '@jridgewell/trace-mapping': 0.3.9 dev: true - /@esbuild/android-arm@0.15.13: - resolution: {integrity: sha512-RY2fVI8O0iFUNvZirXaQ1vMvK0xhCcl0gqRj74Z6yEiO1zAUa7hbsdwZM1kzqbxHK7LFyMizipfXT3JME+12Hw==} + /@esbuild/android-arm64@0.16.17: + resolution: {integrity: sha512-MIGl6p5sc3RDTLLkYL1MyL8BMRN4tLMRCn+yRJJmEDvYZ2M7tmAf80hx1kbNEUX2KJ50RRtxZ4JHLvCfuB6kBg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-arm@0.16.17: + resolution: {integrity: sha512-N9x1CMXVhtWEAMS7pNNONyA14f71VPQN9Cnavj1XQh6T7bskqiLLrSca4O0Vr8Wdcga943eThxnVp3JLnBMYtw==} engines: {node: '>=12'} cpu: [arm] os: [android] @@ -40,8 +49,80 @@ packages: dev: true optional: true - /@esbuild/linux-loong64@0.15.13: - resolution: {integrity: sha512-+BoyIm4I8uJmH/QDIH0fu7MG0AEx9OXEDXnqptXCwKOlOqZiS4iraH1Nr7/ObLMokW3sOCeBNyD68ATcV9b9Ag==} + /@esbuild/android-x64@0.16.17: + resolution: {integrity: sha512-a3kTv3m0Ghh4z1DaFEuEDfz3OLONKuFvI4Xqczqx4BqLyuFaFkuaG4j2MtA6fuWEFeC5x9IvqnX7drmRq/fyAQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-arm64@0.16.17: + resolution: {integrity: sha512-/2agbUEfmxWHi9ARTX6OQ/KgXnOWfsNlTeLcoV7HSuSTv63E4DqtAc+2XqGw1KHxKMHGZgbVCZge7HXWX9Vn+w==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-x64@0.16.17: + resolution: {integrity: sha512-2By45OBHulkd9Svy5IOCZt376Aa2oOkiE9QWUK9fe6Tb+WDr8hXL3dpqi+DeLiMed8tVXspzsTAvd0jUl96wmg==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-arm64@0.16.17: + resolution: {integrity: sha512-mt+cxZe1tVx489VTb4mBAOo2aKSnJ33L9fr25JXpqQqzbUIw/yzIzi+NHwAXK2qYV1lEFp4OoVeThGjUbmWmdw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-x64@0.16.17: + resolution: {integrity: sha512-8ScTdNJl5idAKjH8zGAsN7RuWcyHG3BAvMNpKOBaqqR7EbUhhVHOqXRdL7oZvz8WNHL2pr5+eIT5c65kA6NHug==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm64@0.16.17: + resolution: {integrity: sha512-7S8gJnSlqKGVJunnMCrXHU9Q8Q/tQIxk/xL8BqAP64wchPCTzuM6W3Ra8cIa1HIflAvDnNOt2jaL17vaW+1V0g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm@0.16.17: + resolution: {integrity: sha512-iihzrWbD4gIT7j3caMzKb/RsFFHCwqqbrbH9SqUSRrdXkXaygSZCZg1FybsZz57Ju7N/SHEgPyaR0LZ8Zbe9gQ==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ia32@0.16.17: + resolution: {integrity: sha512-kiX69+wcPAdgl3Lonh1VI7MBr16nktEvOfViszBSxygRQqSpzv7BffMKRPMFwzeJGPxcio0pdD3kYQGpqQ2SSg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-loong64@0.16.17: + resolution: {integrity: sha512-dTzNnQwembNDhd654cA4QhbS9uDdXC3TKqMJjgOWsC0yNCbpzfWoXdZvp0mY7HU6nzk5E0zpRGGx3qoQg8T2DQ==} engines: {node: '>=12'} cpu: [loong64] os: [linux] @@ -49,6 +130,105 @@ packages: dev: true optional: true + /@esbuild/linux-mips64el@0.16.17: + resolution: {integrity: sha512-ezbDkp2nDl0PfIUn0CsQ30kxfcLTlcx4Foz2kYv8qdC6ia2oX5Q3E/8m6lq84Dj/6b0FrkgD582fJMIfHhJfSw==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ppc64@0.16.17: + resolution: {integrity: sha512-dzS678gYD1lJsW73zrFhDApLVdM3cUF2MvAa1D8K8KtcSKdLBPP4zZSLy6LFZ0jYqQdQ29bjAHJDgz0rVbLB3g==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-riscv64@0.16.17: + resolution: {integrity: sha512-ylNlVsxuFjZK8DQtNUwiMskh6nT0vI7kYl/4fZgV1llP5d6+HIeL/vmmm3jpuoo8+NuXjQVZxmKuhDApK0/cKw==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-s390x@0.16.17: + resolution: {integrity: sha512-gzy7nUTO4UA4oZ2wAMXPNBGTzZFP7mss3aKR2hH+/4UUkCOyqmjXiKpzGrY2TlEUhbbejzXVKKGazYcQTZWA/w==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-x64@0.16.17: + resolution: {integrity: sha512-mdPjPxfnmoqhgpiEArqi4egmBAMYvaObgn4poorpUaqmvzzbvqbowRllQ+ZgzGVMGKaPkqUmPDOOFQRUFDmeUw==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/netbsd-x64@0.16.17: + resolution: {integrity: sha512-/PzmzD/zyAeTUsduZa32bn0ORug+Jd1EGGAUJvqfeixoEISYpGnAezN6lnJoskauoai0Jrs+XSyvDhppCPoKOA==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/openbsd-x64@0.16.17: + resolution: {integrity: sha512-2yaWJhvxGEz2RiftSk0UObqJa/b+rIAjnODJgv2GbGGpRwAfpgzyrg1WLK8rqA24mfZa9GvpjLcBBg8JHkoodg==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/sunos-x64@0.16.17: + resolution: {integrity: sha512-xtVUiev38tN0R3g8VhRfN7Zl42YCJvyBhRKw1RJjwE1d2emWTVToPLNEQj/5Qxc6lVFATDiy6LjVHYhIPrLxzw==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-arm64@0.16.17: + resolution: {integrity: sha512-ga8+JqBDHY4b6fQAmOgtJJue36scANy4l/rL97W+0wYmijhxKetzZdKOJI7olaBaMhWt8Pac2McJdZLxXWUEQw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-ia32@0.16.17: + resolution: {integrity: sha512-WnsKaf46uSSF/sZhwnqE4L/F89AYNMiD4YtEcYekBt9Q7nj0DiId2XH2Ng2PHM54qi5oPrQ8luuzGszqi/veig==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-x64@0.16.17: + resolution: {integrity: sha512-y+EHuSchhL7FjHgvQL/0fnnFmO4T1bhvWANX6gcnqTjtnKWbTvUMCpGnv2+t+31d7RzyEAYAd4u2fnIhHL6N/Q==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@jridgewell/resolve-uri@3.1.1: resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} engines: {node: '>=6.0.0'} @@ -118,13 +298,13 @@ packages: ethereumjs-util: 7.1.5 minimist: 1.2.8 proper-lockfile: 4.1.2 - solidity-ast: 0.4.49 + solidity-ast: 0.4.52 transitivePeerDependencies: - supports-color dev: true - /@scure/base@1.1.1: - resolution: {integrity: sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==} + /@scure/base@1.1.3: + resolution: {integrity: sha512-/+SgoRjLq7Xlf0CWuLHq2LUZeL/w65kfzAPG5NH9pcmBhs+nunQTn4gvdwgMTIXnt9b2C/1SeL2XiysZEyIC9Q==} dev: true /@scure/bip32@1.3.0: @@ -132,14 +312,14 @@ packages: dependencies: '@noble/curves': 1.0.0 '@noble/hashes': 1.3.0 - '@scure/base': 1.1.1 + '@scure/base': 1.1.3 dev: true /@scure/bip39@1.2.0: resolution: {integrity: sha512-SX/uKq52cuxm4YFXWFaVByaSHJh2w3BnokVSeUJVCv6K7WulT9u2BuNRBhuFl8vAuYnzx9bEu9WgpcNYTrYieg==} dependencies: '@noble/hashes': 1.3.0 - '@scure/base': 1.1.1 + '@scure/base': 1.1.3 dev: true /@tsconfig/node10@1.0.9: @@ -161,55 +341,44 @@ packages: /@types/bn.js@5.1.1: resolution: {integrity: sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==} dependencies: - '@types/node': 20.5.0 + '@types/node': 20.5.7 dev: true - /@types/node@20.5.0: - resolution: {integrity: sha512-Mgq7eCtoTjT89FqNoTzzXg2XvCi5VMhRV6+I2aYanc6kQCBImeNaAYRs/DyoVqk1YEUJK5gN9VO7HRIdz4Wo3Q==} + /@types/node@20.5.7: + resolution: {integrity: sha512-dP7f3LdZIysZnmvP3ANJYTSwg+wLLl8p7RqniVlV7j+oXSXAbt9h0WIBFmJy5inWZoX9wZN6eXx+YXd9Rh3RBA==} dev: true /@types/pbkdf2@3.1.0: resolution: {integrity: sha512-Cf63Rv7jCQ0LaL8tNXmEyqTHuIJxRdlS5vMh1mj5voN4+QFhVZnlZruezqpWYDiJ8UTzhP0VmeLXCmBk66YrMQ==} dependencies: - '@types/node': 20.5.0 + '@types/node': 20.5.7 dev: true /@types/secp256k1@4.0.3: resolution: {integrity: sha512-Da66lEIFeIz9ltsdMZcpQvmrmmoqrfju8pm1BH8WbYjZSwUgCwXLb9C+9XYogwBITnbsSaMdVPb2ekf7TV+03w==} dependencies: - '@types/node': 20.5.0 + '@types/node': 20.5.7 dev: true /@types/ws@8.5.5: resolution: {integrity: sha512-lwhs8hktwxSjf9UaZ9tG5M03PGogvFaH8gUgLNbN9HKIg0dvv6q+gkSuJ8HN4/VbyxkuLzCjlN7GquQ0gUJfIg==} dependencies: - '@types/node': 20.5.0 - dev: true - - /@wagmi/chains@1.3.0(typescript@5.1.6): - resolution: {integrity: sha512-7tyr1irTZQpA4/4HoIiJP3XYZuJIZuWiZ1V1j5WEG3cjm8TXIlMEzO0N+hT/cZKw4/UtF2EukvB8GkDWa2S77w==} - peerDependencies: - typescript: '>=5.0.4' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - typescript: 5.1.6 + '@types/node': 20.5.7 dev: true - /@wagmi/chains@1.6.0(typescript@5.1.6): - resolution: {integrity: sha512-5FRlVxse5P4ZaHG3GTvxwVANSmYJas1eQrTBHhjxVtqXoorm0aLmCHbhmN8Xo1yu09PaWKlleEvfE98yH4AgIw==} + /@wagmi/chains@1.8.0(typescript@5.2.2): + resolution: {integrity: sha512-UXo0GF0Cl0+neKC2KAmVAahv8L/5rACbFRRqkDvHMefzY6Fh7yzJd8F4GaGNNG3w4hj8eUB/E3+dEpaTYDN62w==} peerDependencies: typescript: '>=5.0.4' peerDependenciesMeta: typescript: optional: true dependencies: - typescript: 5.1.6 + typescript: 5.2.2 dev: true - /@wagmi/cli@1.3.0(typescript@5.1.6): - resolution: {integrity: sha512-/YXmdp0XWgQEwRSVO8IjVB8KY5HK+6+eqJsZI3a+y3XMH4T/NxVBoT/JxTqV6HEivr3HOLgDcTzvQhNy3mZ0HA==} + /@wagmi/cli@1.4.1(typescript@5.2.2): + resolution: {integrity: sha512-Et9ni95ewmN+SCJaPEX16681hvALlXKda5QjDzDgX9tPyy560GOvUZY+mYgU1EEOiSO1ONt2We20C7YhtYwLRQ==} engines: {node: '>=14'} hasBin: true peerDependencies: @@ -224,10 +393,10 @@ packages: wagmi: optional: true dependencies: - '@wagmi/chains': 1.3.0(typescript@5.1.6) - abitype: 0.8.7(typescript@5.1.6)(zod@3.22.1) + '@wagmi/chains': 1.8.0(typescript@5.2.2) + abitype: 0.8.7(typescript@5.2.2)(zod@3.22.2) abort-controller: 3.0.0 - bundle-require: 3.1.2(esbuild@0.15.13) + bundle-require: 3.1.2(esbuild@0.16.17) cac: 6.7.14 change-case: 4.1.2 chokidar: 3.5.3 @@ -235,7 +404,7 @@ packages: detect-package-manager: 2.0.1 dotenv: 16.3.1 dotenv-expand: 10.0.0 - esbuild: 0.15.13 + esbuild: 0.16.17 execa: 6.1.0 find-up: 6.3.0 fs-extra: 10.1.0 @@ -245,15 +414,15 @@ packages: pathe: 1.1.1 picocolors: 1.0.0 prettier: 2.8.8 - typescript: 5.1.6 - viem: 1.6.0(typescript@5.1.6)(zod@3.22.1) - zod: 3.22.1 + typescript: 5.2.2 + viem: 1.9.3(typescript@5.2.2)(zod@3.22.2) + zod: 3.22.2 transitivePeerDependencies: - bufferutil - utf-8-validate dev: true - /abitype@0.8.7(typescript@5.1.6)(zod@3.22.1): + /abitype@0.8.7(typescript@5.2.2)(zod@3.22.2): resolution: {integrity: sha512-wQ7hV8Yg/yKmGyFpqrNZufCxbszDe5es4AZGYPBitocfSqXtjrTG9JMWFcc4N30ukl2ve48aBTwt7NJxVQdU3w==} peerDependencies: typescript: '>=5.0.4' @@ -262,11 +431,11 @@ packages: zod: optional: true dependencies: - typescript: 5.1.6 - zod: 3.22.1 + typescript: 5.2.2 + zod: 3.22.2 dev: true - /abitype@0.9.3(typescript@5.1.6)(zod@3.22.1): + /abitype@0.9.3(typescript@5.2.2)(zod@3.22.2): resolution: {integrity: sha512-dz4qCQLurx97FQhnb/EIYTk/ldQ+oafEDUqC0VVIeQS1Q48/YWt/9YNfMmp9SLFqN41ktxny3c8aYxHjmFIB/w==} peerDependencies: typescript: '>=5.0.4' @@ -277,8 +446,8 @@ packages: zod: optional: true dependencies: - typescript: 5.1.6 - zod: 3.22.1 + typescript: 5.2.2 + zod: 3.22.2 dev: true /abort-controller@3.0.0: @@ -323,6 +492,41 @@ packages: resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} dev: true + /array-buffer-byte-length@1.0.0: + resolution: {integrity: sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==} + dependencies: + call-bind: 1.0.2 + is-array-buffer: 3.0.2 + dev: true + + /array.prototype.findlast@1.2.3: + resolution: {integrity: sha512-kcBubumjciBg4JKp5KTKtI7ec7tRefPk88yjkWJwaVKYd9QfTaxcsOxoMNKd7iBr447zCfDV0z1kOF47umv42g==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.22.1 + es-shim-unscopables: 1.0.0 + get-intrinsic: 1.2.1 + dev: true + + /arraybuffer.prototype.slice@1.0.1: + resolution: {integrity: sha512-09x0ZWFEjj4WD8PDbykUwo3t9arLn8NIzmmYEJFpYekOAQjpkGSyrQhNoRTcwwcFRu+ycWF78QZ63oWTqSjBcw==} + engines: {node: '>= 0.4'} + dependencies: + array-buffer-byte-length: 1.0.0 + call-bind: 1.0.2 + define-properties: 1.2.0 + get-intrinsic: 1.2.1 + is-array-buffer: 3.0.2 + is-shared-array-buffer: 1.0.2 + dev: true + + /available-typed-arrays@1.0.5: + resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==} + engines: {node: '>= 0.4'} + dev: true + /base-x@3.0.9: resolution: {integrity: sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==} dependencies: @@ -405,13 +609,13 @@ packages: ieee754: 1.2.1 dev: true - /bundle-require@3.1.2(esbuild@0.15.13): + /bundle-require@3.1.2(esbuild@0.16.17): resolution: {integrity: sha512-Of6l6JBAxiyQ5axFxUM6dYeP/W7X2Sozeo/4EYB9sJhL+dqL7TKjg+shwxp6jlu/6ZSERfsYtIpSJ1/x3XkAEA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} peerDependencies: esbuild: '>=0.13' dependencies: - esbuild: 0.15.13 + esbuild: 0.16.17 load-tsconfig: 0.2.5 dev: true @@ -420,18 +624,25 @@ packages: engines: {node: '>=8'} dev: true + /call-bind@1.0.2: + resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} + dependencies: + function-bind: 1.1.1 + get-intrinsic: 1.2.1 + dev: true + /camel-case@4.1.2: resolution: {integrity: sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==} dependencies: pascal-case: 3.1.2 - tslib: 2.6.1 + tslib: 2.6.2 dev: true /capital-case@1.0.4: resolution: {integrity: sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==} dependencies: no-case: 3.0.4 - tslib: 2.6.1 + tslib: 2.6.2 upper-case-first: 2.0.2 dev: true @@ -469,7 +680,7 @@ packages: path-case: 3.0.4 sentence-case: 3.0.4 snake-case: 3.0.4 - tslib: 2.6.1 + tslib: 2.6.2 dev: true /chokidar@3.5.3: @@ -484,7 +695,7 @@ packages: normalize-path: 3.0.0 readdirp: 3.6.0 optionalDependencies: - fsevents: 2.3.2 + fsevents: 2.3.3 dev: true /cipher-base@1.0.4: @@ -530,7 +741,7 @@ packages: resolution: {integrity: sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ==} dependencies: no-case: 3.0.4 - tslib: 2.6.1 + tslib: 2.6.2 upper-case: 2.0.2 dev: true @@ -595,6 +806,14 @@ packages: clone: 1.0.4 dev: true + /define-properties@1.2.0: + resolution: {integrity: sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==} + engines: {node: '>= 0.4'} + dependencies: + has-property-descriptors: 1.0.0 + object-keys: 1.1.1 + dev: true + /detect-package-manager@2.0.1: resolution: {integrity: sha512-j/lJHyoLlWi6G1LDdLgvUtz60Zo5GEj+sVYtTVXnYLDPuzgC3llMxonXym9zIwhhUII8vjdw0LXxavpLqTbl1A==} engines: {node: '>=12'} @@ -618,7 +837,7 @@ packages: resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} dependencies: no-case: 3.0.4 - tslib: 2.6.1 + tslib: 2.6.2 dev: true /dotenv-expand@10.0.0: @@ -643,214 +862,103 @@ packages: minimalistic-crypto-utils: 1.0.1 dev: true - /esbuild-android-64@0.15.13: - resolution: {integrity: sha512-yRorukXBlokwTip+Sy4MYskLhJsO0Kn0/Fj43s1krVblfwP+hMD37a4Wmg139GEsMLl+vh8WXp2mq/cTA9J97g==} - engines: {node: '>=12'} - cpu: [x64] - os: [android] - requiresBuild: true - dev: true - optional: true - - /esbuild-android-arm64@0.15.13: - resolution: {integrity: sha512-TKzyymLD6PiVeyYa4c5wdPw87BeAiTXNtK6amWUcXZxkV51gOk5u5qzmDaYSwiWeecSNHamFsaFjLoi32QR5/w==} - engines: {node: '>=12'} - cpu: [arm64] - os: [android] - requiresBuild: true - dev: true - optional: true - - /esbuild-darwin-64@0.15.13: - resolution: {integrity: sha512-WAx7c2DaOS6CrRcoYCgXgkXDliLnFv3pQLV6GeW1YcGEZq2Gnl8s9Pg7ahValZkpOa0iE/ojRVQ87sbUhF1Cbg==} - engines: {node: '>=12'} - cpu: [x64] - os: [darwin] - requiresBuild: true - dev: true - optional: true - - /esbuild-darwin-arm64@0.15.13: - resolution: {integrity: sha512-U6jFsPfSSxC3V1CLiQqwvDuj3GGrtQNB3P3nNC3+q99EKf94UGpsG9l4CQ83zBs1NHrk1rtCSYT0+KfK5LsD8A==} - engines: {node: '>=12'} - cpu: [arm64] - os: [darwin] - requiresBuild: true - dev: true - optional: true - - /esbuild-freebsd-64@0.15.13: - resolution: {integrity: sha512-whItJgDiOXaDG/idy75qqevIpZjnReZkMGCgQaBWZuKHoElDJC1rh7MpoUgupMcdfOd+PgdEwNQW9DAE6i8wyA==} - engines: {node: '>=12'} - cpu: [x64] - os: [freebsd] - requiresBuild: true - dev: true - optional: true - - /esbuild-freebsd-arm64@0.15.13: - resolution: {integrity: sha512-6pCSWt8mLUbPtygv7cufV0sZLeylaMwS5Fznj6Rsx9G2AJJsAjQ9ifA+0rQEIg7DwJmi9it+WjzNTEAzzdoM3Q==} - engines: {node: '>=12'} - cpu: [arm64] - os: [freebsd] - requiresBuild: true - dev: true - optional: true - - /esbuild-linux-32@0.15.13: - resolution: {integrity: sha512-VbZdWOEdrJiYApm2kkxoTOgsoCO1krBZ3quHdYk3g3ivWaMwNIVPIfEE0f0XQQ0u5pJtBsnk2/7OPiCFIPOe/w==} - engines: {node: '>=12'} - cpu: [ia32] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /esbuild-linux-64@0.15.13: - resolution: {integrity: sha512-rXmnArVNio6yANSqDQlIO4WiP+Cv7+9EuAHNnag7rByAqFVuRusLbGi2697A5dFPNXoO//IiogVwi3AdcfPC6A==} - engines: {node: '>=12'} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /esbuild-linux-arm64@0.15.13: - resolution: {integrity: sha512-alEMGU4Z+d17U7KQQw2IV8tQycO6T+rOrgW8OS22Ua25x6kHxoG6Ngry6Aq6uranC+pNWNMB6aHFPh7aTQdORQ==} - engines: {node: '>=12'} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /esbuild-linux-arm@0.15.13: - resolution: {integrity: sha512-Ac6LpfmJO8WhCMQmO253xX2IU2B3wPDbl4IvR0hnqcPrdfCaUa2j/lLMGTjmQ4W5JsJIdHEdW12dG8lFS0MbxQ==} - engines: {node: '>=12'} - cpu: [arm] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /esbuild-linux-mips64le@0.15.13: - resolution: {integrity: sha512-47PgmyYEu+yN5rD/MbwS6DxP2FSGPo4Uxg5LwIdxTiyGC2XKwHhHyW7YYEDlSuXLQXEdTO7mYe8zQ74czP7W8A==} - engines: {node: '>=12'} - cpu: [mips64el] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /esbuild-linux-ppc64le@0.15.13: - resolution: {integrity: sha512-z6n28h2+PC1Ayle9DjKoBRcx/4cxHoOa2e689e2aDJSaKug3jXcQw7mM+GLg+9ydYoNzj8QxNL8ihOv/OnezhA==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /esbuild-linux-riscv64@0.15.13: - resolution: {integrity: sha512-+Lu4zuuXuQhgLUGyZloWCqTslcCAjMZH1k3Xc9MSEJEpEFdpsSU0sRDXAnk18FKOfEjhu4YMGaykx9xjtpA6ow==} - engines: {node: '>=12'} - cpu: [riscv64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /esbuild-linux-s390x@0.15.13: - resolution: {integrity: sha512-BMeXRljruf7J0TMxD5CIXS65y7puiZkAh+s4XFV9qy16SxOuMhxhVIXYLnbdfLrsYGFzx7U9mcdpFWkkvy/Uag==} - engines: {node: '>=12'} - cpu: [s390x] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /esbuild-netbsd-64@0.15.13: - resolution: {integrity: sha512-EHj9QZOTel581JPj7UO3xYbltFTYnHy+SIqJVq6yd3KkCrsHRbapiPb0Lx3EOOtybBEE9EyqbmfW1NlSDsSzvQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [netbsd] - requiresBuild: true - dev: true - optional: true - - /esbuild-openbsd-64@0.15.13: - resolution: {integrity: sha512-nkuDlIjF/sfUhfx8SKq0+U+Fgx5K9JcPq1mUodnxI0x4kBdCv46rOGWbuJ6eof2n3wdoCLccOoJAbg9ba/bT2w==} - engines: {node: '>=12'} - cpu: [x64] - os: [openbsd] - requiresBuild: true - dev: true - optional: true - - /esbuild-sunos-64@0.15.13: - resolution: {integrity: sha512-jVeu2GfxZQ++6lRdY43CS0Tm/r4WuQQ0Pdsrxbw+aOrHQPHV0+LNOLnvbN28M7BSUGnJnHkHm2HozGgNGyeIRw==} - engines: {node: '>=12'} - cpu: [x64] - os: [sunos] - requiresBuild: true - dev: true - optional: true - - /esbuild-windows-32@0.15.13: - resolution: {integrity: sha512-XoF2iBf0wnqo16SDq+aDGi/+QbaLFpkiRarPVssMh9KYbFNCqPLlGAWwDvxEVz+ywX6Si37J2AKm+AXq1kC0JA==} - engines: {node: '>=12'} - cpu: [ia32] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /esbuild-windows-64@0.15.13: - resolution: {integrity: sha512-Et6htEfGycjDrtqb2ng6nT+baesZPYQIW+HUEHK4D1ncggNrDNk3yoboYQ5KtiVrw/JaDMNttz8rrPubV/fvPQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /esbuild-windows-arm64@0.15.13: - resolution: {integrity: sha512-3bv7tqntThQC9SWLRouMDmZnlOukBhOCTlkzNqzGCmrkCJI7io5LLjwJBOVY6kOUlIvdxbooNZwjtBvj+7uuVg==} - engines: {node: '>=12'} - cpu: [arm64] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /esbuild@0.15.13: - resolution: {integrity: sha512-Cu3SC84oyzzhrK/YyN4iEVy2jZu5t2fz66HEOShHURcjSkOSAVL8C/gfUT+lDJxkVHpg8GZ10DD0rMHRPqMFaQ==} + /es-abstract@1.22.1: + resolution: {integrity: sha512-ioRRcXMO6OFyRpyzV3kE1IIBd4WG5/kltnzdxSCqoP8CMGs/Li+M1uF5o7lOkZVFjDs+NLesthnF66Pg/0q0Lw==} + engines: {node: '>= 0.4'} + dependencies: + array-buffer-byte-length: 1.0.0 + arraybuffer.prototype.slice: 1.0.1 + available-typed-arrays: 1.0.5 + call-bind: 1.0.2 + es-set-tostringtag: 2.0.1 + es-to-primitive: 1.2.1 + function.prototype.name: 1.1.6 + get-intrinsic: 1.2.1 + get-symbol-description: 1.0.0 + globalthis: 1.0.3 + gopd: 1.0.1 + has: 1.0.3 + has-property-descriptors: 1.0.0 + has-proto: 1.0.1 + has-symbols: 1.0.3 + internal-slot: 1.0.5 + is-array-buffer: 3.0.2 + is-callable: 1.2.7 + is-negative-zero: 2.0.2 + is-regex: 1.1.4 + is-shared-array-buffer: 1.0.2 + is-string: 1.0.7 + is-typed-array: 1.1.12 + is-weakref: 1.0.2 + object-inspect: 1.12.3 + object-keys: 1.1.1 + object.assign: 4.1.4 + regexp.prototype.flags: 1.5.0 + safe-array-concat: 1.0.0 + safe-regex-test: 1.0.0 + string.prototype.trim: 1.2.7 + string.prototype.trimend: 1.0.6 + string.prototype.trimstart: 1.0.6 + typed-array-buffer: 1.0.0 + typed-array-byte-length: 1.0.0 + typed-array-byte-offset: 1.0.0 + typed-array-length: 1.0.4 + unbox-primitive: 1.0.2 + which-typed-array: 1.1.11 + dev: true + + /es-set-tostringtag@2.0.1: + resolution: {integrity: sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.2.1 + has: 1.0.3 + has-tostringtag: 1.0.0 + dev: true + + /es-shim-unscopables@1.0.0: + resolution: {integrity: sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==} + dependencies: + has: 1.0.3 + dev: true + + /es-to-primitive@1.2.1: + resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} + engines: {node: '>= 0.4'} + dependencies: + is-callable: 1.2.7 + is-date-object: 1.0.5 + is-symbol: 1.0.4 + dev: true + + /esbuild@0.16.17: + resolution: {integrity: sha512-G8LEkV0XzDMNwXKgM0Jwu3nY3lSTwSGY6XbxM9cr9+s0T/qSV1q1JVPBGzm3dcjhCic9+emZDmMffkwgPeOeLg==} engines: {node: '>=12'} hasBin: true requiresBuild: true optionalDependencies: - '@esbuild/android-arm': 0.15.13 - '@esbuild/linux-loong64': 0.15.13 - esbuild-android-64: 0.15.13 - esbuild-android-arm64: 0.15.13 - esbuild-darwin-64: 0.15.13 - esbuild-darwin-arm64: 0.15.13 - esbuild-freebsd-64: 0.15.13 - esbuild-freebsd-arm64: 0.15.13 - esbuild-linux-32: 0.15.13 - esbuild-linux-64: 0.15.13 - esbuild-linux-arm: 0.15.13 - esbuild-linux-arm64: 0.15.13 - esbuild-linux-mips64le: 0.15.13 - esbuild-linux-ppc64le: 0.15.13 - esbuild-linux-riscv64: 0.15.13 - esbuild-linux-s390x: 0.15.13 - esbuild-netbsd-64: 0.15.13 - esbuild-openbsd-64: 0.15.13 - esbuild-sunos-64: 0.15.13 - esbuild-windows-32: 0.15.13 - esbuild-windows-64: 0.15.13 - esbuild-windows-arm64: 0.15.13 + '@esbuild/android-arm': 0.16.17 + '@esbuild/android-arm64': 0.16.17 + '@esbuild/android-x64': 0.16.17 + '@esbuild/darwin-arm64': 0.16.17 + '@esbuild/darwin-x64': 0.16.17 + '@esbuild/freebsd-arm64': 0.16.17 + '@esbuild/freebsd-x64': 0.16.17 + '@esbuild/linux-arm': 0.16.17 + '@esbuild/linux-arm64': 0.16.17 + '@esbuild/linux-ia32': 0.16.17 + '@esbuild/linux-loong64': 0.16.17 + '@esbuild/linux-mips64el': 0.16.17 + '@esbuild/linux-ppc64': 0.16.17 + '@esbuild/linux-riscv64': 0.16.17 + '@esbuild/linux-s390x': 0.16.17 + '@esbuild/linux-x64': 0.16.17 + '@esbuild/netbsd-x64': 0.16.17 + '@esbuild/openbsd-x64': 0.16.17 + '@esbuild/sunos-x64': 0.16.17 + '@esbuild/win32-arm64': 0.16.17 + '@esbuild/win32-ia32': 0.16.17 + '@esbuild/win32-x64': 0.16.17 dev: true /ethereum-cryptography@0.1.3: @@ -966,6 +1074,12 @@ packages: path-exists: 5.0.0 dev: true + /for-each@0.3.3: + resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} + dependencies: + is-callable: 1.2.7 + dev: true + /formdata-polyfill@4.0.10: resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} engines: {node: '>=12.20.0'} @@ -982,19 +1096,54 @@ packages: universalify: 2.0.0 dev: true - /fsevents@2.3.2: - resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + /fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] requiresBuild: true dev: true optional: true + /function-bind@1.1.1: + resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} + dev: true + + /function.prototype.name@1.1.6: + resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.22.1 + functions-have-names: 1.2.3 + dev: true + + /functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + dev: true + + /get-intrinsic@1.2.1: + resolution: {integrity: sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==} + dependencies: + function-bind: 1.1.1 + has: 1.0.3 + has-proto: 1.0.1 + has-symbols: 1.0.3 + dev: true + /get-stream@6.0.1: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} engines: {node: '>=10'} dev: true + /get-symbol-description@1.0.0: + resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.2.1 + dev: true + /glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} @@ -1002,6 +1151,13 @@ packages: is-glob: 4.0.3 dev: true + /globalthis@1.0.3: + resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==} + engines: {node: '>= 0.4'} + dependencies: + define-properties: 1.2.0 + dev: true + /globby@13.2.2: resolution: {integrity: sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -1013,15 +1169,55 @@ packages: slash: 4.0.0 dev: true + /gopd@1.0.1: + resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + dependencies: + get-intrinsic: 1.2.1 + dev: true + /graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} dev: true + /has-bigints@1.0.2: + resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} + dev: true + /has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} dev: true + /has-property-descriptors@1.0.0: + resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==} + dependencies: + get-intrinsic: 1.2.1 + dev: true + + /has-proto@1.0.1: + resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==} + engines: {node: '>= 0.4'} + dev: true + + /has-symbols@1.0.3: + resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + engines: {node: '>= 0.4'} + dev: true + + /has-tostringtag@1.0.0: + resolution: {integrity: sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==} + engines: {node: '>= 0.4'} + dependencies: + has-symbols: 1.0.3 + dev: true + + /has@1.0.3: + resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} + engines: {node: '>= 0.4.0'} + dependencies: + function-bind: 1.1.1 + dev: true + /hash-base@3.1.0: resolution: {integrity: sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==} engines: {node: '>=4'} @@ -1042,7 +1238,7 @@ packages: resolution: {integrity: sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q==} dependencies: capital-case: 1.0.4 - tslib: 2.6.1 + tslib: 2.6.2 dev: true /hmac-drbg@1.0.1: @@ -1076,6 +1272,29 @@ packages: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} dev: true + /internal-slot@1.0.5: + resolution: {integrity: sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.2.1 + has: 1.0.3 + side-channel: 1.0.4 + dev: true + + /is-array-buffer@3.0.2: + resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.2.1 + is-typed-array: 1.1.12 + dev: true + + /is-bigint@1.0.4: + resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} + dependencies: + has-bigints: 1.0.2 + dev: true + /is-binary-path@2.1.0: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} engines: {node: '>=8'} @@ -1083,6 +1302,26 @@ packages: binary-extensions: 2.2.0 dev: true + /is-boolean-object@1.1.2: + resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + has-tostringtag: 1.0.0 + dev: true + + /is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + dev: true + + /is-date-object@1.0.5: + resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + /is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -1100,11 +1339,37 @@ packages: engines: {node: '>=12'} dev: true + /is-negative-zero@2.0.2: + resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==} + 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.0 + dev: true + /is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} dev: true + /is-regex@1.1.4: + resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + has-tostringtag: 1.0.0 + dev: true + + /is-shared-array-buffer@1.0.2: + resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==} + dependencies: + call-bind: 1.0.2 + dev: true + /is-stream@2.0.1: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} @@ -1115,11 +1380,42 @@ packages: engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dev: true + /is-string@1.0.7: + resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + + /is-symbol@1.0.4: + resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} + engines: {node: '>= 0.4'} + dependencies: + has-symbols: 1.0.3 + dev: true + + /is-typed-array@1.1.12: + resolution: {integrity: sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==} + engines: {node: '>= 0.4'} + dependencies: + which-typed-array: 1.1.11 + dev: true + /is-unicode-supported@1.3.0: resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==} engines: {node: '>=12'} dev: true + /is-weakref@1.0.2: + resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} + dependencies: + call-bind: 1.0.2 + dev: true + + /isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + dev: true + /isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} dev: true @@ -1146,7 +1442,7 @@ packages: requiresBuild: true dependencies: node-addon-api: 2.0.2 - node-gyp-build: 4.6.0 + node-gyp-build: 4.6.1 readable-stream: 3.6.2 dev: true @@ -1173,7 +1469,7 @@ packages: /lower-case@2.0.2: resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} dependencies: - tslib: 2.6.1 + tslib: 2.6.2 dev: true /make-error@1.3.6: @@ -1235,7 +1531,7 @@ packages: resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} dependencies: lower-case: 2.0.2 - tslib: 2.6.1 + tslib: 2.6.2 dev: true /node-addon-api@2.0.2: @@ -1256,8 +1552,8 @@ packages: formdata-polyfill: 4.0.10 dev: true - /node-gyp-build@4.6.0: - resolution: {integrity: sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==} + /node-gyp-build@4.6.1: + resolution: {integrity: sha512-24vnklJmyRS8ViBNI8KbtK/r/DmXQMRiOMXTNz2nrTnAYUwjmEEbnnpB/+kt+yWRv73bPsSPRFddrcIbAxSiMQ==} hasBin: true dev: true @@ -1285,6 +1581,25 @@ packages: path-key: 4.0.0 dev: true + /object-inspect@1.12.3: + resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==} + dev: true + + /object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + dev: true + + /object.assign@4.1.4: + resolution: {integrity: sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + has-symbols: 1.0.3 + object-keys: 1.1.1 + dev: true + /onetime@5.1.2: resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} engines: {node: '>=6'} @@ -1332,21 +1647,21 @@ packages: resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==} dependencies: dot-case: 3.0.4 - tslib: 2.6.1 + tslib: 2.6.2 dev: true /pascal-case@3.1.2: resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==} dependencies: no-case: 3.0.4 - tslib: 2.6.1 + tslib: 2.6.2 dev: true /path-case@3.0.4: resolution: {integrity: sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg==} dependencies: dot-case: 3.0.4 - tslib: 2.6.1 + tslib: 2.6.2 dev: true /path-exists@5.0.0: @@ -1433,6 +1748,15 @@ packages: picomatch: 2.3.1 dev: true + /regexp.prototype.flags@1.5.0: + resolution: {integrity: sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + functions-have-names: 1.2.3 + dev: true + /restore-cursor@4.0.0: resolution: {integrity: sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -1471,10 +1795,28 @@ packages: queue-microtask: 1.2.3 dev: true + /safe-array-concat@1.0.0: + resolution: {integrity: sha512-9dVEFruWIsnie89yym+xWTAYASdpw3CJV7Li/6zBewGf9z2i1j31rP6jnY0pHEO4QZh6N0K11bFjWmdR8UGdPQ==} + engines: {node: '>=0.4'} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.2.1 + has-symbols: 1.0.3 + isarray: 2.0.5 + dev: true + /safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} dev: true + /safe-regex-test@1.0.0: + resolution: {integrity: sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.2.1 + is-regex: 1.1.4 + dev: true + /scrypt-js@3.0.1: resolution: {integrity: sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==} dev: true @@ -1486,14 +1828,14 @@ packages: dependencies: elliptic: 6.5.4 node-addon-api: 2.0.2 - node-gyp-build: 4.6.0 + node-gyp-build: 4.6.1 dev: true /sentence-case@3.0.4: resolution: {integrity: sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg==} dependencies: no-case: 3.0.4 - tslib: 2.6.1 + tslib: 2.6.2 upper-case-first: 2.0.2 dev: true @@ -1521,6 +1863,14 @@ packages: engines: {node: '>=8'} dev: true + /side-channel@1.0.4: + resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.2.1 + object-inspect: 1.12.3 + dev: true + /signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} dev: true @@ -1534,11 +1884,13 @@ packages: resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} dependencies: dot-case: 3.0.4 - tslib: 2.6.1 + tslib: 2.6.2 dev: true - /solidity-ast@0.4.49: - resolution: {integrity: sha512-Pr5sCAj1SFqzwFZw1HPKSq0PehlQNdM8GwKyAVYh2DOn7/cCK8LUKD1HeHnKtTgBW7hi9h4nnnan7hpAg5RhWQ==} + /solidity-ast@0.4.52: + resolution: {integrity: sha512-iOya9BSiB9jhM8Vf40n8lGELGzwrUc57rl5BhfNtJ5cvAaMvRcNlHeAMNvqJJyjoUnczqRbHqdivEqK89du3Cw==} + dependencies: + array.prototype.findlast: 1.2.3 dev: true /stdin-discarder@0.1.0: @@ -1548,6 +1900,31 @@ packages: bl: 5.1.0 dev: true + /string.prototype.trim@1.2.7: + resolution: {integrity: sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.22.1 + dev: true + + /string.prototype.trimend@1.0.6: + resolution: {integrity: sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.22.1 + dev: true + + /string.prototype.trimstart@1.0.6: + resolution: {integrity: sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.22.1 + dev: true + /string_decoder@1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} dependencies: @@ -1585,7 +1962,7 @@ packages: is-number: 7.0.0 dev: true - /ts-node@10.9.1(@types/node@20.5.0)(typescript@5.1.6): + /ts-node@10.9.1(@types/node@20.5.7)(typescript@5.2.2): resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} hasBin: true peerDependencies: @@ -1604,28 +1981,75 @@ packages: '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 20.5.0 + '@types/node': 20.5.7 acorn: 8.10.0 acorn-walk: 8.2.0 arg: 4.1.3 create-require: 1.1.1 diff: 4.0.2 make-error: 1.3.6 - typescript: 5.1.6 + typescript: 5.2.2 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 dev: true - /tslib@2.6.1: - resolution: {integrity: sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==} + /tslib@2.6.2: + resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} + dev: true + + /typed-array-buffer@1.0.0: + resolution: {integrity: sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.2.1 + is-typed-array: 1.1.12 dev: true - /typescript@5.1.6: - resolution: {integrity: sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==} + /typed-array-byte-length@1.0.0: + resolution: {integrity: sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + for-each: 0.3.3 + has-proto: 1.0.1 + is-typed-array: 1.1.12 + dev: true + + /typed-array-byte-offset@1.0.0: + resolution: {integrity: sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.5 + call-bind: 1.0.2 + for-each: 0.3.3 + has-proto: 1.0.1 + is-typed-array: 1.1.12 + dev: true + + /typed-array-length@1.0.4: + resolution: {integrity: sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==} + dependencies: + call-bind: 1.0.2 + for-each: 0.3.3 + is-typed-array: 1.1.12 + dev: true + + /typescript@5.2.2: + resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==} engines: {node: '>=14.17'} hasBin: true dev: true + /unbox-primitive@1.0.2: + resolution: {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 + dev: true + /universalify@2.0.0: resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==} engines: {node: '>= 10.0.0'} @@ -1634,13 +2058,13 @@ packages: /upper-case-first@2.0.2: resolution: {integrity: sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg==} dependencies: - tslib: 2.6.1 + tslib: 2.6.2 dev: true /upper-case@2.0.2: resolution: {integrity: sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg==} dependencies: - tslib: 2.6.1 + tslib: 2.6.2 dev: true /util-deprecate@1.0.2: @@ -1651,8 +2075,8 @@ packages: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} dev: true - /viem@1.6.0(typescript@5.1.6)(zod@3.22.1): - resolution: {integrity: sha512-ae9Twkd0q2Qlj4yYpWjb4DzYAhKY0ibEpRH8FJaTywZXNpTjFidSdBaT0CVn1BaH7O7cnX4/O47zvDUMGJD1AA==} + /viem@1.9.3(typescript@5.2.2)(zod@3.22.2): + resolution: {integrity: sha512-5NcxPHkWCZIBYm8A8oOd77l2RQ/+VRm1p56mBxoRcZ+Mij5pA+5Y3cohz7MHt6gYmmoDWlLXUXxsdPLedeF2wg==} peerDependencies: typescript: '>=5.0.4' peerDependenciesMeta: @@ -1665,10 +2089,9 @@ packages: '@scure/bip32': 1.3.0 '@scure/bip39': 1.2.0 '@types/ws': 8.5.5 - '@wagmi/chains': 1.6.0(typescript@5.1.6) - abitype: 0.9.3(typescript@5.1.6)(zod@3.22.1) + abitype: 0.9.3(typescript@5.2.2)(zod@3.22.2) isomorphic-ws: 5.0.0(ws@8.12.0) - typescript: 5.1.6 + typescript: 5.2.2 ws: 8.12.0 transitivePeerDependencies: - bufferutil @@ -1687,6 +2110,27 @@ packages: engines: {node: '>= 8'} dev: true + /which-boxed-primitive@1.0.2: + resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} + dependencies: + is-bigint: 1.0.4 + is-boolean-object: 1.1.2 + is-number-object: 1.0.7 + is-string: 1.0.7 + is-symbol: 1.0.4 + dev: true + + /which-typed-array@1.1.11: + resolution: {integrity: sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.5 + call-bind: 1.0.2 + for-each: 0.3.3 + gopd: 1.0.1 + has-tostringtag: 1.0.0 + dev: true + /which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -1718,6 +2162,6 @@ packages: engines: {node: '>=12.20'} dev: true - /zod@3.22.1: - resolution: {integrity: sha512-+qUhAMl414+Elh+fRNtpU+byrwjDFOS1N7NioLY+tSlcADTx4TkCUua/hxJvxwDXcV4397/nZ420jy4n4+3WUg==} + /zod@3.22.2: + resolution: {integrity: sha512-wvWkphh5WQsJbVk1tbx1l1Ly4yg+XecD+Mq280uBGt9wa5BKSWf4Mhp6GmrkPixhMxmabYY7RbzlwVP32pbGCg==} dev: true diff --git a/script/AddMaticToLiquidityPool.s.sol b/script/AddMaticToLiquidityPool.s.sol index 890c371..53a44dd 100644 --- a/script/AddMaticToLiquidityPool.s.sol +++ b/script/AddMaticToLiquidityPool.s.sol @@ -3,10 +3,10 @@ pragma solidity 0.8.21; import "forge-std/Script.sol"; import "forge-std/console.sol"; -import {UpgradeScript} from "./utils/UpgradeScript.s.sol"; -import {MonoPool} from "swap-pool/MonoPool.sol"; -import {EncoderLib} from "swap-pool/encoder/EncoderLib.sol"; -import {SafeTransferLib} from "solady/utils/SafeTransferLib.sol"; +import { UpgradeScript } from "./utils/UpgradeScript.s.sol"; +import { MonoPool } from "swap-pool/MonoPool.sol"; +import { EncoderLib } from "swap-pool/encoder/EncoderLib.sol"; +import { SafeTransferLib } from "solady/utils/SafeTransferLib.sol"; contract AddMaticToLiquidityPool is UpgradeScript { using SafeTransferLib for address; @@ -39,7 +39,10 @@ contract AddMaticToLiquidityPool is UpgradeScript { } /// @dev Build the add liquidity command - function _buildAddLiquidityCommand(uint256 frkToDeposit, uint256 maticAmount) + function _buildAddLiquidityCommand( + uint256 frkToDeposit, + uint256 maticAmount + ) private pure returns (bytes memory program) @@ -61,11 +64,14 @@ contract AddMaticToLiquidityPool is UpgradeScript { address frkToken, uint256 frkAmount, uint256 maticAmount - ) private deployerBroadcast { + ) + private + deployerBroadcast + { // Approve the pool to spend the frk frkToken.safeApprove(address(pool), frkAmount); // Execute the command - pool.execute{value: maticAmount}(program); + pool.execute{ value: maticAmount }(program); } } diff --git a/script/DeploySwapPool.s.sol b/script/DeploySwapPool.s.sol index 71fce66..cf192b7 100644 --- a/script/DeploySwapPool.s.sol +++ b/script/DeploySwapPool.s.sol @@ -3,9 +3,8 @@ pragma solidity 0.8.21; import "forge-std/Script.sol"; import "forge-std/console.sol"; -import {UpgradeScript} from "./utils/UpgradeScript.s.sol"; -import {MonoPool} from "swap-pool/MonoPool.sol"; - +import { UpgradeScript } from "./utils/UpgradeScript.s.sol"; +import { MonoPool } from "swap-pool/MonoPool.sol"; contract DeploySwapPool is UpgradeScript { /// @dev The basis point for the pool to deploy @@ -19,7 +18,7 @@ contract DeploySwapPool is UpgradeScript { // Get the frak-labs address address feeReceiver; - if (block.chainid == 80001) { + if (block.chainid == 80_001) { feeReceiver = address(0x8Cb488e0E16e49F064e210969EE1c771a55BcD04); } else if (block.chainid == 137) { feeReceiver = address(0x517ecFa01E2F9A6955d8DD04867613E41309213d); @@ -36,7 +35,10 @@ contract DeploySwapPool is UpgradeScript { } /// @dev Fake some reward for a user - function _deployInitialPool(address frkToken, address feeReceiver) + function _deployInitialPool( + address frkToken, + address feeReceiver + ) internal deployerBroadcast returns (MonoPool monotokenPool) diff --git a/script/PerformMaticFrkSwap.s.sol b/script/PerformMaticFrkSwap.s.sol index 7c70584..4bf0b48 100644 --- a/script/PerformMaticFrkSwap.s.sol +++ b/script/PerformMaticFrkSwap.s.sol @@ -3,11 +3,11 @@ pragma solidity 0.8.21; import "forge-std/Script.sol"; import "forge-std/console.sol"; -import {UpgradeScript} from "./utils/UpgradeScript.s.sol"; -import {MonoPool} from "swap-pool/MonoPool.sol"; -import {EncoderLib} from "swap-pool/encoder/EncoderLib.sol"; -import {SafeTransferLib} from "solady/utils/SafeTransferLib.sol"; -import {FrakToken} from "contracts/tokens/FrakTokenL2.sol"; +import { UpgradeScript } from "./utils/UpgradeScript.s.sol"; +import { MonoPool } from "swap-pool/MonoPool.sol"; +import { EncoderLib } from "swap-pool/encoder/EncoderLib.sol"; +import { SafeTransferLib } from "solady/utils/SafeTransferLib.sol"; +import { FrakToken } from "contracts/tokens/FrakToken.sol"; contract PerformMaticFrkSwap is UpgradeScript { using SafeTransferLib for address; @@ -66,7 +66,10 @@ contract PerformMaticFrkSwap is UpgradeScript { } /// @dev Build the matic to frk swap command - function _buildSwapMaticToFrkCommand(uint256 maticAmount, address user) + function _buildSwapMaticToFrkCommand( + uint256 maticAmount, + address user + ) private pure returns (bytes memory program) @@ -82,7 +85,12 @@ contract PerformMaticFrkSwap is UpgradeScript { } /// @dev Build the frk to matic swap command - function _buildSwapFrkToMaticCommand(FrakToken frkToken, address pool, uint256 frkAmount, uint256 privateKey) + function _buildSwapFrkToMaticCommand( + FrakToken frkToken, + address pool, + uint256 frkAmount, + uint256 privateKey + ) private view returns (bytes memory program) @@ -111,7 +119,10 @@ contract PerformMaticFrkSwap is UpgradeScript { bytes32 s; } - function _appendPermitSignature(bytes memory program, AppendPermitSignature memory params) + function _appendPermitSignature( + bytes memory program, + AppendPermitSignature memory params + ) internal pure returns (bytes memory) @@ -165,7 +176,7 @@ contract PerformMaticFrkSwap is UpgradeScript { /// @dev Execute the add liquidity command function _executeMaticSwap(MonoPool pool, bytes memory program, uint256 maticAmount) private deployerBroadcast { // Execute the command - pool.execute{value: maticAmount}(program); + pool.execute{ value: maticAmount }(program); } /// @dev Execute the add liquidity command diff --git a/script/RemoveLiquidityPoolPosition.s.sol b/script/RemoveLiquidityPoolPosition.s.sol index ffa6abe..1a9c568 100644 --- a/script/RemoveLiquidityPoolPosition.s.sol +++ b/script/RemoveLiquidityPoolPosition.s.sol @@ -3,11 +3,11 @@ pragma solidity 0.8.21; import "forge-std/Script.sol"; import "forge-std/console.sol"; -import {UpgradeScript} from "./utils/UpgradeScript.s.sol"; -import {MonoPool} from "swap-pool/MonoPool.sol"; -import {Token} from "swap-pool/libs/TokenLib.sol"; -import {EncoderLib} from "swap-pool/encoder/EncoderLib.sol"; -import {SafeTransferLib} from "solady/utils/SafeTransferLib.sol"; +import { UpgradeScript } from "./utils/UpgradeScript.s.sol"; +import { MonoPool } from "swap-pool/MonoPool.sol"; +import { Token } from "swap-pool/libs/TokenLib.sol"; +import { EncoderLib } from "swap-pool/encoder/EncoderLib.sol"; +import { SafeTransferLib } from "solady/utils/SafeTransferLib.sol"; contract RemoveLiquidityPoolPosition is UpgradeScript { using SafeTransferLib for address; @@ -55,7 +55,10 @@ contract RemoveLiquidityPoolPosition is UpgradeScript { } /// @dev Build the add liquidity command - function _builRemoveLiquidityAndClaimFeesCommand(address owner, uint256 position) + function _builRemoveLiquidityAndClaimFeesCommand( + address owner, + uint256 position + ) private pure returns (bytes memory program) diff --git a/script/UpdateAll.s.sol b/script/UpdateAll.s.sol index 203076f..2b70411 100644 --- a/script/UpdateAll.s.sol +++ b/script/UpdateAll.s.sol @@ -2,17 +2,17 @@ pragma solidity 0.8.21; import "forge-std/Script.sol"; -import {UpgradeScript} from "./utils/UpgradeScript.s.sol"; -import {FrakToken} from "@frak/tokens/FrakTokenL2.sol"; -import {FraktionTokens} from "@frak/tokens/FraktionTokens.sol"; -import {MultiVestingWallets} from "@frak/wallets/MultiVestingWallets.sol"; -import {VestingWalletFactory} from "@frak/wallets/VestingWalletFactory.sol"; -import {FrakTreasuryWallet} from "@frak/wallets/FrakTreasuryWallet.sol"; -import {ReferralPool} from "@frak/reward/pool/ReferralPool.sol"; -import {Minter} from "@frak/minter/Minter.sol"; -import {ContentPool} from "@frak/reward/pool/ContentPool.sol"; -import {Rewarder} from "@frak/reward/Rewarder.sol"; -import {FrakRoles} from "@frak/utils/FrakRoles.sol"; +import { UpgradeScript } from "./utils/UpgradeScript.s.sol"; +import { FrakToken } from "@frak/tokens/FrakToken.sol"; +import { FraktionTokens } from "@frak/fraktions/FraktionTokens.sol"; +import { MultiVestingWallets } from "@frak/wallets/MultiVestingWallets.sol"; +import { VestingWalletFactory } from "@frak/wallets/VestingWalletFactory.sol"; +import { FrakTreasuryWallet } from "@frak/wallets/FrakTreasuryWallet.sol"; +import { ReferralPool } from "@frak/reward/referralPool/ReferralPool.sol"; +import { Minter } from "@frak/minter/Minter.sol"; +import { ContentPool } from "@frak/reward/contentPool/ContentPool.sol"; +import { Rewarder } from "@frak/reward/Rewarder.sol"; +import { FrakRoles } from "@frak/roles/FrakRoles.sol"; contract UpdateAllScript is UpgradeScript { function run() external { diff --git a/script/test/FakeReward.s.sol b/script/test/FakeReward.s.sol index 8b45465..c31ea61 100644 --- a/script/test/FakeReward.s.sol +++ b/script/test/FakeReward.s.sol @@ -2,8 +2,9 @@ pragma solidity 0.8.21; import "forge-std/Script.sol"; -import {UpgradeScript} from "../utils/UpgradeScript.s.sol"; -import {Rewarder} from "@frak/reward/Rewarder.sol"; +import { UpgradeScript } from "../utils/UpgradeScript.s.sol"; +import { Rewarder } from "@frak/reward/Rewarder.sol"; +import { ContentId } from "@frak/libs/ContentId.sol"; contract FakeRewardScript is UpgradeScript { function run() external { @@ -16,8 +17,8 @@ contract FakeRewardScript is UpgradeScript { /// @dev Fake some reward for a user function _fakeSomeReward(Rewarder rewarder, address user) internal deployerBroadcast { - uint256[] memory contentIds = new uint256[](1); - contentIds[0] = 3; + ContentId[] memory contentIds = new ContentId[](1); + contentIds[0] = ContentId.wrap(3); uint256[] memory listenCounts = new uint256[](1); listenCounts[0] = 300; diff --git a/script/utils/DeployAllScript.s.sol b/script/utils/DeployAllScript.s.sol index 6b5e3ed..ea101b3 100644 --- a/script/utils/DeployAllScript.s.sol +++ b/script/utils/DeployAllScript.s.sol @@ -1,20 +1,20 @@ // SPDX-License-Identifier: GNU GPLv3 pragma solidity 0.8.21; -import {UpgradeScript} from "./UpgradeScript.s.sol"; -import {FrakToken} from "@frak/tokens/FrakTokenL2.sol"; -import {FraktionTokens} from "@frak/tokens/FraktionTokens.sol"; -import {MultiVestingWallets} from "@frak/wallets/MultiVestingWallets.sol"; -import {VestingWalletFactory} from "@frak/wallets/VestingWalletFactory.sol"; -import {FrakTreasuryWallet} from "@frak/wallets/FrakTreasuryWallet.sol"; -import {ReferralPool} from "@frak/reward/pool/ReferralPool.sol"; -import {Minter} from "@frak/minter/Minter.sol"; -import {ContentPool} from "@frak/reward/pool/ContentPool.sol"; -import {Rewarder} from "@frak/reward/Rewarder.sol"; -import {FrakRoles} from "@frak/utils/FrakRoles.sol"; +import { UpgradeScript } from "./UpgradeScript.s.sol"; +import { FrakToken } from "@frak/tokens/FrakToken.sol"; +import { FraktionTokens } from "@frak/fraktions/FraktionTokens.sol"; +import { MultiVestingWallets } from "@frak/wallets/MultiVestingWallets.sol"; +import { VestingWalletFactory } from "@frak/wallets/VestingWalletFactory.sol"; +import { FrakTreasuryWallet } from "@frak/wallets/FrakTreasuryWallet.sol"; +import { ReferralPool } from "@frak/reward/referralPool/ReferralPool.sol"; +import { Minter } from "@frak/minter/Minter.sol"; +import { ContentPool } from "@frak/reward/contentPool/ContentPool.sol"; +import { Rewarder } from "@frak/reward/Rewarder.sol"; +import { FrakRoles } from "@frak/roles/FrakRoles.sol"; contract DeployAllScript is UpgradeScript { - constructor() {} + constructor() { } function _setUpTestEnv() internal { // Deploy each tokens related contract @@ -63,7 +63,7 @@ contract DeployAllScript is UpgradeScript { // Deploy the initial frk token FrakToken implementation = new FrakToken(); vm.label(address(implementation), "FrkToken"); - bytes memory initData = abi.encodeCall(FrakToken.initialize, (address(this))); + bytes memory initData = abi.encodeCall(FrakToken.initialize, ()); // Deploy the proxy proxy = _deployProxy(address(implementation), initData, "FrkToken"); } @@ -103,7 +103,10 @@ contract DeployAllScript is UpgradeScript { } /// @dev Grand the required roles to the multi vesting wallet - function _grantMultiVestingWalletRoles(address proxyAddress, address vestingWalletFactory) + function _grantMultiVestingWalletRoles( + address proxyAddress, + address vestingWalletFactory + ) private deployerBroadcast { @@ -134,7 +137,11 @@ contract DeployAllScript is UpgradeScript { } /// @dev Deploy the minter - function _deployMinter(address frkToken, address fraktionTokens, address foundation) + function _deployMinter( + address frkToken, + address fraktionTokens, + address foundation + ) private deployerBroadcast returns (address proxy) @@ -174,7 +181,11 @@ contract DeployAllScript is UpgradeScript { address contentPool, address referralPool, address foundation - ) private deployerBroadcast returns (address proxy) { + ) + private + deployerBroadcast + returns (address proxy) + { // Deploy the initial frk token Rewarder implementation = new Rewarder(); vm.label(address(implementation), "Rewarder"); @@ -191,7 +202,10 @@ contract DeployAllScript is UpgradeScript { address referralPool, address fraktionTokens, address minter - ) private deployerBroadcast { + ) + private + deployerBroadcast + { // Grant role for the rewarder ReferralPool(referralPool).grantRole(FrakRoles.REWARDER, rewarder); ContentPool(contentPool).grantRole(FrakRoles.REWARDER, rewarder); diff --git a/script/utils/UpgradeScript.s.sol b/script/utils/UpgradeScript.s.sol index b7b6fc4..c660c8c 100644 --- a/script/utils/UpgradeScript.s.sol +++ b/script/utils/UpgradeScript.s.sol @@ -3,8 +3,8 @@ pragma solidity 0.8.21; import "forge-std/console.sol"; import "forge-std/Script.sol"; -import {UUPSUpgradeable} from "@oz-upgradeable/proxy/utils/UUPSUpgradeable.sol"; -import {ERC1967Proxy} from "openzeppelin/proxy/ERC1967/ERC1967Proxy.sol"; +import { UUPSUpgradeable } from "@oz-upgradeable/proxy/utils/UUPSUpgradeable.sol"; +import { ERC1967Proxy } from "openzeppelin/proxy/ERC1967/ERC1967Proxy.sol"; /** * @author @KONFeature @@ -49,9 +49,12 @@ abstract contract UpgradeScript is Script { frakTreasuryWallet: 0x7053f61CEA3B7C3b5f0e14de6eEdB01cA1850408, swapPool: 0x8a67c75F8C0bF37a06d92938e9C9e841506D37B0 }); - // forge verify-contract --chain polygon 0x8a67c75F8C0bF37a06d92938e9C9e841506D37B0 MonoPool --constructor-args $(cast abi-encode "constructor(address,address,uint256,address,uint256)" 0x6261E4a478C98419EaFa6289509C49058D21Df8c 0x0000000000000000000000000000000000000000 100 0x517ecFa01E2F9A6955d8DD04867613E41309213d 100) + // forge verify-contract --chain polygon 0x8a67c75F8C0bF37a06d92938e9C9e841506D37B0 MonoPool --constructor-args + // $(cast abi-encode "constructor(address,address,uint256,address,uint256)" + // 0x6261E4a478C98419EaFa6289509C49058D21Df8c 0x0000000000000000000000000000000000000000 100 + // 0x517ecFa01E2F9A6955d8DD04867613E41309213d 100) // Mumbai proxy address - contractAddresses[80001] = ContractProxyAddresses({ + contractAddresses[80_001] = ContractProxyAddresses({ frakToken: 0xbCeE0E1C02E91EAFaEd69eD2B1DC5199789575df, fraktionTokens: 0x00ec5dd47eD5341A43d66F8aA7b6793277d1e29E, multiVestingWallet: 0x08F674c3577f759D315336ae5a7ff6ea5bE2c35E, @@ -104,7 +107,11 @@ abstract contract UpgradeScript is Script { * @param name The name of the contract to deploy * @return proxyAddress The address of the deployed proxy */ - function _deployProxy(address implementation, bytes memory data, string memory name) + function _deployProxy( + address implementation, + bytes memory data, + string memory name + ) internal returns (address proxyAddress) { diff --git a/test/FrkTokenTestHelper.sol b/test/FrkTokenTestHelper.sol index 480a75b..2b452fe 100644 --- a/test/FrkTokenTestHelper.sol +++ b/test/FrkTokenTestHelper.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GNU GPLv3 pragma solidity 0.8.21; -import {FrakToken} from "@frak/tokens/FrakTokenL2.sol"; -import {UUPSTestHelper} from "./UUPSTestHelper.sol"; +import { FrakToken } from "@frak/tokens/FrakToken.sol"; +import { UUPSTestHelper } from "./UUPSTestHelper.sol"; /// Testing the frak l2 token contract FrkTokenTestHelper is UUPSTestHelper { @@ -10,7 +10,7 @@ contract FrkTokenTestHelper is UUPSTestHelper { function _setupFrkToken() internal { // Deploy frak token - bytes memory initData = abi.encodeCall(FrakToken.initialize, (address(this))); + bytes memory initData = abi.encodeCall(FrakToken.initialize, ()); address frkProxyAddr = deployContract(address(new FrakToken()), initData); frakToken = FrakToken(frkProxyAddr); } diff --git a/test/UUPSTestHelper.sol b/test/UUPSTestHelper.sol index 18e599d..aff88f6 100644 --- a/test/UUPSTestHelper.sol +++ b/test/UUPSTestHelper.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GNU GPLv3 pragma solidity 0.8.21; -import {ERC1967Proxy} from "openzeppelin/proxy/ERC1967/ERC1967Proxy.sol"; -import {PRBTest} from "@prb/test/PRBTest.sol"; +import { ERC1967Proxy } from "openzeppelin/proxy/ERC1967/ERC1967Proxy.sol"; +import { PRBTest } from "@prb/test/PRBTest.sol"; /// Testing the frak l2 token contract UUPSTestHelper is PRBTest { diff --git a/test/gas-usage/ErrorGasUsageTest.sol b/test/gas-usage/ErrorGasUsageTest.sol index 383a4c3..0c2487c 100644 --- a/test/gas-usage/ErrorGasUsageTest.sol +++ b/test/gas-usage/ErrorGasUsageTest.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GNU GPLv3 pragma solidity 0.8.21; -import {PRBTest} from "@prb/test/PRBTest.sol"; +import { PRBTest } from "@prb/test/PRBTest.sol"; // Size : 50499 contract RevertWithString { diff --git a/test/gas-usage/EventGasUsageTest.sol b/test/gas-usage/EventGasUsageTest.sol index 7cfc9fb..5c703d6 100644 --- a/test/gas-usage/EventGasUsageTest.sol +++ b/test/gas-usage/EventGasUsageTest.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GNU GPLv3 pragma solidity 0.8.21; -import {PRBTest} from "@prb/test/PRBTest.sol"; +import { PRBTest } from "@prb/test/PRBTest.sol"; contract SampleEvent { event LogTransfer(address indexed from, address indexed to, uint256 value); diff --git a/test/gas-usage/IsZeroGasUsageTest.sol b/test/gas-usage/IsZeroGasUsageTest.sol index 95525aa..ba61bf2 100644 --- a/test/gas-usage/IsZeroGasUsageTest.sol +++ b/test/gas-usage/IsZeroGasUsageTest.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GNU GPLv3 pragma solidity 0.8.21; -import {PRBTest} from "@prb/test/PRBTest.sol"; +import { PRBTest } from "@prb/test/PRBTest.sol"; contract IsZero { function sample(address addr) external pure returns (bool) { diff --git a/test/gas-usage/LoopGasUsageTest.sol b/test/gas-usage/LoopGasUsageTest.sol index 8656281..79c5283 100644 --- a/test/gas-usage/LoopGasUsageTest.sol +++ b/test/gas-usage/LoopGasUsageTest.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GNU GPLv3 pragma solidity 0.8.21; -import {PRBTest} from "@prb/test/PRBTest.sol"; +import { PRBTest } from "@prb/test/PRBTest.sol"; // Deployment cost : 116_565 contract SolidityLoop { @@ -9,7 +9,7 @@ contract SolidityLoop { function sample(uint256[] calldata values) external pure returns (uint256[] memory transformed) { unchecked { transformed = new uint256[](values.length); - for (uint256 i = 0; i < values.length; i++) {} + for (uint256 i = 0; i < values.length; i++) { } } } } @@ -56,7 +56,7 @@ contract AssemblyInfiniteForLoop { let valuesEnd := add(valuesOffset, shl(5, values.length)) // Infinite loop - for {} 1 {} { + for { } 1 { } { // Load value from calldata let value := calldataload(valuesOffset) // Build transformed value diff --git a/test/minter/Minter.t.sol b/test/minter/Minter.t.sol index 8bc308b..2b931d9 100644 --- a/test/minter/Minter.t.sol +++ b/test/minter/Minter.t.sol @@ -1,13 +1,16 @@ // SPDX-License-Identifier: GNU GPLv3 pragma solidity 0.8.21; -import {NotAuthorized, InvalidAddress, ContractPaused, BadgeTooLarge} from "@frak/utils/FrakErrors.sol"; -import {FraktionTokens} from "@frak/tokens/FraktionTokens.sol"; -import {FrakToken} from "@frak/tokens/FrakTokenL2.sol"; -import {FrakMath} from "@frak/utils/FrakMath.sol"; -import {FrakRoles} from "@frak/utils/FrakRoles.sol"; -import {Minter} from "@frak/minter/Minter.sol"; -import {FrkTokenTestHelper} from "../FrkTokenTestHelper.sol"; +import { NotAuthorized, InvalidAddress, ContractPaused, BadgeTooLarge } from "@frak/utils/FrakErrors.sol"; +import { FraktionTokens } from "@frak/fraktions/FraktionTokens.sol"; +import { FrakToken } from "@frak/tokens/FrakToken.sol"; +import { IFrakToken } from "@frak/tokens/IFrakToken.sol"; +import { ContentId } from "@frak/libs/ContentId.sol"; +import { FraktionId } from "@frak/libs/FraktionId.sol"; +import { FrakRoles } from "@frak/roles/FrakRoles.sol"; +import { Minter } from "@frak/minter/Minter.sol"; +import { IMinter } from "@frak/minter/IMinter.sol"; +import { FrkTokenTestHelper } from "../FrkTokenTestHelper.sol"; import { NotAuthorized, InvalidAddress, @@ -18,9 +21,6 @@ import { /// Testing minter contract contract MinterTest is FrkTokenTestHelper { - using FrakMath for address; - using FrakMath for uint256; - FraktionTokens fraktionTokens; address foundationAddr = address(13); @@ -87,19 +87,19 @@ contract MinterTest is FrkTokenTestHelper { } function test_fail_addContent_InvalidSupply() public prankExecAsDeployer { - vm.expectRevert(Minter.InvalidSupply.selector); + vm.expectRevert(IMinter.InvalidSupply.selector); minter.addContent(address(1), 0, 1, 1, 1); - vm.expectRevert(Minter.InvalidSupply.selector); + vm.expectRevert(IMinter.InvalidSupply.selector); minter.addContent(address(1), 501, 1, 1, 1); - vm.expectRevert(Minter.InvalidSupply.selector); + vm.expectRevert(IMinter.InvalidSupply.selector); minter.addContent(address(1), 1, 201, 1, 1); - vm.expectRevert(Minter.InvalidSupply.selector); + vm.expectRevert(IMinter.InvalidSupply.selector); minter.addContent(address(1), 1, 1, 51, 1); - vm.expectRevert(Minter.InvalidSupply.selector); + vm.expectRevert(IMinter.InvalidSupply.selector); minter.addContent(address(1), 1, 1, 1, 21); } @@ -141,13 +141,13 @@ contract MinterTest is FrkTokenTestHelper { address user = vm.addr(privateKey); // Add an initial content prankDeployer(); - uint256 contentId = minter.addContent(address(1), 10, 1, 1, 1); + ContentId contentId = minter.addContent(address(1), 10, 1, 1, 1); // Mint some token to our user prankDeployer(); frakToken.mint(user, 500 ether); // Get the cost of the buy process - uint256 fraktionCommonId = contentId.buildCommonNftId(); + FraktionId fraktionCommonId = contentId.commonFraktionId(); uint256 cost = minter.getCostBadge(fraktionCommonId); // Sign the tx for the user @@ -158,7 +158,7 @@ contract MinterTest is FrkTokenTestHelper { minter.mintFraktionForUser(fraktionCommonId, user, block.timestamp, v, r, s); // Ensure the supply is good assertEq(fraktionTokens.supplyOf(fraktionCommonId), 9); - assertEq(fraktionTokens.balanceOf(user, fraktionCommonId), 1); + assertEq(fraktionTokens.balanceOf(user, FraktionId.unwrap(fraktionCommonId)), 1); } function test_fail_mintFraktionForUser_ContractPaused() public { @@ -166,13 +166,13 @@ contract MinterTest is FrkTokenTestHelper { address user = vm.addr(privateKey); // Add an initial content prankDeployer(); - uint256 contentId = minter.addContent(address(1), 10, 1, 1, 1); + ContentId contentId = minter.addContent(address(1), 10, 1, 1, 1); // Mint some token to our user prankDeployer(); frakToken.mint(user, 500 ether); // Get the cost of the buy process - uint256 fraktionCommonId = contentId.buildCommonNftId(); + FraktionId fraktionCommonId = contentId.commonFraktionId(); uint256 cost = minter.getCostBadge(fraktionCommonId); // Sign the tx for the user @@ -191,13 +191,13 @@ contract MinterTest is FrkTokenTestHelper { address user = vm.addr(privateKey); // Add an initial content prankDeployer(); - uint256 contentId = minter.addContent(address(1), 10, 1, 1, 1); + ContentId contentId = minter.addContent(address(1), 10, 1, 1, 1); // Mint some token to our user prankDeployer(); frakToken.mint(user, 500 ether); // Get the cost of the buy process - uint256 fraktionCommonId = contentId.buildCommonNftId(); + FraktionId fraktionCommonId = contentId.commonFraktionId(); uint256 cost = minter.getCostBadge(fraktionCommonId); // Sign the tx for the user @@ -213,13 +213,13 @@ contract MinterTest is FrkTokenTestHelper { address user = vm.addr(privateKey); // Add an initial content prankDeployer(); - uint256 contentId = minter.addContent(address(1), 1, 0, 1, 1); + ContentId contentId = minter.addContent(address(1), 1, 0, 1, 1); // Mint some token to our user prankDeployer(); frakToken.mint(user, 500 ether); // Get the cost of the buy process - uint256 fraktionCommonId = contentId.buildPremiumNftId(); + FraktionId fraktionCommonId = contentId.premiumFraktionId(); uint256 cost = minter.getCostBadge(fraktionCommonId); // Sign the tx for the user @@ -236,13 +236,13 @@ contract MinterTest is FrkTokenTestHelper { address user = vm.addr(privateKey); // Add an initial content prankDeployer(); - uint256 contentId = minter.addContent(address(1), 1, 1, 1, 1); + ContentId contentId = minter.addContent(address(1), 1, 1, 1, 1); // Mint some token to our user prankDeployer(); frakToken.mint(user, 500 ether); // Get the cost of the buy process - uint256 fraktionCommonId = contentId.buildCommonNftId(); + FraktionId fraktionCommonId = contentId.commonFraktionId(); uint256 cost = minter.getCostBadge(fraktionCommonId); // Sign the tx for the user @@ -251,7 +251,7 @@ contract MinterTest is FrkTokenTestHelper { // Launch the buy prcess vm.expectRevert(InvalidFraktionType.selector); prankDeployer(); - minter.mintFraktionForUser(contentId.buildFreeNftId(), user, block.timestamp, v, r, s); + minter.mintFraktionForUser(contentId.freeFraktionId(), user, block.timestamp, v, r, s); } function test_fail_mintFractionForUser_InvalidSigner() public { @@ -259,13 +259,13 @@ contract MinterTest is FrkTokenTestHelper { address user = vm.addr(privateKey); // Add an initial content prankDeployer(); - uint256 contentId = minter.addContent(address(1), 10, 1, 1, 1); + ContentId contentId = minter.addContent(address(1), 10, 1, 1, 1); // Mint some token to our user prankDeployer(); frakToken.mint(user, 500 ether); // Get the cost of the buy process - uint256 fraktionCommonId = contentId.buildCommonNftId(); + FraktionId fraktionCommonId = contentId.commonFraktionId(); uint256 cost = minter.getCostBadge(fraktionCommonId); // Sign the tx for the user @@ -273,7 +273,7 @@ contract MinterTest is FrkTokenTestHelper { // Launch the buy prcess prankDeployer(); - vm.expectRevert(FrakToken.InvalidSigner.selector); + vm.expectRevert(IFrakToken.InvalidSigner.selector); minter.mintFraktionForUser(fraktionCommonId, user, block.timestamp, v, r, s); // Ensure the supply hasn't changed assertEq(fraktionTokens.supplyOf(fraktionCommonId), 10); @@ -294,13 +294,13 @@ contract MinterTest is FrkTokenTestHelper { address user = vm.addr(privateKey); // Add an initial content prankDeployer(); - uint256 contentId = minter.addContent(address(1), 10, 1, 1, 1); + ContentId contentId = minter.addContent(address(1), 10, 1, 1, 1); // Mint some token to our user prankDeployer(); frakToken.mint(user, 500 ether); // Get the cost of the buy process - uint256 fraktionCommonId = contentId.buildCommonNftId(); + FraktionId fraktionCommonId = contentId.commonFraktionId(); uint256 cost = minter.getCostBadge(fraktionCommonId); // Sign the tx for the user @@ -311,7 +311,7 @@ contract MinterTest is FrkTokenTestHelper { minter.mintFraktion(fraktionCommonId, block.timestamp, v, r, s); // Ensure the supply is good assertEq(fraktionTokens.supplyOf(fraktionCommonId), 9); - assertEq(fraktionTokens.balanceOf(user, fraktionCommonId), 1); + assertEq(fraktionTokens.balanceOf(user, FraktionId.unwrap(fraktionCommonId)), 1); } function test_fail_mintFraktion_ContractPaused() public { @@ -319,13 +319,13 @@ contract MinterTest is FrkTokenTestHelper { address user = vm.addr(privateKey); // Add an initial content prankDeployer(); - uint256 contentId = minter.addContent(address(1), 10, 1, 1, 1); + ContentId contentId = minter.addContent(address(1), 10, 1, 1, 1); // Mint some token to our user prankDeployer(); frakToken.mint(user, 500 ether); // Get the cost of the buy process - uint256 fraktionCommonId = contentId.buildCommonNftId(); + FraktionId fraktionCommonId = contentId.commonFraktionId(); uint256 cost = minter.getCostBadge(fraktionCommonId); // Sign the tx for the user @@ -344,13 +344,13 @@ contract MinterTest is FrkTokenTestHelper { address user = vm.addr(privateKey); // Add an initial content prankDeployer(); - uint256 contentId = minter.addContent(address(1), 10, 1, 1, 1); + ContentId contentId = minter.addContent(address(1), 10, 1, 1, 1); // Mint some token to our user prankDeployer(); frakToken.mint(user, 500 ether); // Get the cost of the buy process - uint256 fraktionCommonId = contentId.buildCommonNftId(); + FraktionId fraktionCommonId = contentId.commonFraktionId(); uint256 cost = minter.getCostBadge(fraktionCommonId); // Sign the tx for the user @@ -364,7 +364,7 @@ contract MinterTest is FrkTokenTestHelper { (v, r, s) = _getSignedPermit(privateKey, cost); // Launch the second buy process - vm.expectRevert(Minter.TooManyFraktion.selector); + vm.expectRevert(IMinter.TooManyFraktion.selector); vm.prank(user); minter.mintFraktion(fraktionCommonId, block.timestamp, v, r, s); } @@ -377,34 +377,34 @@ contract MinterTest is FrkTokenTestHelper { */ function test_mintFreeFraktionForUser() public prankExecAsDeployer { // Add an initial content - uint256 contentId = minter.addContent(address(1), 1, 1, 1, 1); - minter.mintFreeFraktionForUser(contentId.buildFreeNftId(), address(1)); + ContentId contentId = minter.addContent(address(1), 1, 1, 1, 1); + minter.mintFreeFraktionForUser(contentId.freeFraktionId(), address(1)); } function test_fail_mintFreeFraktionForUser_ContractPaused() public prankExecAsDeployer { minter.pause(); vm.expectRevert(ContractPaused.selector); - minter.mintFreeFraktionForUser(1, address(1)); + minter.mintFreeFraktionForUser(FraktionId.wrap(1), address(1)); } function test_fail_mintFreeFraktionForUser_NotAuthorized() public { vm.expectRevert(NotAuthorized.selector); - minter.mintFreeFraktionForUser(1, address(1)); + minter.mintFreeFraktionForUser(FraktionId.wrap(1), address(1)); } function test_fail_mintFreeFraktionForUser_ExpectingOnlyFreeFraktion() public prankExecAsDeployer { // Add an initial content - uint256 contentId = minter.addContent(address(1), 1, 1, 1, 1); - vm.expectRevert(Minter.ExpectingOnlyFreeFraktion.selector); - minter.mintFreeFraktionForUser(contentId.buildCommonNftId(), address(1)); + ContentId contentId = minter.addContent(address(1), 1, 1, 1, 1); + vm.expectRevert(IMinter.ExpectingOnlyFreeFraktion.selector); + minter.mintFreeFraktionForUser(contentId.commonFraktionId(), address(1)); } function test_fail_mintFreeFraktionForUser_AlreadyHaveFreeFraktion() public prankExecAsDeployer { // Add an initial content - uint256 contentId = minter.addContent(address(1), 1, 1, 1, 1); - minter.mintFreeFraktionForUser(contentId.buildFreeNftId(), address(1)); - vm.expectRevert(Minter.TooManyFraktion.selector); - minter.mintFreeFraktionForUser(contentId.buildFreeNftId(), address(1)); + ContentId contentId = minter.addContent(address(1), 1, 1, 1, 1); + minter.mintFreeFraktionForUser(contentId.freeFraktionId(), address(1)); + vm.expectRevert(IMinter.TooManyFraktion.selector); + minter.mintFreeFraktionForUser(contentId.freeFraktionId(), address(1)); } /* @@ -412,39 +412,39 @@ contract MinterTest is FrkTokenTestHelper { */ function test_increaseSupply() public prankExecAsDeployer { // Add an initial content - uint256 contentId = minter.addContent(address(1), 1, 1, 1, 0); + ContentId contentId = minter.addContent(address(1), 1, 1, 1, 0); // Increase it's diamond supply - minter.increaseSupply(contentId.buildDiamondNftId(), 1); + minter.increaseSupply(contentId.diamondFraktionId(), 1); } function test_fail_increaseSupply_ContractPaused() public prankExecAsDeployer { minter.pause(); vm.expectRevert(ContractPaused.selector); - minter.increaseSupply(1, 1); + minter.increaseSupply(FraktionId.wrap(1), 1); } function test_fail_increaseSupply_NotAuthorized() public { vm.expectRevert(NotAuthorized.selector); - minter.increaseSupply(1, 1); + minter.increaseSupply(FraktionId.wrap(1), 1); } function test_fail_increaseSupply_SupplyUpdateNotAllowed() public prankExecAsDeployer { // Add an initial content - uint256 contentId = minter.addContent(address(1), 1, 1, 1, 0); + ContentId contentId = minter.addContent(address(1), 1, 1, 1, 0); // Revert cause of free fraktion vm.expectRevert(FraktionTokens.SupplyUpdateNotAllowed.selector); - minter.increaseSupply(contentId.buildFreeNftId(), 1); + minter.increaseSupply(contentId.freeFraktionId(), 1); // Revert cause of nft id vm.expectRevert(FraktionTokens.SupplyUpdateNotAllowed.selector); - minter.increaseSupply(contentId.buildNftId(), 1); + minter.increaseSupply(contentId.creatorFraktionId(), 1); } function test_fail_increaseSupply_RemainingSupply() public prankExecAsDeployer { // Add an initial content - uint256 contentId = minter.addContent(address(1), 1, 1, 1, 0); + ContentId contentId = minter.addContent(address(1), 1, 1, 1, 0); // Revert cause of free fraktion vm.expectRevert(FraktionTokens.RemainingSupply.selector); - minter.increaseSupply(contentId.buildCommonNftId(), 1); + minter.increaseSupply(contentId.commonFraktionId(), 1); } /* diff --git a/test/reward/Rewarder.pay.t.sol b/test/reward/Rewarder.pay.t.sol index 5f0e8a5..1345c4d 100644 --- a/test/reward/Rewarder.pay.t.sol +++ b/test/reward/Rewarder.pay.t.sol @@ -1,17 +1,20 @@ // SPDX-License-Identifier: GNU GPLv3 pragma solidity 0.8.21; -import {StdUtils} from "@forge-std/StdUtils.sol"; -import {Rewarder} from "@frak/reward/Rewarder.sol"; -import {FrakMath} from "@frak/utils/FrakMath.sol"; -import {ContractPaused, NotAuthorized, InvalidArray, InvalidAddress, RewardTooLarge} from "@frak/utils/FrakErrors.sol"; -import {RewarderTestHelper} from "./RewarderTestHelper.sol"; +import { StdUtils } from "@forge-std/StdUtils.sol"; +import { Rewarder } from "@frak/reward/Rewarder.sol"; +import { IRewarder } from "@frak/reward/IRewarder.sol"; +import { ArrayLib } from "@frak/libs/ArrayLib.sol"; +import { ContentId } from "@frak/libs/ContentId.sol"; +import { FraktionId } from "@frak/libs/FraktionId.sol"; +import { ContractPaused, NotAuthorized, InvalidArray, InvalidAddress, RewardTooLarge } from "@frak/utils/FrakErrors.sol"; +import { RewarderTestHelper } from "./RewarderTestHelper.sol"; /// Testing the rewarder pay function contract RewarderPayTest is RewarderTestHelper, StdUtils { - using FrakMath for uint256; + using ArrayLib for uint256; - uint256 contentId; + ContentId contentId; function setUp() public { _baseSetUp(); @@ -43,12 +46,12 @@ contract RewarderPayTest is RewarderTestHelper, StdUtils { } function test_fail_payUserDirectly_InvalidReward() public withFrkToken(rewarderAddr) prankExecAsDeployer { - vm.expectRevert(Rewarder.InvalidReward.selector); + vm.expectRevert(IRewarder.InvalidReward.selector); rewarder.payUserDirectly(address(1), 0); } function test_fail_payUserDirectly_TooLargeReward() public withFrkToken(rewarderAddr) prankExecAsDeployer { - vm.expectRevert(Rewarder.InvalidReward.selector); + vm.expectRevert(IRewarder.InvalidReward.selector); rewarder.payUserDirectly(address(1), 1_000_001 ether); } @@ -79,21 +82,21 @@ contract RewarderPayTest is RewarderTestHelper, StdUtils { } function test_fail_payCreatorDirectlyBatch_InvalidArray() public withFrkToken(rewarderAddr) prankExecAsDeployer { - uint256[] memory contentIds = new uint256[](3); + ContentId[] memory contentIds = new ContentId[](3); uint256[] memory amountsIds = new uint256[](4); vm.expectRevert(InvalidArray.selector); rewarder.payCreatorDirectlyBatch(contentIds, amountsIds); } function test_fail_payCreatorDirectlyBatch_TooLargeArray() public withFrkToken(rewarderAddr) prankExecAsDeployer { - uint256[] memory contentIds = new uint256[](21); + ContentId[] memory contentIds = new ContentId[](21); uint256[] memory amountsIds = new uint256[](21); vm.expectRevert(InvalidArray.selector); rewarder.payCreatorDirectlyBatch(contentIds, amountsIds); } function test_fail_payCreatorDirectlyBatch_EmptyAmount() public withFrkToken(rewarderAddr) prankExecAsDeployer { - vm.expectRevert(Rewarder.InvalidReward.selector); + vm.expectRevert(IRewarder.InvalidReward.selector); rewarder.payCreatorDirectlyBatch(contentId.asSingletonArray(), uint256(0).asSingletonArray()); } @@ -102,7 +105,7 @@ contract RewarderPayTest is RewarderTestHelper, StdUtils { withLotFrkToken(rewarderAddr) prankExecAsDeployer { - vm.expectRevert(Rewarder.InvalidReward.selector); + vm.expectRevert(IRewarder.InvalidReward.selector); rewarder.payCreatorDirectlyBatch(contentId.asSingletonArray(), uint256(1_000_001 ether).asSingletonArray()); } @@ -115,14 +118,14 @@ contract RewarderPayTest is RewarderTestHelper, StdUtils { )s ===== */ function test_payUser() public withLotFrkToken(rewarderAddr) prankExecAsDeployer { - (uint256[] memory listenCounts, uint256[] memory contentIds) = basePayParam(); + (uint256[] memory listenCounts, ContentId[] memory contentIds) = basePayParam(); rewarder.payUser(address(1), 1, contentIds, listenCounts); } function test_payUser_LargeReward() public withLotFrkToken(rewarderAddr) prankExecAsDeployer { mintFraktions(address(1), 20); - (uint256[] memory listenCounts, uint256[] memory contentIds) = basePayParam(300); + (uint256[] memory listenCounts, ContentId[] memory contentIds) = basePayParam(300); rewarder.payUser(address(1), 1, contentIds, listenCounts); } @@ -216,26 +219,26 @@ contract RewarderPayTest is RewarderTestHelper, StdUtils { rewarder.pause(); vm.expectRevert(ContractPaused.selector); - (uint256[] memory listenCounts, uint256[] memory contentIds) = basePayParam(); + (uint256[] memory listenCounts, ContentId[] memory contentIds) = basePayParam(); rewarder.payUser(address(1), 1, contentIds, listenCounts); } function test_fail_payUser_NotRewarder() public withFrkToken(rewarderAddr) { - (uint256[] memory listenCounts, uint256[] memory contentIds) = basePayParam(); + (uint256[] memory listenCounts, ContentId[] memory contentIds) = basePayParam(); vm.expectRevert(NotAuthorized.selector); rewarder.payUser(address(1), 1, contentIds, listenCounts); } function test_fail_payUser_TooLargeArray() public withFrkToken(rewarderAddr) prankExecAsDeployer { uint256[] memory listenCounts = new uint256[](21); - uint256[] memory contentIds = new uint256[](21); + ContentId[] memory contentIds = new ContentId[](21); vm.expectRevert(InvalidArray.selector); rewarder.payUser(address(1), 1, contentIds, listenCounts); } function test_fail_payUser_ArrayNotSameSize() public withFrkToken(rewarderAddr) prankExecAsDeployer { uint256[] memory listenCounts = new uint256[](4); - uint256[] memory contentIds = new uint256[](3); + ContentId[] memory contentIds = new ContentId[](3); vm.expectRevert(InvalidArray.selector); rewarder.payUser(address(1), 1, contentIds, listenCounts); } @@ -244,18 +247,69 @@ contract RewarderPayTest is RewarderTestHelper, StdUtils { // Then try to pay him (uint256[] memory listenCounts,) = basePayParam(); vm.expectRevert(InvalidAddress.selector); - rewarder.payUser(address(1), 1, uint256(13).asSingletonArray(), listenCounts); + rewarder.payUser(address(1), 1, ContentId.wrap(13).asSingletonArray(), listenCounts); + } + + function test_payUser_NoReward_ContentTypeNotKnown() public withLotFrkToken(rewarderAddr) prankExecAsDeployer { + (uint256[] memory listenCounts, ContentId[] memory contentIds) = basePayParam(); + + // Get the previous claimable balance + uint256 claimableBalance = rewarder.getAvailableFounds(address(1)); + uint256 frakMinted = rewarder.getFrkMinted(); + // Launch the pay + rewarder.payUser(address(1), 0, contentIds, listenCounts); + // Ensure the claimable balance is the same + assertEq(rewarder.getAvailableFounds(address(1)), claimableBalance); + assertEq(rewarder.getFrkMinted(), frakMinted); + } + + function test_payUser_ContentTypeImpactReward() public withLotFrkToken(rewarderAddr) prankExecAsDeployer { + (uint256[] memory listenCounts, ContentId[] memory contentIds) = basePayParam(); + + // Get the previous claimable balance + uint256 claimableBalance = rewarder.getAvailableFounds(address(1)); + + // Launch the pay with content type 3 (music, lowest one) + rewarder.payUser(address(1), 3, contentIds, listenCounts); + // Ensure the claimable diff is greater than 0 + uint256 claimableDiff = rewarder.getAvailableFounds(address(1)) - claimableBalance; + claimableBalance = rewarder.getAvailableFounds(address(1)); + assertGt(claimableDiff, 0); + + // Launch the pay with content type 2 (podcast, middle one) + rewarder.payUser(address(1), 2, contentIds, listenCounts); + // Ensure the claimable diff is greater + uint256 newClaimableDiff = rewarder.getAvailableFounds(address(1)) - claimableBalance; + assertGt(newClaimableDiff, claimableDiff); + claimableDiff = newClaimableDiff; + claimableBalance = rewarder.getAvailableFounds(address(1)); + + // Launch the pay with content type 4 (streaming, middle one) + rewarder.payUser(address(1), 4, contentIds, listenCounts); + // Ensure the claimable diff is greater + newClaimableDiff = rewarder.getAvailableFounds(address(1)) - claimableBalance; + assertEq(newClaimableDiff, claimableDiff); + claimableDiff = newClaimableDiff; + claimableBalance = rewarder.getAvailableFounds(address(1)); + + // Launch the pay with content type 1 (video, highest one) + rewarder.payUser(address(1), 1, contentIds, listenCounts); + // Ensure the claimable diff is greater + newClaimableDiff = rewarder.getAvailableFounds(address(1)) - claimableBalance; + assertGt(newClaimableDiff, claimableDiff); + claimableDiff = newClaimableDiff; + claimableBalance = rewarder.getAvailableFounds(address(1)); } /* * ===== UTILS===== */ - function basePayParam() private view returns (uint256[] memory, uint256[] memory) { + function basePayParam() private view returns (uint256[] memory, ContentId[] memory) { return basePayParam(50); } - function basePayParam(uint16 listenCount) private view returns (uint256[] memory, uint256[] memory) { + function basePayParam(uint16 listenCount) private view returns (uint256[] memory, ContentId[] memory) { uint256[] memory listenCounts = new uint256[](1); listenCounts[0] = listenCount; return (listenCounts, contentId.asSingletonArray()); @@ -267,16 +321,25 @@ contract RewarderPayTest is RewarderTestHelper, StdUtils { function mintFraktions(address target, uint256 amount) private { // Build the param for our new content mint, and mint it - uint256[] memory fTypeArray = FrakMath.payableTokenTypes(); + uint256[] memory fTypeArray = payableFraktionTypes(); uint256[] memory amounts = new uint256[](fTypeArray.length); for (uint256 i = 0; i < fTypeArray.length; i++) { amounts[i] = amount; } contentId = fraktionTokens.mintNewContent(contentOwnerAddress, fTypeArray, amounts); - uint256[] memory fIds = contentId.buildSnftIds(fTypeArray); + FraktionId[] memory fIds = contentId.payableFraktionIds(); for (uint256 i = 0; i < fIds.length; i++) { - fraktionTokens.mint(target, fIds[i], 1); + fraktionTokens.mint(target, FraktionId.unwrap(fIds[i]), 1); } } + + /// @dev Build an array of all the payable fraktion types + function payableFraktionTypes() internal pure returns (uint256[] memory types) { + types = new uint256[](4); + types[0] = 3; + types[1] = 4; + types[2] = 5; + types[3] = 6; + } } diff --git a/test/reward/Rewarder.t.sol b/test/reward/Rewarder.t.sol index 416dd02..f4b8587 100644 --- a/test/reward/Rewarder.t.sol +++ b/test/reward/Rewarder.t.sol @@ -1,13 +1,14 @@ // SPDX-License-Identifier: GNU GPLv3 pragma solidity 0.8.21; -import {RewarderTestHelper} from "./RewarderTestHelper.sol"; -import {FrakMath} from "@frak/utils/FrakMath.sol"; -import {NotAuthorized, InvalidAddress, ContractPaused, BadgeTooLarge} from "@frak/utils/FrakErrors.sol"; +import { RewarderTestHelper } from "./RewarderTestHelper.sol"; +import { ArrayLib } from "@frak/libs/ArrayLib.sol"; +import { ContentId } from "@frak/libs/ContentId.sol"; +import { NotAuthorized, InvalidAddress, ContractPaused, BadgeTooLarge } from "@frak/utils/FrakErrors.sol"; /// Testing the rewarder contract RewarderTest is RewarderTestHelper { - using FrakMath for uint256; + using ArrayLib for uint256; uint256[] fTypeArray = uint256(3).asSingletonArray(); @@ -49,13 +50,13 @@ contract RewarderTest is RewarderTestHelper { ) ===== */ function test_updateContentBadge() public prankExecAsDeployer { - uint256 contentId = fraktionTokens.mintNewContent(contentOwnerAddress, fTypeArray, fTypeArray); + ContentId contentId = fraktionTokens.mintNewContent(contentOwnerAddress, fTypeArray, fTypeArray); rewarder.updateContentBadge(contentId, 2 ether); assertEq(rewarder.getContentBadge(contentId), 2 ether); } function test_fail_updateContentBadge_ContractPaused() public prankExecAsDeployer { - uint256 contentId = fraktionTokens.mintNewContent(contentOwnerAddress, fTypeArray, fTypeArray); + ContentId contentId = fraktionTokens.mintNewContent(contentOwnerAddress, fTypeArray, fTypeArray); rewarder.pause(); vm.expectRevert(ContractPaused.selector); @@ -64,14 +65,14 @@ contract RewarderTest is RewarderTestHelper { function test_fail_updateContentBadge_NotAuthorized() public { prankDeployer(); - uint256 contentId = fraktionTokens.mintNewContent(contentOwnerAddress, fTypeArray, fTypeArray); + ContentId contentId = fraktionTokens.mintNewContent(contentOwnerAddress, fTypeArray, fTypeArray); vm.expectRevert(NotAuthorized.selector); rewarder.updateContentBadge(contentId, 2 ether); } function test_fail_updateContentBadge_BadgeCapReached() public prankExecAsDeployer { - uint256 contentId = fraktionTokens.mintNewContent(contentOwnerAddress, fTypeArray, fTypeArray); + ContentId contentId = fraktionTokens.mintNewContent(contentOwnerAddress, fTypeArray, fTypeArray); vm.expectRevert(BadgeTooLarge.selector); rewarder.updateContentBadge(contentId, 1001 ether); @@ -114,7 +115,7 @@ contract RewarderTest is RewarderTestHelper { } function test_multicall_singleData() public prankExecAsDeployer { - uint256 contentId = fraktionTokens.mintNewContent(contentOwnerAddress, fTypeArray, fTypeArray); + ContentId contentId = fraktionTokens.mintNewContent(contentOwnerAddress, fTypeArray, fTypeArray); // Build our calldata bytes[] memory callingData = new bytes[](1); @@ -124,7 +125,7 @@ contract RewarderTest is RewarderTestHelper { } function test_multicall_multipleData() public prankExecAsDeployer { - uint256 contentId = fraktionTokens.mintNewContent(contentOwnerAddress, fTypeArray, fTypeArray); + ContentId contentId = fraktionTokens.mintNewContent(contentOwnerAddress, fTypeArray, fTypeArray); frakToken.mint(address(rewarder), 5 ether); @@ -158,7 +159,7 @@ contract RewarderTest is RewarderTestHelper { function test_fail_multicall_NotAuthorized() public { prankDeployer(); - uint256 contentId = fraktionTokens.mintNewContent(contentOwnerAddress, fTypeArray, fTypeArray); + ContentId contentId = fraktionTokens.mintNewContent(contentOwnerAddress, fTypeArray, fTypeArray); // Build our calldata bytes[] memory callingData = new bytes[](1); diff --git a/test/reward/RewarderTestHelper.sol b/test/reward/RewarderTestHelper.sol index 77bc176..837914c 100644 --- a/test/reward/RewarderTestHelper.sol +++ b/test/reward/RewarderTestHelper.sol @@ -1,23 +1,21 @@ // SPDX-License-Identifier: GNU GPLv3 pragma solidity 0.8.21; -import {FrakToken} from "@frak/tokens/FrakTokenL2.sol"; -import {FraktionTokens} from "@frak/tokens/FraktionTokens.sol"; -import {ContentPool} from "@frak/reward/pool/ContentPool.sol"; -import {ReferralPool} from "@frak/reward/pool/ReferralPool.sol"; -import {Rewarder} from "@frak/reward/Rewarder.sol"; -import {FraktionTokens} from "@frak/tokens/FraktionTokens.sol"; -import {FrakMath} from "@frak/utils/FrakMath.sol"; -import {FrakRoles} from "@frak/utils/FrakRoles.sol"; -import {MultiVestingWallets} from "@frak/wallets/MultiVestingWallets.sol"; -import {PRBTest} from "@prb/test/PRBTest.sol"; -import {FrkTokenTestHelper} from "../FrkTokenTestHelper.sol"; +import { FrakToken } from "@frak/tokens/FrakToken.sol"; +import { FraktionTokens } from "@frak/fraktions/FraktionTokens.sol"; +import { ContentPool } from "@frak/reward/contentPool/ContentPool.sol"; +import { ReferralPool } from "@frak/reward/referralPool/ReferralPool.sol"; +import { Rewarder } from "@frak/reward/Rewarder.sol"; +import { FraktionTokens } from "@frak/fraktions/FraktionTokens.sol"; +import { ArrayLib } from "@frak/libs/ArrayLib.sol"; +import { ContentId } from "@frak/libs/ContentId.sol"; +import { FrakRoles } from "@frak/roles/FrakRoles.sol"; +import { MultiVestingWallets } from "@frak/wallets/MultiVestingWallets.sol"; +import { PRBTest } from "@prb/test/PRBTest.sol"; +import { FrkTokenTestHelper } from "../FrkTokenTestHelper.sol"; /// Helper for our rewarder test contract RewarderTestHelper is FrkTokenTestHelper { - using FrakMath for address; - using FrakMath for uint256; - FraktionTokens fraktionTokens; ContentPool contentPool; ReferralPool referralPool; @@ -35,16 +33,19 @@ contract RewarderTestHelper is FrkTokenTestHelper { bytes memory initData = abi.encodeCall(FraktionTokens.initialize, ("test_url")); address fraktionProxyAddr = deployContract(address(new FraktionTokens()), initData); fraktionTokens = FraktionTokens(fraktionProxyAddr); + vm.label(fraktionProxyAddr, "FraktionTokens"); // Deploy content pool initData = abi.encodeCall(ContentPool.initialize, (address(frakToken))); address contentPoolProxyAddr = deployContract(address(new ContentPool()), initData); contentPool = ContentPool(contentPoolProxyAddr); + vm.label(contentPoolProxyAddr, "ContentPool"); // Deploy referral pool initData = abi.encodeCall(ReferralPool.initialize, (address(frakToken))); address referralProxyAddr = deployContract(address(new ReferralPool()), initData); referralPool = ReferralPool(referralProxyAddr); + vm.label(referralProxyAddr, "ReferralPool"); // Deploy rewarder contract initData = abi.encodeCall( @@ -53,6 +54,7 @@ contract RewarderTestHelper is FrkTokenTestHelper { ); rewarderAddr = deployContract(address(new Rewarder()), initData); rewarder = Rewarder(rewarderAddr); + vm.label(rewarderAddr, "Rewarder"); // Link our content pool to the fraktion token prankDeployer(); @@ -78,9 +80,9 @@ contract RewarderTestHelper is FrkTokenTestHelper { * ===== UTILS===== */ - function mintAContent() public returns (uint256) { + function mintAContent() public returns (ContentId) { prankDeployer(); - uint256[] memory fTypeArray = uint256(3).asSingletonArray(); + uint256[] memory fTypeArray = ArrayLib.asSingletonArray(uint256(3)); return fraktionTokens.mintNewContent(contentOwnerAddress, fTypeArray, fTypeArray); } } diff --git a/test/reward/pool/ContentPool.t.sol b/test/reward/pool/ContentPool.t.sol index 8691af3..213c525 100644 --- a/test/reward/pool/ContentPool.t.sol +++ b/test/reward/pool/ContentPool.t.sol @@ -2,15 +2,17 @@ pragma solidity 0.8.21; import "forge-std/console.sol"; -import {ContentPool} from "@frak/reward/pool/ContentPool.sol"; -import {FrakMath} from "@frak/utils/FrakMath.sol"; -import {FrakRoles} from "@frak/utils/FrakRoles.sol"; -import {NotAuthorized, InvalidAddress, ContractPaused, NoReward} from "@frak/utils/FrakErrors.sol"; -import {FrkTokenTestHelper} from "../../FrkTokenTestHelper.sol"; +import { ContentPool } from "@frak/reward/contentPool/ContentPool.sol"; +import { ArrayLib } from "@frak/libs/ArrayLib.sol"; +import { ContentId } from "@frak/libs/ContentId.sol"; +import { FraktionId } from "@frak/libs/FraktionId.sol"; +import { FrakRoles } from "@frak/roles/FrakRoles.sol"; +import { NotAuthorized, InvalidAddress, ContractPaused, NoReward } from "@frak/utils/FrakErrors.sol"; +import { FrkTokenTestHelper } from "../../FrkTokenTestHelper.sol"; /// Testing the content pool contract ContentPoolTest is FrkTokenTestHelper { - using FrakMath for uint256; + using ArrayLib for uint256; ContentPool contentPool; @@ -23,7 +25,7 @@ contract ContentPoolTest is FrkTokenTestHelper { contentPool = ContentPool(contentPoolProxyAddr); prankDeployer(); - frakToken.mint(address(contentPool), 1_000 ether); + frakToken.mint(address(contentPool), 1000 ether); prankDeployer(); contentPool.grantRole(FrakRoles.REWARDER, deployer); @@ -43,36 +45,39 @@ contract ContentPoolTest is FrkTokenTestHelper { * ===== TEST : addReward(uint256 contentId, uint256 rewardAmount) ===== */ function test_addReward() public prankExecAsDeployer { - contentPool.addReward(1, 1 ether); + contentPool.addReward(ContentId.wrap(1), 1 ether); } function test_fail_addReward_NotAuthorized() public { vm.expectRevert(NotAuthorized.selector); - contentPool.addReward(1, 1 ether); + contentPool.addReward(ContentId.wrap(1), 1 ether); } function test_fail_addReward_ContractPaused() public prankExecAsDeployer { contentPool.pause(); vm.expectRevert(ContractPaused.selector); - contentPool.addReward(1, 1 ether); + contentPool.addReward(ContentId.wrap(1), 1 ether); } function test_fail_addReward_NoReward() public prankExecAsDeployer { vm.expectRevert(NoReward.selector); - contentPool.addReward(1, 0); + contentPool.addReward(ContentId.wrap(1), 0); vm.expectRevert(NoReward.selector); - contentPool.addReward(1, 100_001 ether); + contentPool.addReward(ContentId.wrap(1), 100_001 ether); } function test_fullProcess() public prankExecAsDeployer { // Add some initial reward - contentPool.addReward(1, 1 ether); + contentPool.addReward(ContentId.wrap(1), 1 ether); // Update a user shares contentPool.onFraktionsTransferred( - address(0), address(1), uint256(1).buildPremiumNftId().asSingletonArray(), uint256(1).asSingletonArray() + address(0), + address(1), + ContentId.wrap(1).premiumFraktionId().asSingletonArray(), + uint256(1).asSingletonArray() ); // Compute it's reward @@ -84,11 +89,11 @@ contract ContentPoolTest is FrkTokenTestHelper { // Add some initial reward console.log(""); console.log("- Add some initial reward"); - contentPool.addReward(1, 2 ether); + contentPool.addReward(ContentId.wrap(1), 2 ether); - uint256[] memory fraktionIds = new uint256[](2); - fraktionIds[0] = uint256(1).buildPremiumNftId(); - fraktionIds[1] = uint256(1).buildGoldNftId(); + FraktionId[] memory fraktionIds = new FraktionId[](2); + fraktionIds[0] = ContentId.wrap(1).premiumFraktionId(); + fraktionIds[1] = ContentId.wrap(1).goldFraktionId(); uint256[] memory amounts = new uint256[](2); amounts[0] = 2; amounts[1] = 1; @@ -101,7 +106,7 @@ contract ContentPoolTest is FrkTokenTestHelper { // Add a few other rewards console.log(""); console.log("- Add new reward"); - contentPool.addReward(1, 10 ether); + contentPool.addReward(ContentId.wrap(1), 10 ether); // Compute it's reward console.log(""); @@ -124,7 +129,7 @@ contract ContentPoolTest is FrkTokenTestHelper { assertEq(contentPool.getAvailableFounds(address(2)), 0); // Add a few other rewards - contentPool.addReward(1, 20 ether); + contentPool.addReward(ContentId.wrap(1), 20 ether); console.log(""); console.log("- Compute user 2"); @@ -134,12 +139,12 @@ contract ContentPoolTest is FrkTokenTestHelper { // Mint a few more fraktion to user 3 contentPool.onFraktionsTransferred(address(0), address(3), fraktionIds, amounts); - contentPool.addReward(1, 20 ether); + contentPool.addReward(ContentId.wrap(1), 20 ether); // Mint a few more fraktion to user 3 contentPool.onFraktionsTransferred(address(0), address(4), fraktionIds, amounts); contentPool.onFraktionsTransferred(address(0), address(5), fraktionIds, amounts); - contentPool.addReward(1, 20 ether); + contentPool.addReward(ContentId.wrap(1), 20 ether); console.log(""); console.log("- Compute user 2"); @@ -149,7 +154,7 @@ contract ContentPoolTest is FrkTokenTestHelper { // Mint a few more fraktion to user 3 contentPool.onFraktionsTransferred(address(4), address(0), fraktionIds, amounts); contentPool.onFraktionsTransferred(address(5), address(0), fraktionIds, amounts); - contentPool.addReward(1, 20 ether); + contentPool.addReward(ContentId.wrap(1), 20 ether); contentPool.computeAllPoolsBalance(address(2)); assertEq(contentPool.getAvailableFounds(address(2)), 45 ether); diff --git a/test/tokens/FraktionTokens.t.sol b/test/tokens/FraktionTokens.t.sol index 5de9618..2ff997a 100644 --- a/test/tokens/FraktionTokens.t.sol +++ b/test/tokens/FraktionTokens.t.sol @@ -1,11 +1,13 @@ // SPDX-License-Identifier: GNU GPLv3 pragma solidity 0.8.21; -import {PRBTest} from "@prb/test/PRBTest.sol"; -import {StdUtils} from "@forge-std/StdUtils.sol"; -import {FraktionTokens} from "@frak/tokens/FraktionTokens.sol"; -import {UUPSTestHelper} from "../UUPSTestHelper.sol"; -import {FrakMath} from "@frak/utils/FrakMath.sol"; +import { PRBTest } from "@prb/test/PRBTest.sol"; +import { StdUtils } from "@forge-std/StdUtils.sol"; +import { FraktionTokens } from "@frak/fraktions/FraktionTokens.sol"; +import { UUPSTestHelper } from "../UUPSTestHelper.sol"; +import { ArrayLib } from "@frak/libs/ArrayLib.sol"; +import { ContentId } from "@frak/libs/ContentId.sol"; +import { FraktionId } from "@frak/libs/FraktionId.sol"; import { NotAuthorized, InvalidAddress, @@ -16,7 +18,7 @@ import { /// Testing the frak l2 token contract FraktionTokensTest is UUPSTestHelper, StdUtils { - using FrakMath for uint256; + using ArrayLib for uint256; FraktionTokens fraktionTokens; @@ -41,9 +43,9 @@ contract FraktionTokensTest is UUPSTestHelper, StdUtils { * ===== TEST : mintNewContent(address ownerAddress) ===== */ function test_mintNewContent() public prankExecAsDeployer { - uint256 contentId = fraktionTokens.mintNewContent(address(1), sampleArray, sampleArray); - uint256 nftId = contentId.buildNftId(); - assertEq(fraktionTokens.balanceOf(address(1), nftId), 1); + ContentId contentId = fraktionTokens.mintNewContent(address(1), sampleArray, sampleArray); + FraktionId nftId = contentId.creatorFraktionId(); + assertEq(fraktionTokens.balanceOf(address(1), FraktionId.unwrap(nftId)), 1); } function test_fail_mintNewContent_ContractPaused() public prankExecAsDeployer { @@ -87,7 +89,7 @@ contract FraktionTokensTest is UUPSTestHelper, StdUtils { } /* - * ===== TEST : safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes calldata data) ===== + * ===== TEST : safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes calldata data) ===== */ function test_transfer() public { // Mint a few initial tokens to different address diff --git a/test/tokens/FrkTokenL2.t.sol b/test/tokens/FrkToken.t.sol similarity index 93% rename from test/tokens/FrkTokenL2.t.sol rename to test/tokens/FrkToken.t.sol index 5e1e245..584c897 100644 --- a/test/tokens/FrkTokenL2.t.sol +++ b/test/tokens/FrkToken.t.sol @@ -1,11 +1,12 @@ // SPDX-License-Identifier: GNU GPLv3 pragma solidity 0.8.21; -import {PRBTest} from "@prb/test/PRBTest.sol"; -import {StdUtils} from "@forge-std/StdUtils.sol"; -import {FrakToken} from "@frak/tokens/FrakTokenL2.sol"; -import {NotAuthorized} from "@frak/utils/FrakErrors.sol"; -import {UUPSTestHelper} from "../UUPSTestHelper.sol"; +import { PRBTest } from "@prb/test/PRBTest.sol"; +import { StdUtils } from "@forge-std/StdUtils.sol"; +import { FrakToken } from "@frak/tokens/FrakToken.sol"; +import { IFrakToken } from "@frak/tokens/FrakToken.sol"; +import { NotAuthorized } from "@frak/utils/FrakErrors.sol"; +import { UUPSTestHelper } from "../UUPSTestHelper.sol"; /// Testing the frak l2 token contract FrkTokenL2Test is UUPSTestHelper, StdUtils { @@ -13,7 +14,7 @@ contract FrkTokenL2Test is UUPSTestHelper, StdUtils { function setUp() public { // Deploy our contract via proxy and set the proxy address - bytes memory initData = abi.encodeCall(FrakToken.initialize, (address(this))); + bytes memory initData = abi.encodeCall(FrakToken.initialize, ()); address proxyAddress = deployContract(address(new FrakToken()), initData); frakToken = FrakToken(proxyAddress); } @@ -36,7 +37,7 @@ contract FrkTokenL2Test is UUPSTestHelper, StdUtils { */ function test_fail_initialize_CantInitTwice() public { vm.expectRevert("Initializable: contract is already initialized"); - frakToken.initialize(address(0)); + frakToken.initialize(); } /* @@ -209,7 +210,7 @@ contract FrkTokenL2Test is UUPSTestHelper, StdUtils { } function test_fail_mint_TooLarge() public prankExecAsDeployer { - vm.expectRevert(FrakToken.CapExceed.selector); + vm.expectRevert(IFrakToken.CapExceed.selector); frakToken.mint(address(1), 3_000_000_001 ether); } @@ -220,7 +221,8 @@ contract FrkTokenL2Test is UUPSTestHelper, StdUtils { } /* - * ===== TEST : permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) ===== + * ===== TEST : permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 + s) ===== */ bytes32 constant PERMIT_TYPEHASH = @@ -261,7 +263,7 @@ contract FrkTokenL2Test is UUPSTestHelper, StdUtils { ) ); - vm.expectRevert(FrakToken.InvalidSigner.selector); + vm.expectRevert(IFrakToken.InvalidSigner.selector); frakToken.permit(owner, address(2), 1 ether, block.timestamp, v, r, s); } @@ -299,7 +301,7 @@ contract FrkTokenL2Test is UUPSTestHelper, StdUtils { ) ); - vm.expectRevert(FrakToken.PermitDelayExpired.selector); + vm.expectRevert(IFrakToken.PermitDelayExpired.selector); frakToken.permit(owner, address(1), 1 ether, block.timestamp - 1, v, r, s); } } diff --git a/test/wallets/FrakTreasuryWallet.t.sol b/test/wallets/FrakTreasuryWallet.t.sol index cc38e9a..0a8904e 100644 --- a/test/wallets/FrakTreasuryWallet.t.sol +++ b/test/wallets/FrakTreasuryWallet.t.sol @@ -1,12 +1,12 @@ // SPDX-License-Identifier: GNU GPLv3 pragma solidity 0.8.21; -import {StdUtils} from "@forge-std/StdUtils.sol"; -import {FrakToken} from "@frak/tokens/FrakTokenL2.sol"; -import {FrakMath} from "@frak/utils/FrakMath.sol"; -import {FrakRoles} from "@frak/utils/FrakRoles.sol"; -import {FrakTreasuryWallet, NotEnoughTreasury} from "@frak/wallets/FrakTreasuryWallet.sol"; -import {FrkTokenTestHelper} from "../FrkTokenTestHelper.sol"; +import { StdUtils } from "@forge-std/StdUtils.sol"; +import { FrakToken } from "@frak/tokens/FrakToken.sol"; +import { ArrayLib } from "@frak/libs/ArrayLib.sol"; +import { FrakRoles } from "@frak/roles/FrakRoles.sol"; +import { FrakTreasuryWallet, NotEnoughTreasury } from "@frak/wallets/FrakTreasuryWallet.sol"; +import { FrkTokenTestHelper } from "../FrkTokenTestHelper.sol"; import { NotAuthorized, InvalidAddress, @@ -18,8 +18,8 @@ import { /// Testing the frak l2 token contract FrakTreasuryWalletTest is FrkTokenTestHelper, StdUtils { - using FrakMath for address; - using FrakMath for uint256; + using ArrayLib for address; + using ArrayLib for uint256; address treasuryWalletAddr; FrakTreasuryWallet treasuryWallet; diff --git a/test/wallets/MultiVestingWallets.t.sol b/test/wallets/MultiVestingWallets.t.sol index 6272d67..28beb23 100644 --- a/test/wallets/MultiVestingWallets.t.sol +++ b/test/wallets/MultiVestingWallets.t.sol @@ -1,12 +1,12 @@ // SPDX-License-Identifier: GNU GPLv3 pragma solidity 0.8.21; -import {FrakToken} from "@frak/tokens/FrakTokenL2.sol"; -import {FrakMath} from "@frak/utils/FrakMath.sol"; +import { FrakToken } from "@frak/tokens/FrakToken.sol"; +import { ArrayLib } from "@frak/libs/ArrayLib.sol"; import { MultiVestingWallets, NotEnoughFounds, InvalidDuration, InvalidDate } from "@frak/wallets/MultiVestingWallets.sol"; -import {FrkTokenTestHelper} from "../FrkTokenTestHelper.sol"; +import { FrkTokenTestHelper } from "../FrkTokenTestHelper.sol"; import { NotAuthorized, InvalidArray, @@ -18,8 +18,8 @@ import { /// Testing the frak l2 token contract MultiVestingWalletsTest is FrkTokenTestHelper { - using FrakMath for address; - using FrakMath for uint256; + using ArrayLib for address; + using ArrayLib for uint256; address multiVestingAddr; MultiVestingWallets vestingWallets; diff --git a/tools/manticore/manticore.sh b/tools/manticore/manticore.sh deleted file mode 100755 index 2a44f76..0000000 --- a/tools/manticore/manticore.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash - -# Assert we are on the root folder -if [ ! -d "contracts" ]; then - echo "error: script needs to be run from project root './tools/manticore/manticore.sh'" - exit 1 -fi - -# Remove previous manticore run -rm -rf mcore_*/ - -# Run manticore -docker run --rm -v "$PWD":/src --ulimit stack=100000000:100000000 --workdir=/src trailofbits/manticore bash -c 'pip3 install solc-select && -solc-select install 0.8.17 && solc-select use 0.8.17 && -manticore contracts/utils/FrakMath.sol --contract=SybelMath --config=tools/manticore/manticore.yaml && -manticore contracts/utils/FrakRoles.sol --contract=SybelRoles --config=tools/manticore/manticore.yaml' - -# Test of the sybel token broken, caused by https://github.com/trailofbits/manticore/issues/2455 -# manticore contracts/tokens/SybelToken.sol --contract=SybelToken --config=tools/manticore/manticore.yaml --solc-remaps @openzeppelin=node_modules/@openzeppelin' \ No newline at end of file diff --git a/tools/manticore/manticore.yaml b/tools/manticore/manticore.yaml deleted file mode 100644 index ae70ebe..0000000 --- a/tools/manticore/manticore.yaml +++ /dev/null @@ -1,5 +0,0 @@ -core: - compress_states: false -cli: - solc_remaps: '@openzeppelin=node_modules/@openzeppelin' - config: tools/manticore/manticore.yaml \ No newline at end of file diff --git a/tools/mythril/mythril.sh b/tools/mythril/mythril.sh deleted file mode 100755 index 54c9459..0000000 --- a/tools/mythril/mythril.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/bash - -# Assert we are in the right folder -if [ ! -d "contracts" ]; then - echo "error: script needs to be run from project root './tools/mythril/mythril.sh'" - exit 1 -fi - -# Run mythril analyse on the given contract -function analyse_contract { - docker run --rm -v `pwd`:/src --workdir=/src mythril/myth -v 4 analyze $1 --solc-json tools/mythril/remapping.json --max-depth 50 -} - -echo "<----- Checking SybelMath.sol ----->" -analyse_contract contracts/utils/FrakMath.sol - -echo "" -echo "<----- Checking MultiVestingWallets.sol ----->" -analyse_contract contracts/wallets/MultiVestingWallets.sol - -echo "" -echo "<----- Checking VestingWalletFactory.sol ----->" -analyse_contract contracts/wallets/VestingWalletFactory.sol - -echo "" -echo "<----- Checking SybelTokenL2.sol ----->" -analyse_contract contracts/tokens/FrakTokenL2.sol diff --git a/tools/mythril/remapping.json b/tools/mythril/remapping.json deleted file mode 100644 index 4e12fb2..0000000 --- a/tools/mythril/remapping.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "remappings": ["@openzeppelin/=node_modules/@openzeppelin/"], - "optimizer": { "enabled": true } -} diff --git a/tools/mythx/mythx.sh b/tools/mythx/mythx.sh deleted file mode 100755 index e5ba96b..0000000 --- a/tools/mythx/mythx.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -# Assert we are in the right folder -if [ ! -d "contracts" ]; then - echo "error: script needs to be run from project root './tools/mythril/mythril.sh'" - exit 1 -fi - -# Run the mythx analysis -mythx --api-key ***Api Key*** analyze --remap-import "@openzeppelin/=$(pwd)/node_modules/@openzeppelin/" --async --mode deep ../../contracts/wallets/VestingWallets.sol \ No newline at end of file diff --git a/tools/mythx/reward.mythx.yml b/tools/mythx/reward.mythx.yml deleted file mode 100644 index 7392afd..0000000 --- a/tools/mythx/reward.mythx.yml +++ /dev/null @@ -1,16 +0,0 @@ -output: mythx-ecosystem.json -format: json - -analyze: - mode: standard - async: true - create-group: true - group-name: Frak.id erc1155 and ecosystem - solc: 0.8.17 - remappings: - - "@openzeppelin/=node_modules/@openzeppelin/" - targets: - - contracts/tokens/FraktionTokens.sol - - contracts/reward/pool/ContentPool.sol - - contracts/reward/pool/ReferralPool.sol - - contracts/minter/Minter.sol diff --git a/tools/mythx/vesting.mythx.yml b/tools/mythx/vesting.mythx.yml deleted file mode 100644 index ae4f119..0000000 --- a/tools/mythx/vesting.mythx.yml +++ /dev/null @@ -1,16 +0,0 @@ -output: mythx-vesting.json -format: json - -analyze: - mode: standard - async: true - create-group: true - group-name: Frak.id erc20 and vesting - solc: 0.8.17 - remappings: - - "@openzeppelin/=node_modules/@openzeppelin/" - targets: - - contracts/tokens/FrakTokenL1.sol - - contracts/tokens/FrakTokenL2.sol - - contracts/wallets/VestingWalletFactory.sol - - contracts/wallets/MultiVestingWallets.sol diff --git a/tools/run-all-nohup.sh b/tools/run-all-nohup.sh deleted file mode 100755 index b5e49a3..0000000 --- a/tools/run-all-nohup.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -# Assert we are on the root folder -if [ ! -d "contracts" ]; then - echo "error: script needs to be run from project root './tools/run-all-nohup.sh'" - exit 1 -fi - -# Exec the run all script with nohup -nohup sh tools/run-all.sh > security-analysis.log 2>&1 & \ No newline at end of file diff --git a/tools/run-all.sh b/tools/run-all.sh deleted file mode 100755 index 6f62cb0..0000000 --- a/tools/run-all.sh +++ /dev/null @@ -1,52 +0,0 @@ -#!/bin/bash - -# Assert we are on the root folder -if [ ! -d "contracts" ]; then - echo "error: script needs to be run from project root './tools/run-all.sh'" - exit 1 -fi - -# Perform slithen flatten and slither analysis -echo "==============================" -echo "=== Starting security scan ===" -echo "==============================" - -#echo "" -#echo "!!==========================!!" -#echo "!!= Slither flatten =!!" -#echo "\\/==========================\\/" -#./tools/slither/flatten.sh -#echo "/\\==========================/\\" -#echo "!!= Slither flatten =!!" -#echo "!!==========================!!" - -#echo "!!==========================!!" -#echo "!!= Slither Analysis =!!" -#echo "\\/==========================\\/" -./tools/slither/slither.sh > tools/slither-output.log -#echo "/\\==========================/\\" -#echo "!!= Slither Analysis =!!" -#echo "!!==========================!!" - -# Perform Mythril run -echo "!!==========================!!" -echo "!!= Mythril =!!" -echo "\\/==========================\\/" -./tools/mythril/mythril.sh > tools/mythril-output.log -echo "/\\==========================/\\" -echo "!!= Mythril =!!" -echo "!!==========================!!" - -# Perform Manticore run -#echo "!!==========================!!" -#echo "!!= Manticore =!!" -#echo "\\/==========================\\/" -#./tools/manticore/manticore.sh > tools/manticore-output.log -#echo "/\\==========================/\\" -#echo "!!= Manticore =!!" -#echo "!!==========================!!" - -echo "" -echo "==============================" -echo "=== Ended security scan ===" -echo "==============================" \ No newline at end of file diff --git a/tools/slither.config.json b/tools/slither.config.json new file mode 100644 index 0000000..3262567 --- /dev/null +++ b/tools/slither.config.json @@ -0,0 +1,4 @@ +{ + "detectors_to_exclude": "naming-convention,reentrancy-events,solc-version,assembly,similar-names", + "filter_paths": "(lib/|test)" +} diff --git a/tools/slither/check-erc.sh b/tools/slither/check-erc.sh deleted file mode 100755 index b1e483f..0000000 --- a/tools/slither/check-erc.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -# Assert we are on the root folder -if [ ! -d "contracts" ]; then - echo "error: script needs to be run from project root './tools/slither/slither.sh'" - exit 1 -fi - -docker run --rm -v "$PWD":/src --workdir=/src trailofbits/eth-security-toolbox -c 'solc-select install 0.8.17 && solc-select use 0.8.17 && -slither-check-erc . SybelToken --solc-remaps @openzeppelin=node_modules/@openzeppelin && -slither-check-erc . SybelInternalTokens --solc-remaps @openzeppelin=node_modules/@openzeppelin' \ No newline at end of file diff --git a/tools/slither/flatten.sh b/tools/slither/flatten.sh deleted file mode 100755 index 531615c..0000000 --- a/tools/slither/flatten.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash - -# Assert we are on the root folder -if [ ! -d "contracts" ]; then - echo "error: script needs to be run from project root './tools/slither/flatten.sh'" - exit 1 -fi - -# Flatten only the contract we will use for echnide -docker run --rm -v "$PWD":/src --workdir=/src trailofbits/eth-security-toolbox -c 'solc-select install 0.8.17 && solc-select use 0.8.17 && -rm -r crytic-export/flattening/ && -slither-flat . --strategy LocalImport --solc-remaps @openzeppelin=node_modules/@openzeppelin --convert-external' \ No newline at end of file diff --git a/tools/slither/slither.sh b/tools/slither/slither.sh deleted file mode 100755 index be96d99..0000000 --- a/tools/slither/slither.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -# Assert we are on the root folder -if [ ! -d "contracts" ]; then - echo "error: script needs to be run from project root './tools/slither/slither.sh'" - exit 1 -fi - -docker run --rm -v "$(pwd)":/src --workdir=/src trailofbits/eth-security-toolbox -c 'solc-select install 0.8.17 && solc-select use 0.8.17 && -slither . --solc-args="--optimize" --solc-remaps @openzeppelin=node_modules/@openzeppelin' \ No newline at end of file