├── .github ├── ISSUE_TEMPLATE.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── build-frontend.yml │ ├── deploy-document.yml │ └── test-contract.yml ├── .gitignore ├── .vscode ├── extensions.json └── settings.json ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── biome.json ├── docs ├── e2e-tests │ ├── .gitkeep │ ├── issues │ │ ├── 00_index.md │ │ ├── 01_workspace_creation.md │ │ ├── 02_role_creation.md │ │ ├── 03_role_assignment.md │ │ ├── 04_assist_credit_transfer.md │ │ ├── 05_activity_view.md │ │ ├── 06_member_view.md │ │ ├── 07_credit_balance.md │ │ ├── 08_split_creation.md │ │ └── 09_split_view.md │ └── test_plan.md ├── img │ └── header.png └── puml │ ├── class-v2.puml │ ├── class.puml │ ├── sequence-v2.puml │ └── sequence.puml ├── lefthook.yaml ├── package.json ├── pkgs ├── cli │ ├── .env.example │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── scripts │ │ └── hat.ts │ ├── src │ │ ├── abi │ │ │ ├── bigbang.ts │ │ │ ├── fractiontoken.ts │ │ │ ├── hats.ts │ │ │ ├── hatsTimeFrameModule.ts │ │ │ └── splits.ts │ │ ├── commands │ │ │ ├── bigbang.ts │ │ │ ├── fractionToken.ts │ │ │ ├── hats.ts │ │ │ ├── pinata.ts │ │ │ ├── splits.ts │ │ │ └── wallet.ts │ │ ├── config.ts │ │ ├── image │ │ │ └── test.png │ │ ├── index.ts │ │ ├── modules │ │ │ ├── bigbang.ts │ │ │ ├── fractiontoken.ts │ │ │ ├── hatsProtocol.ts │ │ │ ├── splits.ts │ │ │ └── viem.ts │ │ └── services │ │ │ ├── loading.ts │ │ │ ├── pinata.ts │ │ │ └── wallet.ts │ └── tsconfig.json ├── contract │ ├── .env.example │ ├── .gitignore │ ├── .openzeppelin │ │ ├── holesky.json │ │ └── sepolia.json │ ├── .solhint.json │ ├── .solhintignore │ ├── README.md │ ├── contracts │ │ ├── bigbang │ │ │ ├── BigBang.sol │ │ │ ├── IHatsModuleFactory.sol │ │ │ └── mock │ │ │ │ └── BigBang_Mock_v2.sol │ │ ├── fractiontoken │ │ │ ├── FractionToken.sol │ │ │ ├── IFractionToken.sol │ │ │ └── mock │ │ │ │ └── FractionToken_Mock_v2.sol │ │ ├── hats │ │ │ ├── lib │ │ │ │ ├── ERC1155 │ │ │ │ │ └── ERC1155.sol │ │ │ │ └── utils │ │ │ │ │ └── Auth.sol │ │ │ ├── module │ │ │ │ ├── HatsModule.sol │ │ │ │ ├── HatsModuleFactory.sol │ │ │ │ └── IHatsModule.sol │ │ │ └── src │ │ │ │ ├── Hats.sol │ │ │ │ ├── HatsIdUtilities.sol │ │ │ │ └── Interfaces │ │ │ │ ├── HatsErrors.sol │ │ │ │ ├── HatsEvents.sol │ │ │ │ ├── IHats.sol │ │ │ │ ├── IHatsEligibility.sol │ │ │ │ ├── IHatsIdUtilities.sol │ │ │ │ └── IHatsToggle.sol │ │ ├── hatsmodules │ │ │ ├── hatcreator │ │ │ │ ├── HatsHatCreatorModule.sol │ │ │ │ └── IHatsHatCreatorModule.sol │ │ │ └── timeframe │ │ │ │ ├── HatsTimeFrameModule.sol │ │ │ │ └── IHatsTimeFrameModule.sol │ │ ├── splits │ │ │ ├── SplitsWarehouse.sol │ │ │ ├── interfaces │ │ │ │ ├── IERC165.sol │ │ │ │ ├── IERC6909.sol │ │ │ │ ├── IERC6909X.sol │ │ │ │ ├── IERC6909XCallback.sol │ │ │ │ ├── ISplitFactoryV2.sol │ │ │ │ ├── ISplitsWarehouse.sol │ │ │ │ └── IWETH9.sol │ │ │ ├── libraries │ │ │ │ ├── Cast.sol │ │ │ │ ├── Clone.sol │ │ │ │ ├── Math.sol │ │ │ │ └── SplitV2.sol │ │ │ ├── splitters │ │ │ │ ├── SplitFactoryV2.sol │ │ │ │ ├── SplitWalletV2.sol │ │ │ │ ├── pull │ │ │ │ │ ├── PullSplit.sol │ │ │ │ │ └── PullSplitFactory.sol │ │ │ │ └── push │ │ │ │ │ ├── PushSplit.sol │ │ │ │ │ └── PushSplitFactory.sol │ │ │ ├── tokens │ │ │ │ ├── ERC6909.sol │ │ │ │ └── ERC6909X.sol │ │ │ └── utils │ │ │ │ ├── ERC1271.sol │ │ │ │ ├── Nonces.sol │ │ │ │ ├── Ownable.sol │ │ │ │ ├── Pausable.sol │ │ │ │ ├── UnorderedNonces.sol │ │ │ │ └── Wallet.sol │ │ ├── splitscreator │ │ │ ├── ISplitsCreator.sol │ │ │ ├── ISplitsCreatorFactory.sol │ │ │ ├── SplitsCreator.sol │ │ │ ├── SplitsCreatorFactory.sol │ │ │ └── mock │ │ │ │ └── SplitsCreator_Mock_v2.sol │ │ └── utils │ │ │ └── Create2Deployer.sol │ ├── gas-report.txt │ ├── hardhat.config.ts │ ├── helpers │ │ ├── deploy │ │ │ ├── BigBang.ts │ │ │ ├── Create2Factory.ts │ │ │ ├── FractionToken.ts │ │ │ ├── Hats.ts │ │ │ ├── Splits.ts │ │ │ ├── Upgradeable.ts │ │ │ ├── contractJsonIgnitionHelper.ts │ │ │ ├── contractsJsonHelper.ts │ │ │ └── test.ts │ │ ├── ens │ │ │ ├── abi.ts │ │ │ ├── constants.ts │ │ │ └── function.ts │ │ ├── upgrade │ │ │ ├── bigbang.ts │ │ │ ├── fractionToken.ts │ │ │ └── splitsCreatorFactory.ts │ │ └── util │ │ │ └── sqrt.ts │ ├── ignition │ │ ├── deployments │ │ │ └── chain-11155111 │ │ │ │ ├── deployed_addresses.json │ │ │ │ └── journal.jsonl │ │ └── modules │ │ │ └── Lock.ts │ ├── outputs │ │ ├── contracts-holesky.json │ │ └── contracts-sepolia.json │ ├── package.json │ ├── scripts │ │ ├── createSplit.ts │ │ ├── deploy │ │ │ ├── all.ts │ │ │ └── create2.ts │ │ └── upgrade │ │ │ ├── bigbang.ts │ │ │ ├── fractionToken.ts │ │ │ ├── hatsHatCreatorModule.ts │ │ │ └── hatsTimeFrameModule.ts │ ├── tasks │ │ ├── BigBang │ │ │ └── bigbang.ts │ │ ├── HatsTimeFrameModule │ │ │ ├── getWoreTime.ts │ │ │ └── mintHat.ts │ │ ├── ens │ │ │ └── registerSubdomain.ts │ │ ├── index.ts │ │ └── utils │ │ │ ├── getBalance.ts │ │ │ ├── getChainInfo.ts │ │ │ ├── getContractAddress.ts │ │ │ └── resetContractAddressesJson.ts │ ├── test │ │ ├── BigBang.ts │ │ ├── FractionToken.ts │ │ ├── Hats.ts │ │ ├── HatsHatCreatorModule.ts │ │ ├── HatsTimeFrameModule.ts │ │ ├── IntegrationTest.ts │ │ ├── Splits.ts │ │ └── SplitsCreator.ts │ └── tsconfig.json ├── document │ ├── .gitignore │ ├── README.md │ ├── docs │ │ ├── Glossary.md │ │ ├── getstarted │ │ │ ├── admin │ │ │ │ ├── assign-role.md │ │ │ │ ├── create-role.md │ │ │ │ ├── create-splitter.md │ │ │ │ ├── create-workspace.md │ │ │ │ ├── distribute-rewards.md │ │ │ │ └── index.md │ │ │ ├── index.md │ │ │ └── member │ │ │ │ ├── assist-token.md │ │ │ │ ├── check-role.md │ │ │ │ ├── get-rewards.md │ │ │ │ ├── get-role.md │ │ │ │ └── index.md │ │ ├── howToUse.md │ │ ├── supportedNetworks.md │ │ └── welcome.md │ ├── docusaurus.config.ts │ ├── package.json │ ├── sidebars.ts │ ├── src │ │ ├── css │ │ │ └── custom.css │ │ └── pages │ │ │ ├── index.module.css │ │ │ └── index.tsx │ ├── static │ │ ├── .nojekyll │ │ └── img │ │ │ ├── banner.png │ │ │ ├── favicon.ico │ │ │ ├── logo.png │ │ │ └── toban-logo.svg │ └── tsconfig.json ├── frontend │ ├── .env.example │ ├── .eslintrc.cjs │ ├── .gitignore │ ├── README.md │ ├── abi │ │ ├── bigbang.ts │ │ ├── erc20.ts │ │ ├── fractiontoken.ts │ │ ├── hats.ts │ │ ├── hatsHatCreatorModule.ts │ │ ├── hatsTimeFrameModule.ts │ │ └── splits.ts │ ├── app │ │ ├── components │ │ │ ├── BasicButton.tsx │ │ │ ├── ContentContainer.tsx │ │ │ ├── Header.tsx │ │ │ ├── PageHeader.tsx │ │ │ ├── RoleAttributesList.tsx │ │ │ ├── SettingSections.tsx │ │ │ ├── StickyNav.tsx │ │ │ ├── SwitchNetwork.tsx │ │ │ ├── assistcredit │ │ │ │ ├── AmountSelector.tsx │ │ │ │ ├── History.tsx │ │ │ │ ├── SendConfirmation.tsx │ │ │ │ ├── Treemap.tsx │ │ │ │ ├── TreemapReceived.tsx │ │ │ │ ├── UserList.tsx │ │ │ │ ├── VerticalBar.tsx │ │ │ │ └── emojiConstants.ts │ │ │ ├── chakra-provider.tsx │ │ │ ├── common │ │ │ │ ├── CommonButton.tsx │ │ │ │ ├── CommonDialog.tsx │ │ │ │ ├── CommonIcon.tsx │ │ │ │ ├── CommonInput.tsx │ │ │ │ ├── CommonTextarea.tsx │ │ │ │ └── HatsListItemParser.tsx │ │ │ ├── icon │ │ │ │ ├── RoleIcon.tsx │ │ │ │ ├── UserIcon.tsx │ │ │ │ └── WorkspaceIcon.tsx │ │ │ ├── input │ │ │ │ ├── InputDescription.tsx │ │ │ │ ├── InputImage.tsx │ │ │ │ ├── InputLink.tsx │ │ │ │ ├── InputName.tsx │ │ │ │ └── InputNumber.tsx │ │ │ ├── roleAttributeDialog │ │ │ │ ├── AddRoleAttributeDialog.tsx │ │ │ │ ├── BaseRoleAttributeDialog.tsx │ │ │ │ └── EditRoleAttributeDialog.tsx │ │ │ ├── roles │ │ │ │ ├── HolderDetail.tsx │ │ │ │ ├── MyRole.tsx │ │ │ │ ├── RoleImageLibrarySelector.tsx │ │ │ │ ├── RoleTag.tsx │ │ │ │ ├── RoleWithBalance.tsx │ │ │ │ └── VRole.tsx │ │ │ ├── splits │ │ │ │ ├── SplitDetail.tsx │ │ │ │ └── SplitRecipientsList.tsx │ │ │ └── ui │ │ │ │ ├── avatar.tsx │ │ │ │ ├── button.tsx │ │ │ │ ├── checkbox.tsx │ │ │ │ ├── close-button.tsx │ │ │ │ ├── color-mode.tsx │ │ │ │ ├── dialog.tsx │ │ │ │ ├── drawer.tsx │ │ │ │ ├── field.tsx │ │ │ │ ├── input-group.tsx │ │ │ │ ├── menu.tsx │ │ │ │ ├── popover.tsx │ │ │ │ ├── provider.tsx │ │ │ │ ├── radio.tsx │ │ │ │ ├── slider.tsx │ │ │ │ └── tooltip.tsx │ │ ├── emotion │ │ │ ├── emotion-cache.ts │ │ │ ├── emotion-client.tsx │ │ │ └── emotion-server.tsx │ │ ├── entry.client.tsx │ │ ├── entry.server.tsx │ │ ├── root.tsx │ │ └── routes │ │ │ ├── $treeId._index.tsx │ │ │ ├── $treeId_.$hatId.tsx │ │ │ ├── $treeId_.$hatId_.$address.tsx │ │ │ ├── $treeId_.$hatId_.$address_.assistcredit.send.tsx │ │ │ ├── $treeId_.$hatId_.assign.tsx │ │ │ ├── $treeId_.$hatId_.edit.tsx │ │ │ ├── $treeId_.assistcredit._index.tsx │ │ │ ├── $treeId_.assistcredit.history.tsx │ │ │ ├── $treeId_.member.tsx │ │ │ ├── $treeId_.member_.$address.tsx │ │ │ ├── $treeId_.roles_.new.tsx │ │ │ ├── $treeId_.settings.tsx │ │ │ ├── $treeId_.splits._index.tsx │ │ │ ├── $treeId_.splits.new.tsx │ │ │ ├── _index.tsx │ │ │ ├── api.namestone.$action.tsx │ │ │ ├── login.tsx │ │ │ ├── signup.tsx │ │ │ ├── transaction.tsx │ │ │ ├── workspace._index.tsx │ │ │ └── workspace.new.tsx │ ├── codegen.ts │ ├── cypress.config.ts │ ├── cypress │ │ ├── e2e │ │ │ ├── basic.cy.ts │ │ │ └── workspace-creation.cy.ts │ │ ├── fixtures │ │ │ └── images │ │ │ │ ├── user_sample.png │ │ │ │ └── workspace_sample.png │ │ └── support │ │ │ ├── e2e.ts │ │ │ ├── page-objects │ │ │ └── WorkspaceCreationPage.ts │ │ │ ├── utils │ │ │ └── TestDataGenerator.ts │ │ │ └── wallet.setup.ts │ ├── gql │ │ ├── fragment-masking.ts │ │ ├── gql.ts │ │ ├── graphql.schema.json │ │ ├── graphql.ts │ │ └── index.ts │ ├── graphql.schema.json │ ├── hooks │ │ ├── useBigBang.ts │ │ ├── useContracts.ts │ │ ├── useCopyToClipboard.ts │ │ ├── useENS.ts │ │ ├── useFractionToken.ts │ │ ├── useHats.ts │ │ ├── useHatsHatCreatorModule.ts │ │ ├── useHatsTimeFrameModule.ts │ │ ├── useIpfs.ts │ │ ├── useSplitsCreator.ts │ │ ├── useViem.ts │ │ ├── useWallet.ts │ │ └── useWorkspace.ts │ ├── package.json │ ├── public │ │ └── images │ │ │ ├── favicon.ico │ │ │ ├── imagelib │ │ │ ├── rpg1.png │ │ │ ├── rpg2.png │ │ │ ├── rpg3.png │ │ │ └── rpg4.png │ │ │ ├── lp │ │ │ ├── people-deco.svg │ │ │ └── wave-deco.svg │ │ │ ├── toban-logo-text.svg │ │ │ └── toban-logo.svg │ ├── tsconfig.json │ ├── types │ │ └── hats.ts │ ├── utils │ │ ├── apollo.ts │ │ ├── ipfs.ts │ │ ├── splits.ts │ │ └── wallet.ts │ └── vite.config.ts └── subgraph │ ├── .gitignore │ ├── README.md │ ├── abis │ ├── BigBang.json │ ├── FractionToken.json │ ├── HatsHatCreatorModule.json │ └── HatsTimeFrameModule.json │ ├── config │ ├── base.json │ └── sepolia.json │ ├── package.json │ ├── schema.graphql │ ├── src │ ├── bigbangMapping.ts │ ├── fractionTokenMapping.ts │ ├── hatsModuleMapping.ts │ └── helper │ │ └── hat.ts │ └── subgraph.template.yaml ├── pnpm-lock.yaml └── pnpm-workspace.yaml /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # Issue 2 | 3 | ## Description 4 | 5 | Please provide a clear and concise description of the issue. 6 | 7 | ## Notes 8 | 9 | ## Screenshots 10 | 11 | If applicable, add screenshots to help explain your problem. 12 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # Pull Request Template 2 | 3 | ## Description 4 | 5 | Please include a summary of the changes and the related issue. Please also include relevant motivation and context. List any dependencies that are required for this change. 6 | 7 | Fixes # (issue) 8 | 9 | ## Type of change 10 | 11 | Please delete options that are not relevant. 12 | 13 | - [ ] Bug fix (non-breaking change which fixes an issue) 14 | - [ ] New feature (non-breaking change which adds functionality) 15 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) 16 | - [ ] This change requires a documentation update 17 | 18 | ## Screenshots 19 | 20 | If applicable, add screenshots to help explain your problem. 21 | -------------------------------------------------------------------------------- /.github/workflows/build-frontend.yml: -------------------------------------------------------------------------------- 1 | name: Build Frontend(Vite + Remix) 2 | 3 | on: 4 | # mainブランチにプッシュされたとき、またはプルリクエストが作成されたときに実行 5 | push: 6 | branches: 7 | - main 8 | pull_request: 9 | branches: 10 | - main 11 | 12 | jobs: 13 | test: 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - name: Checkout repository 18 | uses: actions/checkout@v3 19 | 20 | - name: Setup Node.js 21 | uses: actions/setup-node@v3 22 | with: 23 | node-version: "20" 24 | 25 | - name: Install pnpm 26 | uses: pnpm/action-setup@v2 27 | with: 28 | version: 8.15.4 29 | 30 | - name: Install dependencies 31 | run: pnpm install 32 | 33 | - name: Build Frontend 34 | run: pnpm frontend build 35 | -------------------------------------------------------------------------------- /.github/workflows/deploy-document.yml: -------------------------------------------------------------------------------- 1 | name: Deploy document to GitHub Pages 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | # build job 10 | build: 11 | name: Build 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4 15 | - uses: actions/setup-node@v4 16 | with: 17 | node-version: 20 18 | 19 | - name: Install pnpm 20 | uses: pnpm/action-setup@v2 21 | with: 22 | version: 8.15.4 23 | 24 | - name: Install dependencies 25 | run: pnpm install 26 | 27 | - name: Build Document 28 | run: pnpm document build 29 | 30 | - name: Upload Build Artifact 31 | uses: actions/upload-pages-artifact@v3 32 | with: 33 | path: pkgs/document/build 34 | # deploy job 35 | deploy: 36 | name: Deploy to GitHub Pages 37 | needs: build 38 | permissions: 39 | pages: write 40 | id-token: write 41 | environment: 42 | name: github-pages 43 | url: ${{ steps.deployment.outputs.page_url }} 44 | runs-on: ubuntu-latest 45 | steps: 46 | - name: Deploy Document 47 | id: deployment 48 | uses: actions/deploy-pages@v4 49 | 50 | concurrency: 51 | group: "pages" 52 | cancel-in-progress: false 53 | -------------------------------------------------------------------------------- /.github/workflows/test-contract.yml: -------------------------------------------------------------------------------- 1 | name: Test Contracts 2 | 3 | on: 4 | # mainブランチにプッシュされたとき、またはプルリクエストが作成されたときに実行 5 | push: 6 | branches: 7 | - main 8 | pull_request: 9 | branches: 10 | - main 11 | 12 | jobs: 13 | test: 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - name: Checkout repository 18 | uses: actions/checkout@v3 19 | 20 | - name: Setup Node.js 21 | uses: actions/setup-node@v3 22 | with: 23 | node-version: "20" 24 | 25 | - name: Install pnpm 26 | uses: pnpm/action-setup@v2 27 | with: 28 | version: 8.15.4 29 | 30 | - name: Install dependencies 31 | run: pnpm install 32 | working-directory: ./pkgs/contract 33 | 34 | - name: Lint Check 35 | run: pnpm lint 36 | working-directory: ./pkgs/contract 37 | 38 | - name: Run Hardhat tests 39 | run: | 40 | npx hardhat test > ./test-results.txt 41 | echo "\`\`\`\n$(cat ./test-results.txt)" > ./comments 42 | working-directory: ./pkgs/contract 43 | env: 44 | PRIVATE_KEY: "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" 45 | 46 | # - name: Generate Gas Report 47 | # run: | 48 | # npx hardhat test --network hardhat > ./gas-report.txt 49 | # echo "\n### Gas Report" >> ./comments 50 | # echo "\`\`\`\n$(cat ./gas-report.txt)" >> ./comments 51 | # working-directory: ./pkgs/contract 52 | 53 | # - name: Upload Gas Report as Artifact 54 | # uses: actions/upload-artifact@v3 55 | # with: 56 | # name: gas-report 57 | # path: ./pkgs/contract/gas-report.txt 58 | 59 | # # GitHub PRにテスト結果とガスレポートをコメントとして投稿 60 | # - name: Post comments 61 | # env: 62 | # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 63 | # URL: ${{ github.event.pull_request.html_url }} 64 | # run: gh pr comment -F ./comments "${URL}" 65 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/.DS_Store 2 | node_modules 3 | **/.env 4 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "biomejs.biome", 4 | "NomicFoundation.hardhat-solidity", 5 | "tintinweb.solidity-visual-auditor", 6 | "jebbs.plantuml", 7 | "bradlc.vscode-tailwindcss" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true, 3 | "editor.defaultFormatter": "biomejs.biome", 4 | "[typescript]": { 5 | "editor.defaultFormatter": "biomejs.biome", 6 | "editor.codeActionsOnSave": { 7 | "source.fixAll.eslint": "explicit" 8 | } 9 | }, 10 | "[plantuml]": { 11 | "editor.defaultFormatter": "jebbs.plantuml" 12 | }, 13 | "files.insertFinalNewline": true, 14 | "files.trimTrailingWhitespace": true 15 | } 16 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # 貢献の仕方 2 | 3 | コントリビューターの皆様、本プロジェクトにご参加いただきありがとうございます!! 4 | 本サイトの開発に参加してくださる方に対して、参加方法をお伝えします。 5 | 6 | ## Issue へのコメントや Pull Request について 7 | 8 | - Issue へのコメントはご自由にどうぞ!新しい質問や提案なども受け付けます。 9 | - Issue を追加する場合、必ず既に同様の Issue が無いか検索をしてから作成してください。 10 | - Pull Request を送る場合、必ず対応する Issue 番号を追記してください。単独の Pull Request は受け付けません。 11 | - Issue については必ず反映できると限りませんのでご了承ください。 12 | - 文言変更や見た目の大幅な変更といった、コンテンツの意味を変えるものについては、関係者の確認が必要です。採用される可能性もあまり高くないことをご了承ください。 13 | - good first issue / help wanted / bug を優先して対応いただけると助かります。 14 | 15 | ## コミュニケーションへの参加方法 16 | 17 | Hackdays project の Discord サーバーにてコミュニケーションをとっています。 18 | 19 | - Hackdays project の Discord サーバーに入っていない場合、[こちらからご参加ください](https://discord.com/invite/4hJefCEYKS)。 20 | - Toban のカテゴリにはいっているチャンネルで連絡を取っています。 21 | 22 | ## 参加にあたって 23 | 24 | - コミュニケーションにあたっては、Code for Japan の [行動規範](https://github.com/codeforjapan/codeofconduct) もご確認ください。 25 | - Toban は Hackdays project のひとつのプロダクトです。Hackdays project の目指すところや、コミュニティについては[こちらも適宜ご参照ください](https://hackdays.notion.site/HackDays-Onboarding-e49abeee55354d689083cf08051cd022?pvs=4)。 26 | - 自分ができそうな Issue に誰もアサインされていない場合、Issue に「やります!」等とコメントしてから開発をはじめてください。 27 | - 1 週間以上作業から離れそうな場合は、他の人が作業を引き継げるようにしておいてください。 28 | - 1 週間以上更新されない Issue については、当方で assign を外させていただくことがあります。作業途中でも、[Draft Pull Request](https://qiita.com/tatane616/items/13da1b6797a7b871ad58) を送ってもらえると、動きが把握しやすくなります。 29 | - Issue に関連した質問等は、Slack より Issue 内のコメントを活用しましょう 30 | - 提案なども受け付けます!積極的に新しく Issue を作ってください。 31 | 32 | 本 ドキュメント の更新も大歓迎です! 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Hackdays 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /biome.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json", 3 | "vcs": { 4 | "enabled": false, 5 | "clientKind": "git", 6 | "useIgnoreFile": false 7 | }, 8 | "files": { 9 | "ignoreUnknown": false, 10 | "ignore": [ 11 | "**/node_modules", 12 | "**/build", 13 | "**/dist", 14 | "**/coverage", 15 | "**/temp", 16 | "**/tmp", 17 | "**/out", 18 | "**/target", 19 | "**/public", 20 | "**/docs", 21 | "**/contract/artifacts", 22 | "**/contract/cache", 23 | "**/contract/typechain-types", 24 | "**/contract/ignition/deployments", 25 | "**/contract/test", 26 | "**/subgraph/generated", 27 | "**/subgraph/build", 28 | "**/frontend/gql", 29 | "**/frontend/**/downloads" 30 | ] 31 | }, 32 | "formatter": { 33 | "enabled": true, 34 | "indentStyle": "space" 35 | }, 36 | "organizeImports": { 37 | "enabled": true 38 | }, 39 | "linter": { 40 | "enabled": true, 41 | "rules": { 42 | "recommended": true 43 | }, 44 | "ignore": ["**/subgraph/src/**", "**/contract/test/**"] 45 | }, 46 | "javascript": { 47 | "formatter": { 48 | "quoteStyle": "double" 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /docs/e2e-tests/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackdays-io/toban/1ffd398b45703d2b9cf6117a5ca161489f3c3663/docs/e2e-tests/.gitkeep -------------------------------------------------------------------------------- /docs/e2e-tests/issues/00_index.md: -------------------------------------------------------------------------------- 1 | # E2Eテスト実装計画: 個別テストケース一覧 2 | 3 | このディレクトリには、[E2Eテスト実装計画: 主要ユーザーフロー (PR #362)](https://github.com/hackdays-io/toban/pull/362) に基づいた個別のテストケース実装計画が含まれています。 4 | 5 | ## テストケース一覧 6 | 7 | 1. [ワークスペース作成フロー](./01_workspace_creation.md) 8 | 2. [役割作成フロー](./02_role_creation.md) 9 | 3. [役割割り当てフロー](./03_role_assignment.md) 10 | 4. [アシストクレジット送信フロー](./04_assist_credit_transfer.md) 11 | 5. [アクティビティ履歴閲覧フロー](./05_activity_view.md) 12 | 6. [メンバー情報閲覧フロー](./06_member_view.md) 13 | 7. [アシストクレジット残高確認フロー](./07_credit_balance.md) 14 | 8. [スプリット作成フロー](./08_split_creation.md) 15 | 9. [スプリット閲覧フロー](./09_split_view.md) 16 | 17 | ## ウォレット署名が必要なフロー 18 | 19 | 以下のフローではMetaMaskウォレットでの署名プロセスのテストが必要です: 20 | 21 | 1. **ワークスペース作成フロー** 22 | 2. **役割作成フロー** 23 | 3. **役割割り当てフロー** 24 | 4. **アシストクレジット送信フロー** 25 | 5. **スプリット作成フロー** 26 | 27 | ## 優先順位 28 | 29 | テストケースの実装優先順位: 30 | 31 | 1. ワークスペース作成フロー(基本機能) 32 | 2. 役割作成フロー(基本組織管理) 33 | 3. 役割割り当てフロー(基本組織管理) 34 | 4. アシストクレジット送信フロー(主要機能) 35 | 5. スプリット作成フロー(重要機能) 36 | 6. アシストクレジット残高確認フロー(情報確認) 37 | 7. アクティビティ履歴閲覧フロー(情報確認) 38 | 8. メンバー情報閲覧フロー(情報確認) 39 | 9. スプリット閲覧フロー(情報確認) 40 | 41 | ## 関連PR 42 | 43 | - [E2Eテストコードのリファクタリング (PR #361)](https://github.com/hackdays-io/toban/pull/361) 44 | - [E2Eテスト実装計画: 主要ユーザーフロー (PR #362)](https://github.com/hackdays-io/toban/pull/362) 45 | -------------------------------------------------------------------------------- /docs/e2e-tests/issues/01_workspace_creation.md: -------------------------------------------------------------------------------- 1 | # E2Eテスト実装: ワークスペース作成フロー 2 | 3 | ## 概要 4 | 新しいワークスペースを作成する基本的なユーザーフローのE2Eテストを実装します。 5 | 6 | ## 詳細 7 | ワークスペース作成は、Tobanアプリケーションの最初のステップであり、ユーザーが新しい組織を設定するための基本的なフローです。 8 | 9 | ### テストステップ 10 | 1. ワークスペース一覧ページにアクセスする 11 | 2. 「新しいワークスペースを作成」ボタンをクリックする 12 | 3. ワークスペース作成フォームが表示されることを確認する 13 | 4. ワークスペース画像をアップロードする 14 | 5. ワークスペース名を入力する 15 | 6. ワークスペースの説明を入力する 16 | 7. 「作成」ボタンをクリックする 17 | 8. **MetaMaskウォレットでの署名要求が表示されることを確認する** 18 | 9. **署名を承認する** 19 | 10. 新しく作成されたワークスペースのホームページに遷移することを確認する 20 | 11. 作成したワークスペースの情報(名前、説明、画像)が正しく表示されていることを確認する 21 | 22 | ### 期待される結果 23 | - ワークスペースが正常に作成される 24 | - 作成後、そのワークスペースのホームページに自動的に遷移する 25 | - ワークスペースの情報が正しく保存され表示される 26 | 27 | ## 技術的な実装ポイント 28 | - MetaMaskウォレット連携の確認 29 | - 画像アップロード機能のテスト 30 | - フォームバリデーションのテスト 31 | - ページ遷移の確認 32 | - データの永続化の確認 33 | 34 | ## 実装方針 35 | 1. ページオブジェクトパターンを使用して、ワークスペース作成ページの操作をカプセル化する 36 | 2. Synpressを使用してMetaMaskウォレットとの連携をテストする 37 | 3. 画像アップロードのモックを作成する 38 | 4. フォームバリデーションのエラーケースもテストする 39 | 40 | ## 優先度 41 | 高(基本的なユーザーフロー) 42 | 43 | ## 関連リソース 44 | - [既存のE2Eテスト](https://github.com/hackdays-io/toban/tree/main/pkgs/frontend/cypress) 45 | - [Synpress ドキュメント](https://github.com/Synthetixio/synpress) 46 | -------------------------------------------------------------------------------- /docs/e2e-tests/issues/02_role_creation.md: -------------------------------------------------------------------------------- 1 | # E2Eテスト実装: 役割作成フロー 2 | 3 | ## 概要 4 | ワークスペース内で新しい役割を作成するフローのE2Eテストを実装します。 5 | 6 | ## 詳細 7 | 役割作成は、ワークスペース内での組織構造を定義するための重要なフローです。 8 | 9 | ### テストステップ 10 | 1. ワークスペースのホームページにアクセスする 11 | 2. 役割一覧セクションの「役割を追加」ボタンをクリックする 12 | 3. 役割作成フォームが表示されることを確認する 13 | 4. 役割の画像をアップロードする 14 | 5. 役割の名前を入力する 15 | 6. 役割の説明を入力する 16 | 7. 役割が遂行するべきタスクを入力する 17 | 8. 役割が持つべき権限を選択する 18 | 9. 「作成」ボタンをクリックする 19 | 10. **MetaMaskウォレットでの署名要求が表示されることを確認する** 20 | 11. **署名を承認する** 21 | 12. 役割が正常に作成され、役割一覧に表示されることを確認する 22 | 13. 作成した役割の詳細ページにアクセスし、情報が正しく表示されることを確認する 23 | 24 | ### 期待される結果 25 | - 役割が正常に作成される 26 | - 作成した役割が役割一覧に表示される 27 | - 役割の詳細情報が正しく保存され表示される 28 | 29 | ## 技術的な実装ポイント 30 | - フォームバリデーションのテスト 31 | - 画像アップロード機能のテスト 32 | - 権限設定の正確性の確認 33 | - データの永続化の確認 34 | - MetaMaskウォレット連携と署名プロセスのテスト 35 | 36 | ## 実装方針 37 | 1. ページオブジェクトパターンを使用して、役割作成ページの操作をカプセル化する 38 | 2. Synpressを使用してMetaMaskウォレットとの連携をテストする 39 | 3. 画像アップロードのモックを作成する 40 | 4. 権限選択のインタラクションをテストする 41 | 5. フォームバリデーションのエラーケースもテストする 42 | 43 | ## 優先度 44 | 高(基本的な組織管理フロー) 45 | 46 | ## 関連リソース 47 | - [既存のE2Eテスト](https://github.com/hackdays-io/toban/tree/main/pkgs/frontend/cypress) 48 | - [Synpress ドキュメント](https://github.com/Synthetixio/synpress) 49 | -------------------------------------------------------------------------------- /docs/e2e-tests/issues/03_role_assignment.md: -------------------------------------------------------------------------------- 1 | # E2Eテスト実装: 役割割り当てフロー 2 | 3 | ## 概要 4 | 作成した役割をメンバーに割り当てるフローのE2Eテストを実装します。 5 | 6 | ## 詳細 7 | 役割割り当ては、組織内のメンバーに責任と権限を付与するための重要なフローです。 8 | 9 | ### テストステップ 10 | 1. ワークスペースのホームページにアクセスする 11 | 2. 役割一覧から特定の役割を選択し、詳細ページに移動する 12 | 3. 「役割を渡す」ボタンをクリックする 13 | 4. 役割割り当てフォームが表示されることを確認する 14 | 5. 割り当て先ユーザーのユーザー名またはウォレットアドレスを入力する 15 | 6. 開始日を選択する 16 | 7. 「アサイン」ボタンをクリックする 17 | 8. **MetaMaskウォレットでの署名要求が表示されることを確認する** 18 | 9. **署名を承認する** 19 | 10. 役割が正常に割り当てられたことを確認するメッセージが表示されることを確認する 20 | 11. 役割詳細ページに戻り、割り当てられたメンバーが表示されることを確認する 21 | 12. 割り当てられたメンバーのアカウントでログインし、自分のワークスペースホームで役割が表示されることを確認する 22 | 23 | ### 期待される結果 24 | - 役割が指定したメンバーに正常に割り当てられる 25 | - 割り当てられたメンバーが役割詳細ページに表示される 26 | - 割り当てられたメンバーが自分のアカウントで役割を確認できる 27 | 28 | ## 技術的な実装ポイント 29 | - ユーザー検索機能のテスト 30 | - 日付選択コンポーネントのテスト 31 | - ウォレットアドレス入力のバリデーション 32 | - 複数アカウント間のデータ共有の確認 33 | - 権限の適切な移譲の確認 34 | - MetaMaskウォレット連携と署名プロセスのテスト 35 | 36 | ## 実装方針 37 | 1. ページオブジェクトパターンを使用して、役割割り当てページの操作をカプセル化する 38 | 2. Synpressを使用してMetaMaskウォレットとの連携をテストする 39 | 3. 複数のユーザーアカウントを使用したテストシナリオを作成する 40 | 4. 日付選択コンポーネントの操作をテストする 41 | 5. ウォレットアドレス入力のバリデーションをテストする 42 | 43 | ## 優先度 44 | 高(基本的な組織管理フロー) 45 | 46 | ## 関連リソース 47 | - [既存のE2Eテスト](https://github.com/hackdays-io/toban/tree/main/pkgs/frontend/cypress) 48 | - [Synpress ドキュメント](https://github.com/Synthetixio/synpress) 49 | -------------------------------------------------------------------------------- /docs/e2e-tests/issues/04_assist_credit_transfer.md: -------------------------------------------------------------------------------- 1 | # E2Eテスト実装: アシストクレジット送信フロー 2 | 3 | ## 概要 4 | メンバー間でアシストクレジットを送信するフローのE2Eテストを実装します。 5 | 6 | ## 詳細 7 | アシストクレジット送信は、メンバー間の貢献を記録し、報酬を分配するための主要な機能です。 8 | 9 | ### テストステップ 10 | 1. ワークスペースのホームページにアクセスする 11 | 2. 自分の役割セクションから「アシストクレジットを送る」ボタンをクリックする 12 | 3. クレジット送信フォームが表示されることを確認する 13 | 4. 送信先ユーザーのユーザー名またはウォレットアドレスを入力する 14 | 5. ワークスペースメンバーリストから送信先ユーザーを選択する 15 | 6. 送信量を入力する 16 | 7. 「次へ」ボタンをクリックする 17 | 8. 送信内容確認画面が表示されることを確認する 18 | 9. 送信アイテムアイコンを下から上にスワイプする 19 | 10. **MetaMaskウォレットでの署名要求が表示されることを確認する** 20 | 11. **署名を承認する** 21 | 12. 送信完了メッセージが表示されることを確認する 22 | 13. 送信者と受信者の両方のアカウントでクレジット残高が正しく更新されていることを確認する 23 | 24 | ### 期待される結果 25 | - アシストクレジットが正常に送信される 26 | - 送信者のクレジット残高が減少する 27 | - 受信者のクレジット残高が増加する 28 | - トランザクション履歴に送信記録が表示される 29 | 30 | ## 技術的な実装ポイント 31 | - ユーザー検索機能のテスト 32 | - 数値入力と上限チェックのテスト 33 | - スワイプジェスチャーの動作確認 34 | - MetaMaskウォレット連携と署名プロセスのテスト 35 | - ブロックチェーントランザクションの確認 36 | - 残高更新の確認 37 | 38 | ## 実装方針 39 | 1. ページオブジェクトパターンを使用して、クレジット送信ページの操作をカプセル化する 40 | 2. Synpressを使用してMetaMaskウォレットとの連携をテストする 41 | 3. スワイプジェスチャーのシミュレーションを実装する 42 | 4. 複数のユーザーアカウントを使用したテストシナリオを作成する 43 | 5. トランザクション完了後の状態確認を実装する 44 | 45 | ## 優先度 46 | 高(主要な機能フロー) 47 | 48 | ## 関連リソース 49 | - [既存のE2Eテスト](https://github.com/hackdays-io/toban/tree/main/pkgs/frontend/cypress) 50 | - [Synpress ドキュメント](https://github.com/Synthetixio/synpress) 51 | -------------------------------------------------------------------------------- /docs/e2e-tests/issues/05_activity_view.md: -------------------------------------------------------------------------------- 1 | # E2Eテスト実装: アクティビティ履歴閲覧フロー 2 | 3 | ## 概要 4 | ワークスペース内のアクティビティ履歴を閲覧するフローのE2Eテストを実装します。 5 | 6 | ## 詳細 7 | アクティビティ履歴閲覧は、ワークスペース内での活動を追跡し、透明性を確保するための重要な機能です。 8 | 9 | ### テストステップ 10 | 1. ワークスペースのホームページにアクセスする 11 | 2. 「直近のアクティビティ」セクションの「もっと見る」ボタンをクリックする 12 | 3. アクティビティ履歴ページが表示されることを確認する 13 | 4. アシストクレジットの送付履歴が表示されることを確認する 14 | 5. 特定のアクティビティの詳細情報(送信者、受信者、量)が正しく表示されることを確認する 15 | 6. グラフタブをクリックする 16 | 7. 組織全体のアシストクレジットの保有量や流通量を示すグラフが表示されることを確認する 17 | 8. 期間フィルターを操作し、異なる期間のデータが表示されることを確認する 18 | 9. ページネーションを操作し、過去のアクティビティが表示されることを確認する 19 | 20 | ### 期待される結果 21 | - アクティビティ履歴が正しく表示される 22 | - グラフが正確にデータを表示する 23 | - フィルターが正常に機能する 24 | - ページネーションが正常に機能する 25 | 26 | ## 技術的な実装ポイント 27 | - データ取得と表示のテスト 28 | - グラフコンポーネントの表示確認 29 | - フィルター機能の動作確認 30 | - ページネーションの動作確認 31 | - 大量のデータがある場合のパフォーマンス確認 32 | 33 | ## 実装方針 34 | 1. ページオブジェクトパターンを使用して、アクティビティ履歴ページの操作をカプセル化する 35 | 2. APIレスポンスのモックを作成して、様々なデータパターンをテストする 36 | 3. グラフコンポーネントの表示確認を実装する 37 | 4. フィルター操作とその結果の検証を実装する 38 | 5. ページネーション操作とその結果の検証を実装する 39 | 40 | ## 優先度 41 | 中(情報確認フロー) 42 | 43 | ## 関連リソース 44 | - [既存のE2Eテスト](https://github.com/hackdays-io/toban/tree/main/pkgs/frontend/cypress) 45 | - [Cypress ドキュメント](https://docs.cypress.io/guides/references/best-practices) 46 | -------------------------------------------------------------------------------- /docs/e2e-tests/issues/06_member_view.md: -------------------------------------------------------------------------------- 1 | # E2Eテスト実装: メンバー情報閲覧フロー 2 | 3 | ## 概要 4 | ワークスペース内のメンバー情報を閲覧するフローのE2Eテストを実装します。 5 | 6 | ## 詳細 7 | メンバー情報閲覧は、組織内のメンバーと役割の関係を把握するための重要な機能です。 8 | 9 | ### テストステップ 10 | 1. ワークスペースのホームページにアクセスする 11 | 2. ボトムメニューの「メンバー」タブをクリックする 12 | 3. メンバーページが表示されることを確認する 13 | 4. ロールを持っているメンバーのリストが表示されることを確認する 14 | 5. アシストクレジットを受け取ったメンバーのリストが表示されることを確認する 15 | 6. 特定のメンバーをクリックする 16 | 7. メンバー詳細ページが表示されることを確認する 17 | 8. メンバーの基本情報(名前、プロフィール画像)が表示されることを確認する 18 | 9. メンバーが持つ役割の一覧が表示されることを確認する 19 | 10. メンバーのアシストクレジット情報が表示されることを確認する 20 | 21 | ### 期待される結果 22 | - メンバーリストが正しく表示される 23 | - メンバーの詳細情報が正確に表示される 24 | - 役割とアシストクレジット情報が正確に表示される 25 | 26 | ## 技術的な実装ポイント 27 | - データ取得と表示のテスト 28 | - リストのフィルタリング機能の確認 29 | - メンバー詳細ページへの遷移確認 30 | - 大量のメンバーがいる場合のパフォーマンス確認 31 | 32 | ## 実装方針 33 | 1. ページオブジェクトパターンを使用して、メンバーページの操作をカプセル化する 34 | 2. APIレスポンスのモックを作成して、様々なデータパターンをテストする 35 | 3. リストのフィルタリング操作とその結果の検証を実装する 36 | 4. メンバー詳細ページへの遷移と情報表示の確認を実装する 37 | 38 | ## 優先度 39 | 中(情報確認フロー) 40 | 41 | ## 関連リソース 42 | - [既存のE2Eテスト](https://github.com/hackdays-io/toban/tree/main/pkgs/frontend/cypress) 43 | - [Cypress ドキュメント](https://docs.cypress.io/guides/references/best-practices) 44 | -------------------------------------------------------------------------------- /docs/e2e-tests/issues/07_credit_balance.md: -------------------------------------------------------------------------------- 1 | # E2Eテスト実装: アシストクレジット残高確認フロー 2 | 3 | ## 概要 4 | ユーザーのアシストクレジット残高を確認するフローのE2Eテストを実装します。 5 | 6 | ## 詳細 7 | アシストクレジット残高確認は、ユーザーが自分の貢献度と報酬を把握するための重要な機能です。 8 | 9 | ### テストステップ 10 | 1. ワークスペースのホームページにアクセスする 11 | 2. ボトムメニューの「アシストクレジット」タブをクリックする 12 | 3. アシストクレジットページが表示されることを確認する 13 | 4. 保有しているアシストクレジットの一覧が表示されることを確認する 14 | 5. 各クレジットの残高が正しく表示されることを確認する 15 | 6. 受け取り元の情報が正しく表示されることを確認する 16 | 7. 特定のクレジットの「送る」ボタンをクリックする 17 | 8. クレジット送信フォームに遷移することを確認する 18 | 19 | ### 期待される結果 20 | - アシストクレジットの残高が正確に表示される 21 | - 受け取り元の情報が正確に表示される 22 | - クレジット送信フォームへの遷移が正常に機能する 23 | 24 | ## 技術的な実装ポイント 25 | - データ取得と表示のテスト 26 | - 残高計算の正確性確認 27 | - クレジット送信フォームへの遷移確認 28 | - 複数のクレジットタイプがある場合の表示確認 29 | 30 | ## 実装方針 31 | 1. ページオブジェクトパターンを使用して、アシストクレジットページの操作をカプセル化する 32 | 2. APIレスポンスのモックを作成して、様々なデータパターンをテストする 33 | 3. 残高表示の正確性を検証する 34 | 4. クレジット送信フォームへの遷移を確認する 35 | 36 | ## 優先度 37 | 中(情報確認フロー) 38 | 39 | ## 関連リソース 40 | - [既存のE2Eテスト](https://github.com/hackdays-io/toban/tree/main/pkgs/frontend/cypress) 41 | - [Cypress ドキュメント](https://docs.cypress.io/guides/references/best-practices) 42 | -------------------------------------------------------------------------------- /docs/e2e-tests/issues/08_split_creation.md: -------------------------------------------------------------------------------- 1 | # E2Eテスト実装: スプリット作成フロー 2 | 3 | ## 概要 4 | 報酬分配のためのスプリットを作成するフローのE2Eテストを実装します。 5 | 6 | ## 詳細 7 | スプリット作成は、メンバーへの報酬分配を設定するための重要な機能です。 8 | 9 | ### テストステップ 10 | 1. ワークスペースのホームページにアクセスする 11 | 2. ボトムメニューの「スプリッツ」タブをクリックする 12 | 3. スプリッツページが表示されることを確認する 13 | 4. 「クリエイトニュー」ボタンをクリックする 14 | 5. スプリット作成フォームが表示されることを確認する 15 | 6. スプリッターの名前を入力する 16 | 7. 各役割に分配係数を設定する 17 | 8. 分配対象外にしたい役割のチェックボックスを外す 18 | 9. 詳細設定を開き、特定のメンバーのチェックボックスを外す 19 | 10. 「プレビュー」ボタンをクリックする 20 | 11. 分配プレビューが表示され、各メンバーへの分配額が正しく計算されていることを確認する 21 | 12. 「作成」ボタンをクリックする 22 | 13. **MetaMaskウォレットでの署名要求が表示されることを確認する** 23 | 14. **署名を承認する** 24 | 15. スプリットが正常に作成され、スプリット一覧に表示されることを確認する 25 | 26 | ### 期待される結果 27 | - スプリットが正常に作成される 28 | - 分配設定が正確に保存される 29 | - 作成したスプリットがスプリット一覧に表示される 30 | 31 | ## 技術的な実装ポイント 32 | - フォーム入力と検証のテスト 33 | - 分配係数の計算確認 34 | - チェックボックス操作の確認 35 | - プレビュー表示の正確性確認 36 | - MetaMaskウォレット連携と署名プロセスのテスト 37 | - スマートコントラクト作成の確認 38 | 39 | ## 実装方針 40 | 1. ページオブジェクトパターンを使用して、スプリット作成ページの操作をカプセル化する 41 | 2. Synpressを使用してMetaMaskウォレットとの連携をテストする 42 | 3. 分配係数の入力と計算結果の検証を実装する 43 | 4. チェックボックス操作とその結果の検証を実装する 44 | 5. プレビュー表示の正確性を検証する 45 | 46 | ## 優先度 47 | 高(重要機能) 48 | 49 | ## 関連リソース 50 | - [既存のE2Eテスト](https://github.com/hackdays-io/toban/tree/main/pkgs/frontend/cypress) 51 | - [Synpress ドキュメント](https://github.com/Synthetixio/synpress) 52 | -------------------------------------------------------------------------------- /docs/e2e-tests/issues/09_split_view.md: -------------------------------------------------------------------------------- 1 | # E2Eテスト実装: スプリット閲覧フロー 2 | 3 | ## 概要 4 | 作成済みのスプリット情報を閲覧するフローのE2Eテストを実装します。 5 | 6 | ## 詳細 7 | スプリット閲覧は、設定された報酬分配の詳細を確認するための機能です。 8 | 9 | ### テストステップ 10 | 1. ワークスペースのホームページにアクセスする 11 | 2. ボトムメニューの「スプリッツ」タブをクリックする 12 | 3. スプリッツページが表示されることを確認する 13 | 4. スプリットの一覧が表示されることを確認する 14 | 5. 特定のスプリットのアコーディオンメニューをクリックして開く 15 | 6. スプリットの詳細情報が表示されることを確認する 16 | 7. 各役割への分配率が正しく表示されることを確認する 17 | 8. 各メンバーへの分配率が正しく表示されることを確認する 18 | 19 | ### 期待される結果 20 | - スプリット一覧が正しく表示される 21 | - スプリットの詳細情報が正確に表示される 22 | - 分配率の情報が正確に表示される 23 | 24 | ## 技術的な実装ポイント 25 | - データ取得と表示のテスト 26 | - アコーディオンメニューの操作確認 27 | - 分配率表示の正確性確認 28 | - 複数のスプリットがある場合の表示確認 29 | 30 | ## 実装方針 31 | 1. ページオブジェクトパターンを使用して、スプリット閲覧ページの操作をカプセル化する 32 | 2. APIレスポンスのモックを作成して、様々なデータパターンをテストする 33 | 3. アコーディオンメニューの操作とその結果の検証を実装する 34 | 4. 分配率表示の正確性を検証する 35 | 36 | ## 優先度 37 | 低(情報確認フロー) 38 | 39 | ## 関連リソース 40 | - [既存のE2Eテスト](https://github.com/hackdays-io/toban/tree/main/pkgs/frontend/cypress) 41 | - [Cypress ドキュメント](https://docs.cypress.io/guides/references/best-practices) 42 | -------------------------------------------------------------------------------- /docs/img/header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackdays-io/toban/1ffd398b45703d2b9cf6117a5ca161489f3c3663/docs/img/header.png -------------------------------------------------------------------------------- /docs/puml/class.puml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 3 | abstract ERC1155{ 4 | # _balanceOf 5 | --- 6 | } 7 | 8 | abstract ERC2771Context { 9 | - _trustedForwarder: address 10 | --- 11 | } 12 | 13 | class HatsIdUtilities{ 14 | + linkedTreeAdmins: mapping 15 | --- 16 | } 17 | 18 | class Hats { 19 | + _hats: mapping 20 | --- 21 | + TopHat をミントする 22 | + Hat を作成する 23 | + Hat をミントする 24 | } 25 | 26 | struct Hat { 27 | eligibility: address 28 | maxSupply: uint32 29 | supply: uint32 30 | lastHatId: uint16 31 | toggle: address 32 | config: uint96 33 | details: string 34 | imageURI: string 35 | } 36 | 37 | class HatsModuleFactory { 38 | --- 39 | + モジュールをデプロイする 40 | } 41 | 42 | class HatsModule { 43 | 44 | } 45 | 46 | class HatsTimeFrameModule { 47 | - woreTime: mapping 48 | --- 49 | + Hat をミントする 50 | # Hat の woreTime を記録する 51 | } 52 | 53 | class FractionToken { 54 | - tokenRecipients: mapping 55 | - allTokenIds: uint256[] 56 | --- 57 | + ミントする 58 | + トランスファーする 59 | + tokenRecipients を取得する 60 | + allTokenIds を取得する 61 | } 62 | 63 | class SplitWarehouse { 64 | --- 65 | + トランスファーする 66 | + 残高を更新する 67 | # バーンする 68 | + 引き出す 69 | } 70 | 71 | class SplitCreator { 72 | --- 73 | + 分配を計算し、Splitを作成する 74 | } 75 | 76 | class SplitFactoryV2 { 77 | --- 78 | + Splitをデプロイする 79 | } 80 | 81 | class PullSplit { 82 | --- 83 | + 分配する 84 | } 85 | 86 | HatsModuleFactory --> HatsTimeFrameModule : モジュールを作成する 87 | HatsTimeFrameModule x--> Hats : Hat をミントする 88 | SplitCreator ..> Hats : hatId 間の比率を\n参照(引数) 89 | SplitCreator ..> HatsTimeFrameModule : hat の着用時間を\n参照(引数) 90 | SplitCreator ..> FractionToken : トークンの情報を取得する 91 | SplitCreator --> SplitFactoryV2 : 分配量を計算し、\nPullSplitのデプロイを\n依頼する 92 | SplitFactoryV2 --> PullSplit : デプロイする 93 | PullSplit x--> SplitWarehouse : 入金する 94 | PullSplit x--> SplitWarehouse : 残高を更新する 95 | 96 | remove @unlinked 97 | @enduml -------------------------------------------------------------------------------- /lefthook.yaml: -------------------------------------------------------------------------------- 1 | pre-commit: 2 | commands: 3 | check: 4 | glob: "*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}" 5 | run: npx biome check --write --no-errors-on-unmatched --files-ignore-unknown=true . && git update-index --again 6 | format: 7 | glob: "*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}" 8 | run: npx biome format --write --no-errors-on-unmatched --files-ignore-unknown=true . && git update-index --again 9 | type-check: 10 | glob: "*.{ts,tsx}" 11 | run: | 12 | npx pnpm frontend typecheck 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "toban", 3 | "version": "1.0.0", 4 | "private": true, 5 | "main": "index.js", 6 | "repository": "https://github.com/hackdays-io/toban", 7 | "author": "", 8 | "license": "MIT", 9 | "engines": { 10 | "node": ">=20" 11 | }, 12 | "scripts": { 13 | "biome:format": "npx biome format --write .", 14 | "biome:check": "npx biome check --write .", 15 | "frontend": "pnpm --filter frontend", 16 | "contract": "pnpm --filter contract", 17 | "cli": "pnpm --filter cli", 18 | "document": "pnpm --filter document", 19 | "subgraph": "pnpm --filter subgraph" 20 | }, 21 | "devDependencies": { 22 | "@biomejs/biome": "^1.9.4", 23 | "lefthook": "^1.10.1" 24 | }, 25 | "packageManager": "pnpm@8.15.4" 26 | } 27 | -------------------------------------------------------------------------------- /pkgs/cli/.env.example: -------------------------------------------------------------------------------- 1 | TOBAN_PRIVATE_KEY="" -------------------------------------------------------------------------------- /pkgs/cli/.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | .env 3 | 4 | profiles.json 5 | /**/profiles.json -------------------------------------------------------------------------------- /pkgs/cli/README.md: -------------------------------------------------------------------------------- 1 | # Toban CLI Tool 2 | 3 | ## setUp 4 | 5 | 以下のコマンドをルートディレクトリから実行します。 6 | 7 | - **install** 8 | 9 | ```bash 10 | yarn 11 | ``` 12 | 13 | - **build** 14 | 15 | ```bash 16 | yarn cli build 17 | ``` 18 | 19 | - **global link** 20 | 21 | ```bash 22 | yarn cli link 23 | ``` 24 | 25 | ## How to use 26 | 27 | プロジェクトのルートディレクトリからは以下の方法で呼び出せます。 28 | 29 | ```bash 30 | yarn cli toban function --help 31 | ``` 32 | 33 | ```bash 34 | This is a CLI tool for toban project 35 | 36 | Options: 37 | -V, --version output the version number 38 | -h, --help display help for command 39 | 40 | Commands: 41 | list List all shifts 42 | add Add a new shift 43 | random Pick a random person for the shift 44 | show [options] Show the arguments 45 | help [command] display help for command 46 | ✨ Done in 0.66s. 47 | ``` 48 | 49 | 例えば `show` コマンドを呼ぶ場合 50 | 51 | ```bash 52 | yarn cli toban function show -t test 53 | ``` 54 | 55 | ツリー ID を指定してそれに紐づく hats 一覧を取得する。 56 | 57 | ```bash 58 | yarn cli toban hats list --treeId 163 59 | ``` 60 | 61 | HatId に紐づく全ての着用者のウォレットアドレス一覧を取得する。 62 | 63 | ```bash 64 | yarn cli toban hats wears --hatId 0x000000a300010002000100000000000000000000000000000000000000000000 65 | ``` 66 | 67 | 特定のウォレットアドレスに紐づく全ての Hats の情報を取得する。 68 | 69 | ```bash 70 | yarn cli toban hats wear --address 0xfedfa388151b6f8d5901f5482564988ed87b64f1 71 | ``` 72 | -------------------------------------------------------------------------------- /pkgs/cli/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cli", 3 | "version": "1.0.0", 4 | "private": true, 5 | "license": "MIT", 6 | "bin": { 7 | "toban": "./dist/index.js" 8 | }, 9 | "scripts": { 10 | "build": "npx tsc", 11 | "start": "npx tsc && npx ts-node ./dist/index.js", 12 | "hat:sample": "npx ts-node ./scripts/hat.ts" 13 | }, 14 | "dependencies": { 15 | "@hatsprotocol/sdk-v1-core": "^0.10.0", 16 | "@hatsprotocol/sdk-v1-subgraph": "^1.0.0", 17 | "commander": "^12.1.0", 18 | "dotenv": "^16.4.5", 19 | "pinata-web3": "^0.5.0", 20 | "ts-node": "^10.9.2", 21 | "typescript": "^5.6.2", 22 | "viem": "^2.21.15", 23 | "zod": "^3.23.8" 24 | }, 25 | "devDependencies": { 26 | "@types/commander": "^2.12.2", 27 | "@types/node": "^22.7.4" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /pkgs/cli/scripts/hat.ts: -------------------------------------------------------------------------------- 1 | import { optimism } from "viem/chains"; 2 | import { hatsSubgraphClient } from "../src/modules/hatsProtocol"; 3 | 4 | /** 5 | * HatsProtocolの機能を試すためのスクリプト 6 | */ 7 | const main = async () => { 8 | /* 9 | // hatの情報を取得する。 10 | const hat = await hatsSubgraphClient.getHat({ 11 | chainId: optimism.id, 12 | hatId: BigInt( 13 | 4394471306745554286530723459184199799802854540874113314419888470622208 14 | // 12664760623049752223273549914669722706079586129193058213113992646181026529280 15 | ), 16 | props: { 17 | maxSupply: true, // get the maximum amount of wearers for the hat 18 | wearers: { 19 | // get the hat's wearers 20 | props: {}, // for each wearer, include only its ID (address) 21 | }, 22 | events: { 23 | // get the hat's events 24 | props: { 25 | transactionID: true, // for each event, include the transaction ID 26 | }, 27 | filters: { 28 | first: 10, // fetch only the latest 10 events 29 | }, 30 | }, 31 | }, 32 | }); 33 | 34 | console.log(hat); 35 | */ 36 | 37 | // ツリー情報を全て取得する。 38 | const tree = await hatsSubgraphClient.getTree({ 39 | chainId: optimism.id, 40 | treeId: 163, 41 | props: { 42 | hats: { 43 | props: { 44 | prettyId: true, 45 | status: true, 46 | createdAt: true, 47 | details: true, 48 | maxSupply: true, 49 | eligibility: true, 50 | imageUri: true, 51 | toggle: true, 52 | levelAtLocalTree: true, 53 | currentSupply: true, 54 | }, 55 | }, 56 | }, 57 | }); 58 | 59 | console.log(tree); 60 | }; 61 | 62 | main().catch(console.error); 63 | -------------------------------------------------------------------------------- /pkgs/cli/src/commands/bigbang.ts: -------------------------------------------------------------------------------- 1 | import { Command } from "commander"; 2 | import { zeroAddress } from "viem"; 3 | import { bigbang } from "../modules/bigbang"; 4 | 5 | export const bigbangCommands = new Command(); 6 | 7 | bigbangCommands 8 | .name("bigbang") 9 | .description("This is a CLI bigbang for toban project") 10 | .version("1.0.0"); 11 | 12 | bigbangCommands 13 | .command("create") 14 | .description("Create project") 15 | .requiredOption("-o, --owner ", "Owner") 16 | .requiredOption("-td, --topHatDetails ", "Top hat details") 17 | .requiredOption("-ti, --topHatImageURI ", "Top hat image URI") 18 | .option("-hd, --hatterHatDetails ", "Hatter hat details") 19 | .option( 20 | "-hi, --hatterHatImageURI ", 21 | "Hatter hat image URI", 22 | ) 23 | .option("-f, --trustedForwarder ", "Trusted forwarder") 24 | .action(async (options) => { 25 | const hash = await bigbang({ 26 | owner: options.owner, 27 | topHatDetails: options.topHatDetails, 28 | topHatImageURI: options.topHatImageURI, 29 | hatterHatDetails: options.hatterHatDetails || "", 30 | hatterHatImageURI: options.hatterHatImageURI || "", 31 | trustedForwarder: options.trustedForwarder || zeroAddress, 32 | }); 33 | 34 | console.log(`Transaction hash: ${hash}`); 35 | }); 36 | -------------------------------------------------------------------------------- /pkgs/cli/src/commands/fractionToken.ts: -------------------------------------------------------------------------------- 1 | import { Command } from "commander"; 2 | import { mintFractionToken, sendFractionToken } from "../modules/fractiontoken"; 3 | 4 | export const fractionTokenCommands = new Command(); 5 | 6 | fractionTokenCommands 7 | .name("fractionToken") 8 | .description("This is a CLI fractionToken for toban project") 9 | .version("1.0.0"); 10 | 11 | fractionTokenCommands 12 | .command("mint") 13 | .description("Mint fraction token") 14 | .requiredOption("-hid, --hatId ", "Hat ID") 15 | .requiredOption("-a, --account ", "Account") 16 | .action(async (options) => { 17 | const transactionHash = await mintFractionToken( 18 | BigInt(options.hatId), 19 | options.account, 20 | ); 21 | 22 | console.log(`Transaction hash: ${transactionHash}`); 23 | }); 24 | 25 | fractionTokenCommands 26 | .command("send") 27 | .description("Send fraction token") 28 | .requiredOption("-t, --to ", "To") 29 | .requiredOption("-hid, --hatId ", "Hat ID") 30 | .requiredOption("-a, --amount ", "Amount") 31 | .action(async (options) => { 32 | const transactionHash = await sendFractionToken( 33 | options.to, 34 | BigInt(options.hatId), 35 | BigInt(options.amount), 36 | ); 37 | 38 | console.log(`Transaction hash: ${transactionHash}`); 39 | }); 40 | -------------------------------------------------------------------------------- /pkgs/cli/src/commands/wallet.ts: -------------------------------------------------------------------------------- 1 | import { Command } from "commander"; 2 | import { walletClient } from ".."; 3 | import { sendEth } from "../modules/viem"; 4 | import { deleteProfile, listProfiles, saveProfile } from "../services/wallet"; 5 | 6 | export const walletCommands = new Command(); 7 | 8 | walletCommands 9 | .name("wallet") 10 | .description("This is a CLI function for toban project") 11 | .version("1.0.0"); 12 | 13 | /** 14 | * ウォレット一覧 15 | */ 16 | walletCommands 17 | .command("list") 18 | .description("show wallets") 19 | .action(async () => { 20 | listProfiles(); 21 | }); 22 | 23 | /** 24 | * 新しいウォレットを追加 25 | */ 26 | walletCommands 27 | .command("add") 28 | .description("add a new wallet") 29 | .requiredOption("--privateKey ", "Private key to be saved") 30 | .requiredOption("--name ", "Wallet name") 31 | .action(({ name, privateKey }) => { 32 | saveProfile({ name, privateKey }); 33 | }); 34 | 35 | /** 36 | * ウォレットを削除 37 | */ 38 | walletCommands 39 | .command("remove") 40 | .description("remove a wallet") 41 | .requiredOption("--name ", "Wallet name") 42 | .action(({ name }) => { 43 | deleteProfile({ name }); 44 | }); 45 | 46 | /** 47 | * ETHを送信 48 | */ 49 | walletCommands 50 | .command("sendEth") 51 | .description("Send ETH") 52 | .requiredOption("--receiver ", "Receiver address") 53 | .requiredOption("--amount ", "Amount") 54 | .action(async ({ receiver, amount }) => { 55 | await sendEth(walletClient, receiver, amount); 56 | }); 57 | -------------------------------------------------------------------------------- /pkgs/cli/src/config.ts: -------------------------------------------------------------------------------- 1 | import type { Address } from "viem"; 2 | import { BIGBANG_ABI } from "./abi/bigbang"; 3 | import { FRACTION_TOKEN_ABI } from "./abi/fractiontoken"; 4 | import { HATS_ABI } from "./abi/hats"; 5 | import { HATS_TIME_FRAME_MODULE_ABI } from "./abi/hatsTimeFrameModule"; 6 | 7 | export const skipPreActionCommands = ["wallet>add", "wallet>list"]; 8 | 9 | export const hatsContractBaseConfig = { 10 | address: "0x3bc1A0Ad72417f2d411118085256fC53CBdDd137" as Address, 11 | abi: HATS_ABI, 12 | }; 13 | 14 | export const hatsTimeFrameContractBaseConfig = { 15 | abi: HATS_TIME_FRAME_MODULE_ABI, 16 | }; 17 | 18 | export const bigbangContractBaseConfig = { 19 | address: "0x5d7a64Cc808294C516076d371685ed4E6aDd6337" as Address, 20 | abi: BIGBANG_ABI, 21 | }; 22 | 23 | export const fractionTokenBaseConfig = { 24 | address: "0xb8f7ca7a5b1e457b8735884419e114f90d53e1d5" as Address, 25 | abi: FRACTION_TOKEN_ABI, 26 | }; 27 | -------------------------------------------------------------------------------- /pkgs/cli/src/image/test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackdays-io/toban/1ffd398b45703d2b9cf6117a5ca161489f3c3663/pkgs/cli/src/image/test.png -------------------------------------------------------------------------------- /pkgs/cli/src/index.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import { Command } from "commander"; 4 | import type { PublicClient, WalletClient } from "viem"; 5 | import { bigbangCommands } from "./commands/bigbang"; 6 | import { fractionTokenCommands } from "./commands/fractionToken"; 7 | import { hatsCommands } from "./commands/hats"; 8 | import { pinataCommands } from "./commands/pinata"; 9 | import { splitsCommands } from "./commands/splits"; 10 | import { walletCommands } from "./commands/wallet"; 11 | import { skipPreActionCommands } from "./config"; 12 | import { getPublicClient } from "./modules/viem"; 13 | import { getWalletClient } from "./services/wallet"; 14 | 15 | export const rootProgram = new Command(); 16 | 17 | export let publicClient!: PublicClient; 18 | 19 | export let walletClient!: WalletClient; 20 | 21 | rootProgram 22 | .version("0.0.1") 23 | .option("--chain ", "chain id", "11155111") 24 | .option("--profile ", "Wallet profile") 25 | .hook("preAction", async (_, actionCommand) => { 26 | const { chain, profile } = rootProgram.opts(); 27 | const parentName = actionCommand.parent?.name(); 28 | const commandName = actionCommand.name(); 29 | 30 | if (!skipPreActionCommands.includes(`${parentName}>${commandName}`)) { 31 | publicClient = await getPublicClient(chain); 32 | walletClient = await getWalletClient(profile, chain); 33 | } 34 | }); 35 | 36 | rootProgram.addCommand(bigbangCommands); 37 | rootProgram.addCommand(hatsCommands); 38 | rootProgram.addCommand(walletCommands); 39 | rootProgram.addCommand(splitsCommands); 40 | rootProgram.addCommand(pinataCommands); 41 | rootProgram.addCommand(fractionTokenCommands); 42 | 43 | rootProgram.parse(process.argv); 44 | -------------------------------------------------------------------------------- /pkgs/cli/src/modules/bigbang.ts: -------------------------------------------------------------------------------- 1 | // ############################################################### 2 | // Write with viem 3 | // ############################################################### 4 | 5 | import { hatIdToTreeId } from "@hatsprotocol/sdk-v1-core"; 6 | import { type Address, decodeEventLog } from "viem"; 7 | import { publicClient, walletClient } from ".."; 8 | import { bigbangContractBaseConfig } from "../config"; 9 | import { startLoading } from "../services/loading"; 10 | 11 | /** 12 | * プロジェクト作成 13 | */ 14 | 15 | export const bigbang = async (params: { 16 | owner: Address; 17 | topHatDetails: string; 18 | topHatImageURI: string; 19 | hatterHatDetails: string; 20 | hatterHatImageURI: string; 21 | trustedForwarder: Address; 22 | }) => { 23 | const stop = startLoading(); 24 | 25 | const { request } = await publicClient.simulateContract({ 26 | ...bigbangContractBaseConfig, 27 | account: walletClient.account, 28 | functionName: "bigbang", 29 | args: [ 30 | params.owner, 31 | params.topHatDetails, 32 | params.topHatImageURI, 33 | params.hatterHatDetails, 34 | params.hatterHatImageURI, 35 | params.trustedForwarder, 36 | ], 37 | }); 38 | const transactionHash = await walletClient.writeContract(request); 39 | 40 | const receipt = await publicClient.waitForTransactionReceipt({ 41 | hash: transactionHash, 42 | }); 43 | 44 | const log = receipt.logs.find((log) => { 45 | try { 46 | const decodedLog = decodeEventLog({ 47 | abi: bigbangContractBaseConfig.abi, 48 | data: log.data, 49 | topics: log.topics, 50 | }); 51 | return decodedLog.eventName === "Executed"; 52 | } catch (error) {} 53 | }); 54 | 55 | stop(); 56 | 57 | if (log) { 58 | const decodedLog = decodeEventLog({ 59 | abi: bigbangContractBaseConfig.abi, 60 | data: log.data, 61 | topics: log.topics, 62 | }); 63 | console.log(decodedLog); 64 | console.log( 65 | "Tree Link:", 66 | `https://app.hatsprotocol.xyz/trees/${String( 67 | publicClient.chain?.id, 68 | )}/${hatIdToTreeId(BigInt(decodedLog.args.topHatId))}`, 69 | ); 70 | } 71 | 72 | return transactionHash; 73 | }; 74 | -------------------------------------------------------------------------------- /pkgs/cli/src/modules/fractiontoken.ts: -------------------------------------------------------------------------------- 1 | import { type Address, decodeEventLog } from "viem"; 2 | import { publicClient, walletClient } from ".."; 3 | import { fractionTokenBaseConfig } from "../config"; 4 | import { startLoading } from "../services/loading"; 5 | 6 | export const getTokenId = async (hatId: bigint, account: Address) => { 7 | const res = await publicClient.readContract({ 8 | ...fractionTokenBaseConfig, 9 | functionName: "getTokenId", 10 | args: [hatId, account], 11 | }); 12 | 13 | return res; 14 | }; 15 | 16 | export const mintFractionToken = async (hatId: bigint, account: Address) => { 17 | const { request } = await publicClient.simulateContract({ 18 | ...fractionTokenBaseConfig, 19 | account: walletClient.account, 20 | functionName: "mint", 21 | args: [hatId, account], 22 | }); 23 | const transactionHash = await walletClient.writeContract(request); 24 | 25 | return transactionHash; 26 | }; 27 | 28 | export const sendFractionToken = async ( 29 | to: Address, 30 | hatId: bigint, 31 | amount: bigint, 32 | ) => { 33 | if (!walletClient.account) { 34 | throw new Error("Wallet is not connected"); 35 | } 36 | const stop = startLoading(); 37 | const tokenId = await getTokenId(hatId, walletClient.account.address); 38 | 39 | const { request } = await publicClient.simulateContract({ 40 | ...fractionTokenBaseConfig, 41 | account: walletClient.account, 42 | functionName: "safeTransferFrom", 43 | args: [walletClient.account.address, to, tokenId, amount, "0x"], 44 | }); 45 | const transactionHash = await walletClient.writeContract(request); 46 | 47 | const receipt = await publicClient.waitForTransactionReceipt({ 48 | hash: transactionHash, 49 | }); 50 | 51 | const log = receipt.logs.find((log) => { 52 | const decodedLog = decodeEventLog({ 53 | abi: fractionTokenBaseConfig.abi, 54 | data: log.data, 55 | topics: log.topics, 56 | }); 57 | return decodedLog.eventName === "TransferSingle"; 58 | }); 59 | 60 | stop(); 61 | 62 | if (log) { 63 | console.log( 64 | decodeEventLog({ 65 | abi: fractionTokenBaseConfig.abi, 66 | data: log.data, 67 | topics: log.topics, 68 | }), 69 | ); 70 | } 71 | 72 | return transactionHash; 73 | }; 74 | -------------------------------------------------------------------------------- /pkgs/cli/src/modules/splits.ts: -------------------------------------------------------------------------------- 1 | import { type Address, decodeEventLog } from "viem"; 2 | import { publicClient, walletClient } from ".."; 3 | import { SPLITS_ABI } from "../abi/splits"; 4 | import { startLoading } from "../services/loading"; 5 | 6 | type SplitsInfo = { 7 | hatId: bigint; 8 | multiplierBottom: bigint; 9 | multiplierTop: bigint; 10 | wearers: Address[]; 11 | }; 12 | 13 | /** 14 | * Splitsを作成 15 | */ 16 | export const create = async (splitsAddress: Address, args: SplitsInfo[]) => { 17 | const stop = startLoading(); 18 | const { request } = await publicClient.simulateContract({ 19 | address: splitsAddress, 20 | abi: SPLITS_ABI, 21 | account: walletClient.account, 22 | functionName: "create", 23 | args: [args], 24 | }); 25 | 26 | const hash = await walletClient.writeContract(request); 27 | 28 | const receipt = await publicClient.waitForTransactionReceipt({ 29 | hash, 30 | }); 31 | 32 | const log = receipt.logs.find((log) => { 33 | try { 34 | const decodedLog = decodeEventLog({ 35 | abi: SPLITS_ABI, 36 | data: log.data, 37 | topics: log.topics, 38 | }); 39 | return decodedLog.eventName === "SplitsCreated"; 40 | } catch (error) {} 41 | }); 42 | 43 | stop(); 44 | 45 | if (log) { 46 | const decodedLog = decodeEventLog({ 47 | abi: SPLITS_ABI, 48 | data: log.data, 49 | topics: log.topics, 50 | }); 51 | console.log(decodedLog); 52 | console.log( 53 | "Split Link:", 54 | `https://app.splits.org/accounts/${ 55 | decodedLog.args.split 56 | }/?chainId=${String(publicClient.chain?.id)}`, 57 | ); 58 | } 59 | 60 | return hash; 61 | }; 62 | -------------------------------------------------------------------------------- /pkgs/cli/src/services/loading.ts: -------------------------------------------------------------------------------- 1 | export const startLoading = () => { 2 | const brailleChars = ["⠁", "⠃", "⠇", "⡇", "⡏", "⡟", "⡿", "⡿", "⣿"]; 3 | 4 | let index = 0; 5 | const interval = setInterval(() => { 6 | process.stdout.write(`\rLoading ${brailleChars[index]} `); 7 | index = (index + 1) % brailleChars.length; 8 | }, 200); 9 | 10 | return () => { 11 | clearInterval(interval); 12 | console.log("Done!\n"); 13 | }; 14 | }; 15 | -------------------------------------------------------------------------------- /pkgs/cli/src/services/pinata.ts: -------------------------------------------------------------------------------- 1 | import { existsSync, readFileSync, writeFileSync } from "node:fs"; 2 | import path from "node:path"; 3 | const pinataPath = path.join(__dirname, "pinata.json"); 4 | 5 | export interface Pinata { 6 | jwt: string; 7 | } 8 | 9 | export const getJwt = () => { 10 | if (!existsSync(pinataPath)) { 11 | writeFileSync(pinataPath, JSON.stringify({ jwt: "" })); 12 | } 13 | 14 | const data = readFileSync(pinataPath, "utf8"); 15 | return JSON.parse(data) as Pinata; 16 | }; 17 | 18 | export const setJwt = (jwt: string) => { 19 | writeFileSync(pinataPath, JSON.stringify({ jwt: jwt })); 20 | }; 21 | -------------------------------------------------------------------------------- /pkgs/cli/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2016", 4 | "module": "commonjs", 5 | "outDir": "./dist", // コンパイル後のファイル出力先 6 | "rootDir": "./src", // ソースコードのルートディレクトリ 7 | "strict": true, // 厳密な型チェック 8 | "esModuleInterop": true, // ESモジュールのインポートサポート 9 | "skipLibCheck": true // 型定義ファイルのチェックをスキップ, 10 | }, 11 | "include": ["src/**/*"], // コンパイル対象のファイル 12 | "exclude": ["node_modules"] // 除外するディレクトリ 13 | } 14 | -------------------------------------------------------------------------------- /pkgs/contract/.env.example: -------------------------------------------------------------------------------- 1 | PRIVATE_KEY="" 2 | ETHERSCAN_API_KEY="" 3 | ALCHEMY_API_KEY="" 4 | GAS_REPORT= 5 | COINMARKETCAP_API_KEY="" 6 | HATS_ADDRESS="" 7 | HATS_MODULE_FACTORY_ADDRESS="" 8 | PULL_SPLITS_FACTORY_ADDRESS="" -------------------------------------------------------------------------------- /pkgs/contract/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | 4 | # Hardhat files 5 | /cache 6 | /artifacts 7 | 8 | # TypeChain files 9 | /typechain 10 | /typechain-types 11 | 12 | # solidity-coverage files 13 | /coverage 14 | /coverage.json 15 | 16 | # Hardhat Ignition default folder for deployments against a local node 17 | ignition/deployments/chain-31337 18 | 19 | **/artifacts/** 20 | **/build-info/** 21 | # solhint report files 22 | *_solhintReport.txt 23 | -------------------------------------------------------------------------------- /pkgs/contract/.solhint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solhint:recommended", 3 | "plugins": [], 4 | "rules": { 5 | "avoid-suicide": "error", 6 | "avoid-sha3": "warn" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /pkgs/contract/.solhintignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | contracts/hats/module/ 3 | contracts/**/mock/** 4 | -------------------------------------------------------------------------------- /pkgs/contract/README.md: -------------------------------------------------------------------------------- 1 | # Sample Hardhat Project 2 | 3 | This project demonstrates a basic Hardhat use case. It comes with a sample contract, a test for that contract, and a Hardhat Ignition module that deploys that contract. 4 | 5 | Try running some of the following tasks: 6 | 7 | ```shell 8 | npx hardhat help 9 | npx hardhat test 10 | REPORT_GAS=true npx hardhat test 11 | npx hardhat node 12 | npx hardhat ignition deploy ./ignition/modules/Lock.ts 13 | ``` 14 | -------------------------------------------------------------------------------- /pkgs/contract/contracts/bigbang/IHatsModuleFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.24; 4 | 5 | interface IHatsModuleFactory { 6 | function createHatsModule( 7 | address _implementation, 8 | uint256 _hatId, 9 | bytes calldata _otherImmutableArgs, 10 | bytes calldata _initData, 11 | uint256 _saltNonce 12 | ) external returns (address); 13 | } 14 | -------------------------------------------------------------------------------- /pkgs/contract/contracts/fractiontoken/IFractionToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.24; 4 | 5 | import "@openzeppelin/contracts/token/ERC1155/IERC1155.sol"; 6 | 7 | interface IFractionToken is IERC1155 { 8 | event InitialMint( 9 | address indexed wearer, 10 | uint256 indexed hatId, 11 | uint256 indexed tokenId 12 | ); 13 | 14 | function mintInitialSupply( 15 | uint256 hatId, 16 | address account, 17 | uint256 amount 18 | ) external; 19 | 20 | function mint(uint256 hatId, address account, uint256 amount) external; 21 | 22 | function burn( 23 | address from, 24 | address wearer, 25 | uint256 hatId, 26 | uint256 value 27 | ) external; 28 | 29 | function getTokenRecipients( 30 | uint256 tokenId 31 | ) external view returns (address[] memory); 32 | 33 | function getTokenId( 34 | uint256 hatId, 35 | address account 36 | ) external view returns (uint256); 37 | 38 | function balanceOf( 39 | address account, 40 | address warer, 41 | uint256 hatId 42 | ) external view returns (uint256); 43 | 44 | function balanceOfBatch( 45 | address[] memory accounts, 46 | address[] memory warers, 47 | uint256[] memory hatIds 48 | ) external view returns (uint256[] memory); 49 | 50 | function totalSupply( 51 | address wearer, 52 | uint256 hatId 53 | ) external view returns (uint256); 54 | } 55 | -------------------------------------------------------------------------------- /pkgs/contract/contracts/hats/module/IHatsModule.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.19; 3 | 4 | import {IHats} from "../../hats/src/Interfaces/IHats.sol"; 5 | 6 | interface IHatsModule { 7 | /// @notice Hats Protocol address 8 | function HATS() external pure returns (IHats); 9 | 10 | /// @notice The address of the implementation contract of which this instance is a clone 11 | function IMPLEMENTATION() external pure returns (address); 12 | 13 | /// @notice The hat id for which this HatsModule instance has been deployed 14 | function hatId() external pure returns (uint256); 15 | 16 | /** 17 | * @notice Sets up this instance with initial operational values (`_initData`) 18 | * @dev This function can only be called once, on initialization 19 | * @param _initData Data to set up initial operational values for this instance 20 | */ 21 | function setUp(bytes memory _initData) external; 22 | 23 | /// @notice The version of this HatsModule 24 | /// @dev Used only for the implementation contract; for clones, use {version} 25 | function version_() external view returns (string memory); 26 | 27 | /// @notice The version of this HatsModule 28 | function version() external view returns (string memory); 29 | } 30 | -------------------------------------------------------------------------------- /pkgs/contract/contracts/hats/src/Interfaces/IHatsEligibility.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0 2 | // Copyright (C) 2023 Haberdasher Labs 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Affero General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Affero General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Affero General Public License 15 | // along with this program. If not, see . 16 | 17 | pragma solidity >=0.8.13; 18 | 19 | interface IHatsEligibility { 20 | /// @notice Returns the status of a wearer for a given hat 21 | /// @dev If standing is false, eligibility MUST also be false 22 | /// @param _wearer The address of the current or prospective Hat wearer 23 | /// @param _hatId The id of the hat in question 24 | /// @return eligible Whether the _wearer is eligible to wear the hat 25 | /// @return standing Whether the _wearer is in goog standing 26 | function getWearerStatus(address _wearer, uint256 _hatId) external view returns (bool eligible, bool standing); 27 | } 28 | -------------------------------------------------------------------------------- /pkgs/contract/contracts/hats/src/Interfaces/IHatsIdUtilities.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0 2 | // Copyright (C) 2023 Haberdasher Labs 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Affero General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Affero General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Affero General Public License 15 | // along with this program. If not, see . 16 | 17 | pragma solidity >=0.8.13; 18 | 19 | interface IHatsIdUtilities { 20 | function buildHatId(uint256 _admin, uint16 _newHat) external pure returns (uint256 id); 21 | 22 | function getHatLevel(uint256 _hatId) external view returns (uint32 level); 23 | 24 | function getLocalHatLevel(uint256 _hatId) external pure returns (uint32 level); 25 | 26 | function isTopHat(uint256 _hatId) external view returns (bool _topHat); 27 | 28 | function isLocalTopHat(uint256 _hatId) external pure returns (bool _localTopHat); 29 | 30 | function isValidHatId(uint256 _hatId) external view returns (bool validHatId); 31 | 32 | function getAdminAtLevel(uint256 _hatId, uint32 _level) external view returns (uint256 admin); 33 | 34 | function getAdminAtLocalLevel(uint256 _hatId, uint32 _level) external pure returns (uint256 admin); 35 | 36 | function getTopHatDomain(uint256 _hatId) external view returns (uint32 domain); 37 | 38 | function getTippyTopHatDomain(uint32 _topHatDomain) external view returns (uint32 domain); 39 | 40 | function noCircularLinkage(uint32 _topHatDomain, uint256 _linkedAdmin) external view returns (bool notCircular); 41 | 42 | function sameTippyTopHatDomain(uint32 _topHatDomain, uint256 _newAdminHat) 43 | external 44 | view 45 | returns (bool sameDomain); 46 | } 47 | -------------------------------------------------------------------------------- /pkgs/contract/contracts/hats/src/Interfaces/IHatsToggle.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0 2 | // Copyright (C) 2023 Haberdasher Labs 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Affero General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Affero General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Affero General Public License 15 | // along with this program. If not, see . 16 | 17 | pragma solidity >=0.8.13; 18 | 19 | interface IHatsToggle { 20 | function getHatStatus(uint256 _hatId) external view returns (bool); 21 | } 22 | -------------------------------------------------------------------------------- /pkgs/contract/contracts/splits/interfaces/IERC165.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity ^0.8.23; 3 | 4 | interface IERC165 { 5 | /// @notice Checks if a contract implements an interface. 6 | /// @param interfaceId The interface identifier, as specified in ERC-165. 7 | /// @return supported True if the contract implements `interfaceId` and 8 | /// `interfaceId` is not 0xffffffff, false otherwise. 9 | function supportsInterface(bytes4 interfaceId) external view returns (bool supported); 10 | } 11 | -------------------------------------------------------------------------------- /pkgs/contract/contracts/splits/interfaces/IERC6909X.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity ^0.8.23; 3 | 4 | import { IERC5267 } from "@openzeppelin/contracts/interfaces/IERC5267.sol"; 5 | 6 | /** 7 | * @author https://github.com/frangio/erc6909-extensions 8 | */ 9 | interface IERC6909X is IERC5267 { 10 | function temporaryApproveAndCall( 11 | address spender, 12 | bool operator, 13 | uint256 id, 14 | uint256 amount, 15 | address target, 16 | bytes calldata data 17 | ) 18 | external 19 | returns (bool); 20 | 21 | function temporaryApproveAndCallBySig( 22 | address owner, 23 | address spender, 24 | bool operator, 25 | uint256 id, 26 | uint256 amount, 27 | address target, 28 | bytes calldata data, 29 | uint256 nonce, 30 | uint48 deadline, 31 | bytes calldata signature 32 | ) 33 | external 34 | returns (bool); 35 | 36 | function approveBySig( 37 | address owner, 38 | address spender, 39 | bool operator, 40 | uint256 id, 41 | uint256 amount, 42 | uint256 nonce, 43 | uint48 deadline, 44 | bytes calldata signature 45 | ) 46 | external 47 | returns (bool); 48 | } 49 | -------------------------------------------------------------------------------- /pkgs/contract/contracts/splits/interfaces/IERC6909XCallback.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity ^0.8.23; 3 | 4 | interface IERC6909XCallback { 5 | function onTemporaryApprove( 6 | address owner, 7 | bool operator, 8 | uint256 id, 9 | uint256 amount, 10 | bytes calldata data 11 | ) 12 | external 13 | returns (bytes4); 14 | } 15 | -------------------------------------------------------------------------------- /pkgs/contract/contracts/splits/interfaces/ISplitFactoryV2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.24; 4 | 5 | import {SplitV2Lib} from "../libraries/SplitV2.sol"; 6 | 7 | interface ISplitFactoryV2 { 8 | function createSplitDeterministic( 9 | SplitV2Lib.Split calldata _splitParams, 10 | address _owner, 11 | address _creator, 12 | bytes32 _salt 13 | ) external returns (address split); 14 | 15 | function createSplit( 16 | SplitV2Lib.Split calldata _splitParams, 17 | address _owner, 18 | address _creator 19 | ) external returns (address split); 20 | } 21 | -------------------------------------------------------------------------------- /pkgs/contract/contracts/splits/interfaces/ISplitsWarehouse.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity ^0.8.23; 3 | 4 | import { IERC6909 } from "./IERC6909.sol"; 5 | 6 | interface ISplitsWarehouse is IERC6909 { 7 | function NATIVE_TOKEN() external view returns (address); 8 | 9 | function deposit(address receiver, address token, uint256 amount) external payable; 10 | 11 | function batchDeposit(address[] calldata receivers, address token, uint256[] calldata amounts) external; 12 | 13 | function batchTransfer(address[] calldata receivers, address token, uint256[] calldata amounts) external; 14 | 15 | function withdraw(address owner, address token) external; 16 | } 17 | -------------------------------------------------------------------------------- /pkgs/contract/contracts/splits/interfaces/IWETH9.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity ^0.8.23; 3 | 4 | interface IWETH9 { 5 | function deposit() external payable; 6 | function withdraw(uint256) external; 7 | } 8 | -------------------------------------------------------------------------------- /pkgs/contract/contracts/splits/libraries/Cast.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity ^0.8.23; 3 | 4 | library Cast { 5 | error Overflow(); 6 | 7 | function toAddress(uint256 _value) internal pure returns (address) { 8 | return address(toUint160(_value)); 9 | } 10 | 11 | function toUint256(address _value) internal pure returns (uint256) { 12 | return uint256(uint160(_value)); 13 | } 14 | 15 | function toUint160(uint256 _x) internal pure returns (uint160 y) { 16 | if (_x >> 160 != 0) revert Overflow(); 17 | // solhint-disable-next-line no-inline-assembly 18 | assembly { 19 | y := _x 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /pkgs/contract/contracts/splits/libraries/Math.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity ^0.8.23; 3 | 4 | library Math { 5 | function sum(uint256[] calldata values) internal pure returns (uint256 total) { 6 | for (uint256 i; i < values.length; ++i) { 7 | total += values[i]; 8 | } 9 | } 10 | 11 | function sumMem(uint256[] memory values) internal pure returns (uint256 total) { 12 | for (uint256 i; i < values.length; ++i) { 13 | total += values[i]; 14 | } 15 | } 16 | 17 | function min(uint256 a, uint256 b) internal pure returns (uint256) { 18 | return a < b ? a : b; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /pkgs/contract/contracts/splits/splitters/pull/PullSplitFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity ^0.8.23; 3 | 4 | import { SplitFactoryV2 } from "../SplitFactoryV2.sol"; 5 | import { PullSplit } from "./PullSplit.sol"; 6 | 7 | /** 8 | * @title Pull split factory 9 | * @author Splits 10 | * @notice Minimal smart wallet clone-factory for pull flow splitters. 11 | */ 12 | contract PullSplitFactory is SplitFactoryV2 { 13 | /* -------------------------------------------------------------------------- */ 14 | /* CONSTRUCTOR */ 15 | /* -------------------------------------------------------------------------- */ 16 | 17 | constructor(address _splitsWarehouse) { 18 | SPLIT_WALLET_IMPLEMENTATION = address(new PullSplit(_splitsWarehouse)); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /pkgs/contract/contracts/splits/splitters/push/PushSplitFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity ^0.8.23; 3 | 4 | import { SplitFactoryV2 } from "../SplitFactoryV2.sol"; 5 | import { PushSplit } from "./PushSplit.sol"; 6 | 7 | /** 8 | * @title Push split factory 9 | * @author Splits 10 | * @notice Minimal smart wallet clone-factory for push flow splitters. 11 | */ 12 | contract PushSplitFactory is SplitFactoryV2 { 13 | /* -------------------------------------------------------------------------- */ 14 | /* CONSTRUCTOR */ 15 | /* -------------------------------------------------------------------------- */ 16 | 17 | constructor(address _splitsWarehouse) { 18 | SPLIT_WALLET_IMPLEMENTATION = address(new PushSplit(_splitsWarehouse)); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /pkgs/contract/contracts/splits/utils/Nonces.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity ^0.8.23; 3 | 4 | /** 5 | * @title Track hash Nonces 6 | * @dev Inspired by OpenZeppelin's Nonces.sol 7 | */ 8 | abstract contract Nonces { 9 | mapping(bytes32 hash => uint256) private _nonces; 10 | 11 | /** 12 | * @dev Returns the next unused nonce for a hash. 13 | */ 14 | function nonces(bytes32 _hash) public view virtual returns (uint256) { 15 | return _nonces[_hash]; 16 | } 17 | 18 | /** 19 | * @dev Consumes a nonce. 20 | * 21 | * Returns the current value and increments nonce. 22 | */ 23 | function useNonce(bytes32 _hash) internal virtual returns (uint256) { 24 | // For each hash, the nonce has an initial value of 0, can only be incremented by one, and cannot be 25 | // decremented or reset. This guarantees that the nonce never overflows. 26 | unchecked { 27 | // It is important to do x++ and not ++x here. 28 | return _nonces[_hash]++; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /pkgs/contract/contracts/splitscreator/ISplitsCreator.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.24; 4 | 5 | interface ISplitsCreator { 6 | struct SplitsInfo { 7 | uint256 hatId; 8 | uint256 multiplierBottom; 9 | uint256 multiplierTop; 10 | address[] wearers; 11 | } 12 | 13 | event SplitsCreated( 14 | address split, 15 | address[] shareHolders, 16 | uint256[] allocations, 17 | uint256 totalAllocation 18 | ); 19 | 20 | function create(SplitsInfo[] memory _splitInfos) external returns (address); 21 | } 22 | -------------------------------------------------------------------------------- /pkgs/contract/contracts/splitscreator/ISplitsCreatorFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.24; 4 | 5 | interface ISplitsCreatorFactory { 6 | function SPLITS_CREATOR_IMPLEMENTATION() external view returns (address); 7 | 8 | function createSplitCreatorDeterministic( 9 | uint256 _topHatId, 10 | address _hats, 11 | address _splitFactoryV2, 12 | address _hatsTimeFrameModule, 13 | address _fractionToken, 14 | bytes32 _salt 15 | ) external returns (address); 16 | } 17 | -------------------------------------------------------------------------------- /pkgs/contract/hardhat.config.ts: -------------------------------------------------------------------------------- 1 | import "@nomicfoundation/hardhat-ethers"; 2 | import "@nomicfoundation/hardhat-toolbox-viem"; 3 | import "@nomicfoundation/hardhat-viem"; 4 | import "@openzeppelin/hardhat-upgrades"; 5 | import * as dotenv from "dotenv"; 6 | import "hardhat-gas-reporter"; 7 | import type { HardhatUserConfig } from "hardhat/config"; 8 | import "./tasks"; 9 | 10 | dotenv.config(); 11 | 12 | const { 13 | PRIVATE_KEY, 14 | ETHERSCAN_API_KEY, 15 | ALCHEMY_API_KEY, 16 | COINMARKETCAP_API_KEY, 17 | PRODUCTION_PRIVATE_KEY, 18 | BASESCAN_API_KEY, 19 | GAS_REPORT, 20 | } = process.env; 21 | 22 | const config: HardhatUserConfig = { 23 | solidity: { 24 | compilers: [ 25 | { 26 | version: "0.8.24", 27 | settings: { 28 | viaIR: true, 29 | optimizer: { 30 | runs: 200, 31 | }, 32 | }, 33 | }, 34 | ], 35 | }, 36 | networks: { 37 | hardhat: { 38 | allowUnlimitedContractSize: true, 39 | }, 40 | sepolia: { 41 | url: `https://eth-sepolia.g.alchemy.com/v2/${ALCHEMY_API_KEY}`, 42 | accounts: PRIVATE_KEY !== undefined ? [PRIVATE_KEY] : [], 43 | }, 44 | holesky: { 45 | url: `https://eth-holesky.g.alchemy.com/v2/${ALCHEMY_API_KEY}`, 46 | accounts: PRIVATE_KEY !== undefined ? [PRIVATE_KEY] : [], 47 | }, 48 | base: { 49 | url: `https://base-mainnet.g.alchemy.com/v2/${ALCHEMY_API_KEY}`, 50 | accounts: 51 | PRODUCTION_PRIVATE_KEY !== undefined ? [PRODUCTION_PRIVATE_KEY] : [], 52 | }, 53 | }, 54 | etherscan: { 55 | apiKey: { 56 | sepolia: ETHERSCAN_API_KEY ?? "", 57 | holesky: ETHERSCAN_API_KEY ?? "", 58 | base: BASESCAN_API_KEY ?? "", 59 | }, 60 | }, 61 | gasReporter: { 62 | enabled: true, 63 | currency: "USD", 64 | token: "ETH", 65 | coinmarketcap: COINMARKETCAP_API_KEY, 66 | gasPriceApi: 67 | "https://api.etherscan.io/api?module=proxy&action=eth_gasPrice", 68 | }, 69 | }; 70 | 71 | export default config; 72 | -------------------------------------------------------------------------------- /pkgs/contract/helpers/deploy/BigBang.ts: -------------------------------------------------------------------------------- 1 | import { ethers, viem } from "hardhat"; 2 | import type { Address } from "viem"; 3 | import { baseSalt, deployContract_Create2 } from "./Create2Factory"; 4 | import { ProxyFactory } from "./Upgradeable"; 5 | 6 | export type BigBang = Awaited>["BigBang"]; 7 | 8 | export const deployBigBang = async ( 9 | params: { 10 | hatsContractAddress: Address; 11 | hatsModuleFacotryAddress: Address; 12 | hatsTimeFrameModule_impl: Address; 13 | hatsHatCreatorModule_impl: Address; 14 | splitsCreatorFactoryAddress: Address; 15 | splitsFactoryV2Address: Address; 16 | fractionTokenAddress: Address; 17 | }, 18 | create2DeployerAddress?: string, 19 | ) => { 20 | const [deployer] = await ethers.getSigners(); 21 | 22 | const BigBangFactory = await ethers.getContractFactory("BigBang"); 23 | const BigBangImplTx = await BigBangFactory.getDeployTransaction(); 24 | const BigBangImplAddress = await deployContract_Create2( 25 | baseSalt, 26 | BigBangImplTx.data || "0x", 27 | ethers.keccak256(BigBangImplTx.data), 28 | "BigBang_Implementation", 29 | create2DeployerAddress, 30 | ); 31 | 32 | const BigBangInitData = BigBangFactory.interface.encodeFunctionData( 33 | "initialize", 34 | [ 35 | deployer.address, 36 | params.hatsContractAddress, 37 | params.hatsModuleFacotryAddress, 38 | params.hatsTimeFrameModule_impl, 39 | params.hatsHatCreatorModule_impl, 40 | params.splitsCreatorFactoryAddress, 41 | params.splitsFactoryV2Address, 42 | params.fractionTokenAddress, 43 | ], 44 | ); 45 | 46 | const UpgradeableProxy = await ProxyFactory(); 47 | const BigBangProxyTx = await UpgradeableProxy.getDeployTransaction( 48 | BigBangImplAddress, 49 | BigBangInitData, 50 | ); 51 | const BigBangAddress = await deployContract_Create2( 52 | baseSalt, 53 | BigBangProxyTx.data || "0x", 54 | ethers.keccak256(BigBangProxyTx.data), 55 | "BigBang", 56 | create2DeployerAddress, 57 | ); 58 | 59 | const BigBang = await viem.getContractAt( 60 | "BigBang", 61 | BigBangAddress as Address, 62 | ); 63 | 64 | return { BigBang, BigBangImplAddress, BigBangInitData }; 65 | }; 66 | -------------------------------------------------------------------------------- /pkgs/contract/helpers/deploy/Create2Factory.ts: -------------------------------------------------------------------------------- 1 | import { ethers, viem } from "hardhat"; 2 | import type { Address } from "viem"; 3 | 4 | const create2DeployerAbi = [ 5 | "function deploy(uint256 amount, bytes32 salt, bytes calldata code) public returns (address)", 6 | "function computeAddress(bytes32 salt, bytes32 codeHash) public view returns (address)", 7 | ]; 8 | 9 | const create2DeployerAddress = "0x13b0D85CcB8bf860b6b79AF3029fCA081AE9beF2"; 10 | 11 | export type Create2Deployer = Awaited< 12 | ReturnType 13 | >["Create2Deployer"]; 14 | 15 | export const baseSalt = ethers.keccak256(ethers.toUtf8Bytes("toban")); 16 | 17 | export const computeAddress = async ( 18 | salt: string, 19 | hash: string, 20 | create2Deployer: string = create2DeployerAddress, 21 | ) => { 22 | const create2Factory = await ethers.getContractAt( 23 | create2DeployerAbi, 24 | create2Deployer, 25 | ); 26 | const computedAddress = await create2Factory.computeAddress(salt, hash); 27 | return computedAddress; 28 | }; 29 | 30 | export const checkAlreadyDeployed = async (address: string) => { 31 | const code = await ethers.provider.getCode(address); 32 | return code !== "0x"; 33 | }; 34 | 35 | export const deployContract_Create2 = async ( 36 | salt: string, 37 | code: string, 38 | hash: string, 39 | name: string, 40 | create2Deployer = create2DeployerAddress, 41 | ) => { 42 | const computedAddress: Address = await computeAddress( 43 | salt, 44 | hash, 45 | create2Deployer, 46 | ); 47 | const alreadyDeployed = await checkAlreadyDeployed(computedAddress); 48 | if (alreadyDeployed) { 49 | console.log(`${name} already deployed at: ${computedAddress}`); 50 | return computedAddress; 51 | } 52 | const create2Factory = await ethers.getContractAt( 53 | create2DeployerAbi, 54 | create2Deployer, 55 | ); 56 | const tx = await create2Factory.deploy(0, salt, code); 57 | await tx.wait(); 58 | 59 | console.log(`${name} deployed at: ${computedAddress}`); 60 | 61 | return computedAddress; 62 | }; 63 | 64 | export const deployCreate2Deployer = async () => { 65 | const Create2Deployer = await viem.deployContract("Create2Deployer"); 66 | 67 | return { Create2Deployer }; 68 | }; 69 | -------------------------------------------------------------------------------- /pkgs/contract/helpers/deploy/FractionToken.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades, viem } from "hardhat"; 2 | import type { Address } from "viem"; 3 | import { baseSalt, deployContract_Create2 } from "./Create2Factory"; 4 | import { ProxyFactory } from "./Upgradeable"; 5 | 6 | export type FractionToken = Awaited< 7 | ReturnType 8 | >["FractionToken"]; 9 | 10 | export const deployFractionToken = async ( 11 | uri: string, 12 | tokenSupply: bigint, 13 | hatsContractAddress: Address, 14 | create2DeployerAddress?: string, 15 | ) => { 16 | const [deployer] = await ethers.getSigners(); 17 | const _tokenSupply = !tokenSupply ? 10000n : tokenSupply; 18 | 19 | const FractionTokenFactory = await ethers.getContractFactory("FractionToken"); 20 | const FractionTokenImplTx = await FractionTokenFactory.getDeployTransaction(); 21 | const FractionTokenImplAddress = await deployContract_Create2( 22 | baseSalt, 23 | FractionTokenImplTx.data || "0x", 24 | ethers.keccak256(FractionTokenImplTx.data), 25 | "FractionToken_Implementation", 26 | create2DeployerAddress, 27 | ); 28 | 29 | const FractionTokenInitData = 30 | FractionTokenFactory.interface.encodeFunctionData("initialize", [ 31 | deployer.address, 32 | _tokenSupply, 33 | hatsContractAddress, 34 | uri, 35 | ]); 36 | 37 | const UpgradeableProxy = await ProxyFactory(); 38 | const FractionTokenProxyTx = await UpgradeableProxy.getDeployTransaction( 39 | FractionTokenImplAddress, 40 | FractionTokenInitData, 41 | ); 42 | const FractionTokenAddress = await deployContract_Create2( 43 | baseSalt, 44 | FractionTokenProxyTx.data || "0x", 45 | ethers.keccak256(FractionTokenProxyTx.data), 46 | "FractionToken", 47 | create2DeployerAddress, 48 | ); 49 | 50 | // create a new instance of the contract 51 | const FractionToken = await viem.getContractAt( 52 | "FractionToken", 53 | FractionTokenAddress as Address, 54 | ); 55 | 56 | return { FractionToken, FractionTokenImplAddress, FractionTokenInitData }; 57 | }; 58 | -------------------------------------------------------------------------------- /pkgs/contract/helpers/deploy/Upgradeable.ts: -------------------------------------------------------------------------------- 1 | import ERC1967Proxy from "@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts-v5/proxy/ERC1967/ERC1967Proxy.sol/ERC1967Proxy.json"; 2 | import TransparentUpgradeableProxy from "@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts-v5/proxy/transparent/TransparentUpgradeableProxy.sol/TransparentUpgradeableProxy.json"; 3 | import { ethers } from "hardhat"; 4 | import type { Address } from "viem"; 5 | import { baseSalt, deployContract_Create2 } from "./Create2Factory"; 6 | 7 | export const deployProxyAdmin = async ( 8 | ownerAddress: Address, 9 | create2DeployerAddress?: string, 10 | ) => { 11 | const proxyAdminFactory = await ethers.getContractFactory("ProxyAdmin"); 12 | const proxyAdminBytecode = 13 | await proxyAdminFactory.getDeployTransaction(ownerAddress); 14 | 15 | const proxyAdminAddress = await deployContract_Create2( 16 | baseSalt, 17 | proxyAdminBytecode.data || "0x", 18 | ethers.keccak256(proxyAdminBytecode.data), 19 | "ProxyAdmin", 20 | create2DeployerAddress, 21 | ); 22 | 23 | return proxyAdminAddress; 24 | }; 25 | 26 | export const ProxyFactory = async () => { 27 | return await ethers.getContractFactory( 28 | ERC1967Proxy.abi, 29 | ERC1967Proxy.bytecode, 30 | ); 31 | }; 32 | 33 | export const TransparentUpgradeableProxyFactory = async () => { 34 | return await ethers.getContractFactory( 35 | TransparentUpgradeableProxy.abi, 36 | TransparentUpgradeableProxy.bytecode, 37 | ); 38 | }; 39 | -------------------------------------------------------------------------------- /pkgs/contract/helpers/deploy/contractJsonIgnitionHelper.ts: -------------------------------------------------------------------------------- 1 | import fs from "node:fs"; 2 | import path from "node:path"; 3 | 4 | /** 5 | * デプロイされたアドレス情報をjsonファイルから取得するヘルパーメソッド 6 | * @param chainId チェーンID 7 | * @param contractName コントラクト名 <コントラクト名>Module#<コントラクト名>の形式で指定する。 8 | * @returns 9 | */ 10 | export function getContractAddress( 11 | chainId: string, 12 | contractName: string, 13 | ): string | undefined { 14 | try { 15 | // ファイルパスを構築 16 | const filePath = path.join( 17 | __dirname, 18 | "../../", 19 | "ignition", 20 | "deployments", 21 | `chain-${chainId}`, 22 | "deployed_addresses.json", 23 | ); 24 | 25 | // JSONファイルの内容を読み込み 26 | const fileContent = fs.readFileSync(filePath, "utf-8"); 27 | 28 | // JSONをオブジェクトにパース 29 | const deployedAddresses = JSON.parse(fileContent); 30 | 31 | // 指定されたコントラクト名のアドレスを返す 32 | return deployedAddresses[contractName]; 33 | } catch (error) { 34 | console.error("Error reading contract address:", error); 35 | return undefined; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /pkgs/contract/helpers/deploy/test.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackdays-io/toban/1ffd398b45703d2b9cf6117a5ca161489f3c3663/pkgs/contract/helpers/deploy/test.ts -------------------------------------------------------------------------------- /pkgs/contract/helpers/ens/constants.ts: -------------------------------------------------------------------------------- 1 | // NAME Wrapper Contract Address(sepolia) 2 | export const NAME_WRAPPER_CONTRACT_ADDRESS = 3 | "0x0635513f179D50A207757E05759CbD106d7dFcE8"; 4 | // contract.toban.ethのparent node(sepolia) 5 | export const CONTRACT_TOBAN_PARENT_NODE = 6 | "0x8f16dcf0ba3c4c5b2bb9786c84c45925294ff9e18b65e97dda3521708b071a33"; 7 | // public resolever contract address(sepolia) 8 | export const RESOLEVER_CONTRACT_ADDRESS = 9 | "0x8FADE66B79cC9f707aB26799354482EB93a5B7dD"; 10 | -------------------------------------------------------------------------------- /pkgs/contract/helpers/ens/function.ts: -------------------------------------------------------------------------------- 1 | import * as dotenv from "dotenv"; 2 | import type { HardhatRuntimeEnvironment } from "hardhat/types"; 3 | import { privateKeyToAccount } from "viem/accounts"; 4 | import { NAME_WRAPPER_ABI } from "./abi"; 5 | import { 6 | CONTRACT_TOBAN_PARENT_NODE, 7 | NAME_WRAPPER_CONTRACT_ADDRESS, 8 | RESOLEVER_CONTRACT_ADDRESS, 9 | } from "./constants"; 10 | 11 | dotenv.config(); 12 | 13 | const { PRIVATE_KEY } = process.env; 14 | 15 | // create Signer from Private Key 16 | const account = privateKeyToAccount(PRIVATE_KEY as `0x${string}`); 17 | 18 | /** 19 | * register subdomain method 20 | */ 21 | export const registerSubdomain = async ( 22 | hre: HardhatRuntimeEnvironment, 23 | label: string, 24 | ) => { 25 | const [bobWalletClient] = await hre.viem.getWalletClients(); 26 | // register subdomain methodを呼び出す。 27 | const txHash = await bobWalletClient.writeContract({ 28 | address: NAME_WRAPPER_CONTRACT_ADDRESS, 29 | abi: NAME_WRAPPER_ABI, 30 | functionName: "setSubnodeRecord", 31 | args: [ 32 | CONTRACT_TOBAN_PARENT_NODE, 33 | label, 34 | account.address, 35 | RESOLEVER_CONTRACT_ADDRESS, 36 | 0, 37 | 0, 38 | 0, 39 | ], 40 | account, 41 | }); 42 | 43 | console.log(`registerSubdomain tx: ${txHash}`); 44 | 45 | return txHash; 46 | }; 47 | -------------------------------------------------------------------------------- /pkgs/contract/helpers/upgrade/bigbang.ts: -------------------------------------------------------------------------------- 1 | import { ethers, viem } from "hardhat"; 2 | import type { Address } from "viem"; 3 | import { baseSalt, deployContract_Create2 } from "../deploy/Create2Factory"; 4 | 5 | /** 6 | * BigBang Contractをアップグレードするメソッド 7 | * @param contractAddress アップグレード対象のコントラクトアドレス 8 | * @param contractName アップグレード後のコントラクト名 9 | * @param params アップグレード時に必要なパラメータ 10 | * @returns 11 | */ 12 | export const upgradeBigBang = async ( 13 | contractAddress: Address, 14 | contractName: "BigBang_Mock_v2", 15 | create2DeployerAddress?: string, 16 | ) => { 17 | const UpgradedBigBangFactory = await ethers.getContractFactory(contractName); 18 | const UpgradedBigBang_ImplTx = 19 | await UpgradedBigBangFactory.getDeployTransaction(); 20 | const UpgradedBigBang_ImplAddress = await deployContract_Create2( 21 | baseSalt, 22 | UpgradedBigBang_ImplTx.data || "0x", 23 | ethers.keccak256(UpgradedBigBang_ImplTx.data), 24 | `${contractName}_Implementation`, 25 | create2DeployerAddress, 26 | ); 27 | 28 | const UpgradedBigBang = await viem.getContractAt( 29 | contractName, 30 | contractAddress, 31 | ); 32 | 33 | await UpgradedBigBang.write.upgradeToAndCall([ 34 | UpgradedBigBang_ImplAddress, 35 | "0x", 36 | ]); 37 | 38 | return { UpgradedBigBang }; 39 | }; 40 | -------------------------------------------------------------------------------- /pkgs/contract/helpers/upgrade/fractionToken.ts: -------------------------------------------------------------------------------- 1 | import { ethers, viem } from "hardhat"; 2 | import type { Address } from "viem"; 3 | import { baseSalt, deployContract_Create2 } from "../deploy/Create2Factory"; 4 | 5 | /** 6 | * FractionToken Contractをアップグレードするメソッド 7 | * @param contractAddress アップグレード対象のコントラクトアドレス 8 | * @param contractName アップグレード後のコントラクト名 9 | * @param params アップグレード時に必要なパラメータ 10 | * @returns 11 | */ 12 | export const upgradeFractionToken = async ( 13 | contractAddress: Address, 14 | contractName: "FractionToken_Mock_v2", 15 | create2DeployerAddress?: string, 16 | params?: unknown[], 17 | ) => { 18 | // 新しいコントラクトのファクトリーを取得 19 | const UpgradedFractionTokenFactory = 20 | await ethers.getContractFactory(contractName); 21 | const UpgradedFractionToken_ImplTx = 22 | await UpgradedFractionTokenFactory.getDeployTransaction(); 23 | const UpgradedFractionToken_ImplAddress = await deployContract_Create2( 24 | baseSalt, 25 | UpgradedFractionToken_ImplTx.data || "0x", 26 | ethers.keccak256(UpgradedFractionToken_ImplTx.data), 27 | `${contractName}_Implementation`, 28 | create2DeployerAddress, 29 | ); 30 | 31 | const UpgradedFractionToken = await viem.getContractAt( 32 | contractName, 33 | contractAddress, 34 | ); 35 | 36 | await UpgradedFractionToken.write.upgradeToAndCall([ 37 | UpgradedFractionToken_ImplAddress, 38 | "0x", 39 | ]); 40 | 41 | return { UpgradedFractionToken }; 42 | }; 43 | -------------------------------------------------------------------------------- /pkgs/contract/helpers/upgrade/splitsCreatorFactory.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades, viem } from "hardhat"; 2 | import type { Address } from "viem"; 3 | import { baseSalt, deployContract_Create2 } from "../deploy/Create2Factory"; 4 | 5 | /** 6 | * SplitsCreatorFactory Contractをアップグレードするメソッド 7 | * @param contractAddress アップグレード対象のコントラクトアドレス 8 | * @param contractName アップグレード後のコントラクト名 9 | * @param params アップグレード時に必要なパラメータ 10 | * @returns 11 | */ 12 | export async function upgradeSplitsCreatorFacotry( 13 | contractAddress: Address, 14 | contractName: "SplitsCreatorFactory_Mock_v2", 15 | create2DeployerAddress?: string, 16 | ) { 17 | // 新しいコントラクトのファクトリーを取得 18 | const UpgradedSplitsCreatorFactory_Factory = 19 | await ethers.getContractFactory(contractName); 20 | const UpgradedSplitsCreatorFactory_ImplTx = 21 | await UpgradedSplitsCreatorFactory_Factory.getDeployTransaction(); 22 | const UpgradedSplitsCreatorFactory_ImplAddress = await deployContract_Create2( 23 | baseSalt, 24 | UpgradedSplitsCreatorFactory_ImplTx.data || "0x", 25 | ethers.keccak256(UpgradedSplitsCreatorFactory_ImplTx.data), 26 | `${contractName}_Implementation`, 27 | create2DeployerAddress, 28 | ); 29 | 30 | const UpgradedSplitsCreatorFactory = await viem.getContractAt( 31 | contractName, 32 | contractAddress, 33 | ); 34 | 35 | await UpgradedSplitsCreatorFactory.write.upgradeToAndCall([ 36 | UpgradedSplitsCreatorFactory_ImplAddress, 37 | "0x", 38 | ]); 39 | 40 | return { UpgradedSplitsCreatorFactory }; 41 | } 42 | -------------------------------------------------------------------------------- /pkgs/contract/helpers/util/sqrt.ts: -------------------------------------------------------------------------------- 1 | export function sqrt(y: bigint): bigint { 2 | if (y > 3n) { 3 | let z: bigint = y; 4 | let x: bigint = y / 2n + 1n; 5 | while (x < z) { 6 | z = x; 7 | x = (y / x + x) / 2n; 8 | } 9 | return z; 10 | } 11 | if (y !== 0n) { 12 | return 1n; 13 | } 14 | return 0n; 15 | } 16 | -------------------------------------------------------------------------------- /pkgs/contract/ignition/deployments/chain-11155111/deployed_addresses.json: -------------------------------------------------------------------------------- 1 | { 2 | "LockModule#Lock": "0x427e5da5aBd9DdE6272EDb6d7388Fb1C7AB7E519" 3 | } 4 | -------------------------------------------------------------------------------- /pkgs/contract/ignition/modules/Lock.ts: -------------------------------------------------------------------------------- 1 | import { buildModule } from "@nomicfoundation/hardhat-ignition/modules"; 2 | import { parseEther } from "viem"; 3 | 4 | const JAN_1ST_2030 = 1893456000; 5 | const ONE_GWEI: bigint = parseEther("0.001"); 6 | 7 | /** 8 | * デプロイスクリプト 9 | */ 10 | const LockModule = buildModule("LockModule", (m) => { 11 | const unlockTime = m.getParameter("unlockTime", JAN_1ST_2030); 12 | const lockedAmount = m.getParameter("lockedAmount", ONE_GWEI); 13 | 14 | const lock = m.contract("Lock", [unlockTime], { 15 | value: lockedAmount, 16 | }); 17 | 18 | return { lock }; 19 | }); 20 | 21 | export default LockModule; 22 | -------------------------------------------------------------------------------- /pkgs/contract/outputs/contracts-holesky.json: -------------------------------------------------------------------------------- 1 | { 2 | "contracts": { 3 | "Hats": "0x3bc1A0Ad72417f2d411118085256fC53CBdDd137", 4 | "HatsModuleFactory": "0xfE661c01891172046feE16D3a57c3Cf456729efA", 5 | "PullSplitsFactory": "0x80f1B766817D04870f115fEBbcCADF8DBF75E017", 6 | "BigBang": "\"0x3E70d10aCdcC14B6C31DA26DcC195a6EDf1C2c16\"", 7 | "FractionToken": "\"0x54889278bf4F16ACAa3CC1402C987A6C42a5308B\"", 8 | "SplitsCreatorFactory": "\"0x2b44c1F5B0D2a6a39F83effbF48aA09C833EBe12\"", 9 | "SplitsCreator": "\"0x09b853E0945d1c86af10b5665472501bD5F6627c\"", 10 | "HatsTimeFrameModule": "\"0x808996331ADD2715854e31e3dd4f9a736DE23604\"", 11 | "ProxyAdmin": "\"0xdAEC7C851DA8E9b041e4592fdCF19843Bc1f8bE8\"" 12 | }, 13 | "implementations": { 14 | "FractionToken_Implementation": "\"0x7F096Ba747bEAfFd61c4f5BEAeD4756fd767cc7e\"", 15 | "SplitsCreatorFactory_Implementation": "\"0xfD82EeaE18133F9629Ee220Cf35d06951bccEf51\"", 16 | "BigBang_Implementation": "\"0x8F7FaFBabdAD34Fb88785E4a07fef68F2aDb470E\"" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /pkgs/contract/outputs/contracts-sepolia.json: -------------------------------------------------------------------------------- 1 | { 2 | "contracts": { 3 | "Hats": "0x3bc1A0Ad72417f2d411118085256fC53CBdDd137", 4 | "HatsModuleFactory": "0x0a3f85fa597B6a967271286aA0724811acDF5CD9", 5 | "PullSplitsFactory": "0x80f1B766817D04870f115fEBbcCADF8DBF75E017", 6 | "BigBang": "\"0x2662b0Dc151bbD9C0e5F1b5bD8674b4eD3E92D0b\"", 7 | "FractionToken": "\"0xd6031f9543bEB0963e32CA2AC474de69D0515059\"", 8 | "SplitsCreatorFactory": "\"0x447f22AB169eE89118E03f2f2DCbE8667010340D\"", 9 | "SplitsCreator": "\"0x4BbA4e70437bF162F8EfB8de88E5ECb3C19e11e6\"", 10 | "HatsTimeFrameModule": "\"0xa5449bAe3512a81D63a60E65E525B443a78de3b0\"", 11 | "ProxyAdmin": "\"0xdAEC7C851DA8E9b041e4592fdCF19843Bc1f8bE8\"" 12 | }, 13 | "implementations": { 14 | "FractionToken_Implementation": "\"0x6d701256205019e40cE02e13D799ed2cd3BBE8e8\"", 15 | "SplitsCreatorFactory_Implementation": "\"0x5850255ed6DC90476eea2739dAc0B2829ac7e825\"", 16 | "BigBang_Implementation": "\"0xe5c4dd13786Acc50228Fe1878709d10ef863503B\"" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /pkgs/contract/scripts/createSplit.ts: -------------------------------------------------------------------------------- 1 | import { network, viem } from "hardhat"; 2 | import { loadDeployedContractAddresses } from "../helpers/deploy/contractsJsonHelper"; 3 | 4 | const create = async () => { 5 | // SplitsCreatorコントラクトのアドレスをjsonファイルから取得してくる。 6 | const { 7 | contracts: { SplitsCreator }, 8 | } = loadDeployedContractAddresses(network.name); 9 | 10 | const contract = await viem.getContractAt( 11 | "SplitsCreator", 12 | SplitsCreator, 13 | // "0xf7f536b25d3f1aEb84E32A35ca8E48b6fd0597A7" 14 | ); 15 | 16 | const res = await contract.write.create([ 17 | [ 18 | { 19 | hatId: BigInt( 20 | "0x0000023900010001000000000000000000000000000000000000000000000000", 21 | ), 22 | multiplierBottom: 1n, 23 | multiplierTop: 1n, 24 | wearers: ["0x777EE5eeEd30c3712bEE6C83260D786857d9C556"], 25 | }, 26 | ], 27 | ]); 28 | 29 | console.log(res); 30 | }; 31 | 32 | create(); 33 | -------------------------------------------------------------------------------- /pkgs/contract/scripts/upgrade/bigbang.ts: -------------------------------------------------------------------------------- 1 | import * as dotenv from "dotenv"; 2 | import { network } from "hardhat"; 3 | import { loadDeployedContractAddresses } from "../../helpers/deploy/contractsJsonHelper"; 4 | import { upgradeBigBang } from "../../helpers/upgrade/bigbang"; 5 | 6 | dotenv.config(); 7 | 8 | /** 9 | * BigBangをアップグレードするスクリプト 10 | * @returns 11 | */ 12 | const upgrade = async () => { 13 | console.log( 14 | "##################################### [Upgrade START] #####################################", 15 | ); 16 | 17 | // BigBangコントラクトの各アドレスをjsonファイルから取得してくる。 18 | const { 19 | contracts: { BigBang }, 20 | } = loadDeployedContractAddresses(network.name); 21 | 22 | // BigBangコントラクトをアップグレードする 23 | const newBigBang = await upgradeBigBang( 24 | BigBang, 25 | "BigBang_Mock_v2", // ここにアップグレード後のBigBangのコントラクト名を指定する。 26 | ); 27 | 28 | console.log("upgrded address:", newBigBang.address); 29 | 30 | console.log( 31 | "##################################### [Upgrade END] #####################################", 32 | ); 33 | 34 | return; 35 | }; 36 | 37 | upgrade(); 38 | -------------------------------------------------------------------------------- /pkgs/contract/scripts/upgrade/fractionToken.ts: -------------------------------------------------------------------------------- 1 | import * as dotenv from "dotenv"; 2 | import { network } from "hardhat"; 3 | import { loadDeployedContractAddresses } from "../../helpers/deploy/contractsJsonHelper"; 4 | import { upgradeFractionToken } from "../../helpers/upgrade/fractionToken"; 5 | 6 | dotenv.config(); 7 | 8 | /** 9 | * FractionTokenをアップグレードするスクリプト 10 | * @returns 11 | */ 12 | const upgrade = async () => { 13 | console.log( 14 | "##################################### [Upgrade START] #####################################", 15 | ); 16 | 17 | // FractionTokenコントラクトの各アドレスをjsonファイルから取得してくる。 18 | const { 19 | contracts: { FractionToken }, 20 | } = loadDeployedContractAddresses(network.name); 21 | 22 | // FractionTokenコントラクトをアップグレードする 23 | const newFractionToken = await upgradeFractionToken( 24 | FractionToken, 25 | "FractionToken", // ここにアップグレード後のFractionTokenのコントラクト名を指定する。 26 | ); 27 | 28 | console.log("upgrded address:", newFractionToken.address); 29 | 30 | console.log( 31 | "##################################### [Upgrade END] #####################################", 32 | ); 33 | 34 | return; 35 | }; 36 | 37 | upgrade(); 38 | -------------------------------------------------------------------------------- /pkgs/contract/scripts/upgrade/hatsHatCreatorModule.ts: -------------------------------------------------------------------------------- 1 | import { network, viem } from "hardhat"; 2 | import { deployHatsHatCreatorModule } from "../../helpers/deploy/Hats"; 3 | 4 | const upgrade = async () => { 5 | const module = await deployHatsHatCreatorModule( 6 | "0x0000000000000000000000000000000000000001", 7 | ); 8 | 9 | const bigBang = await viem.getContractAt( 10 | "BigBang", 11 | "0x3E70d10aCdcC14B6C31DA26DcC195a6EDf1C2c16", 12 | ); 13 | 14 | await bigBang.write.setHatsHatCreatorModuleImpl([ 15 | module.HatsHatCreatorModule.address, 16 | ]); 17 | 18 | console.log( 19 | "HatsHatCreatorModule module:\n", 20 | `npx hardhat verify ${module.HatsHatCreatorModule.address} "0.0.0" 0x0000000000000000000000000000000000000001 --network ${network.name}\n`, 21 | ); 22 | }; 23 | 24 | upgrade(); 25 | -------------------------------------------------------------------------------- /pkgs/contract/scripts/upgrade/hatsTimeFrameModule.ts: -------------------------------------------------------------------------------- 1 | import { network, viem } from "hardhat"; 2 | import { deployHatsTimeFrameModule } from "../../helpers/deploy/Hats"; 3 | 4 | const upgrade = async () => { 5 | const module = await deployHatsTimeFrameModule( 6 | "0x0000000000000000000000000000000000000001", 7 | ); 8 | 9 | const bigBang = await viem.getContractAt( 10 | "BigBang", 11 | "0x3E70d10aCdcC14B6C31DA26DcC195a6EDf1C2c16", 12 | ); 13 | 14 | await bigBang.write.setHatsTimeFrameModuleImpl([ 15 | module.HatsTimeFrameModule.address, 16 | ]); 17 | 18 | console.log( 19 | "HatsTimeframeModule module:\n", 20 | `npx hardhat verify ${module.HatsTimeFrameModule.address} "0.0.0" 0x0000000000000000000000000000000000000001 --network ${network.name}\n`, 21 | ); 22 | }; 23 | 24 | upgrade(); 25 | -------------------------------------------------------------------------------- /pkgs/contract/tasks/BigBang/bigbang.ts: -------------------------------------------------------------------------------- 1 | import { task } from "hardhat/config"; 2 | import type { HardhatRuntimeEnvironment } from "hardhat/types"; 3 | import { loadDeployedContractAddresses } from "../../helpers/deploy/contractsJsonHelper"; 4 | 5 | interface BigBangTaskArgs { 6 | owner: string; 7 | tophatdetails: string; 8 | tophatimageuri: string; 9 | hatterhatdetails: string; 10 | hatterhatimageuri: string; 11 | } 12 | 13 | /** 14 | * 【Task】execute bigbang method 15 | */ 16 | task("bigbang", "bigbang") 17 | .addParam("owner", "The address of the user who will own the topHat.") 18 | .addParam("tophatdetails", "The details of the topHat.") 19 | .addParam("tophatimageuri", "The image URI of the topHat.") 20 | .addParam("hatterhatdetails", "The details of the hatterHat.") 21 | .addParam("hatterhatimageuri", "The image URI of the hatterHat.") 22 | .setAction( 23 | async (taskArgs: BigBangTaskArgs, hre: HardhatRuntimeEnvironment) => { 24 | console.log( 25 | "################################### [START] ###################################", 26 | ); 27 | const [bobWalletClient] = await hre.viem.getWalletClients(); 28 | 29 | // BigBangコントラクトのアドレスをjsonファイルから取得してくる。 30 | const { 31 | contracts: { BigBang }, 32 | } = loadDeployedContractAddresses(hre.network.name); 33 | 34 | // create BigBang instance 35 | const bigbang = await hre.viem.getContractAt("BigBang", BigBang); 36 | 37 | const address = bobWalletClient.account?.address; 38 | if (!address) { 39 | throw new Error("Wallet client account address is undefined"); 40 | } 41 | 42 | // call bigbang method 43 | const tx = await bigbang.write.bigbang([ 44 | address, 45 | taskArgs.tophatdetails, 46 | taskArgs.tophatimageuri, 47 | taskArgs.hatterhatdetails, 48 | taskArgs.hatterhatimageuri, 49 | ]); 50 | 51 | console.log(`tx: ${tx}`); 52 | 53 | console.log( 54 | "################################### [END] ###################################", 55 | ); 56 | }, 57 | ); 58 | -------------------------------------------------------------------------------- /pkgs/contract/tasks/HatsTimeFrameModule/getWoreTime.ts: -------------------------------------------------------------------------------- 1 | import { task } from "hardhat/config"; 2 | import type { HardhatRuntimeEnvironment } from "hardhat/types"; 3 | import { loadDeployedContractAddresses } from "../../helpers/deploy/contractsJsonHelper"; 4 | 5 | /** 6 | * 【Task】get getWoreTime of HatsTimeFrameModule 7 | */ 8 | task("getWoreTime", "getWoreTime") 9 | .addParam("wearer", "address of wearer") 10 | .setAction( 11 | async ( 12 | taskArgs: { 13 | wearer: `0x${string}`; 14 | }, 15 | hre: HardhatRuntimeEnvironment, 16 | ) => { 17 | console.log( 18 | "################################### [START] ###################################", 19 | ); 20 | 21 | // BigBangコントラクトのアドレスをjsonファイルから取得してくる。 22 | const { 23 | contracts: { HatsTimeFrameModule }, 24 | } = loadDeployedContractAddresses(hre.network.name); 25 | 26 | // create HatsTimeFrameModule instance 27 | const hatsTimeFrameModuleByBigBang = await hre.viem.getContractAt( 28 | "HatsTimeFrameModule", 29 | HatsTimeFrameModule, 30 | ); 31 | 32 | // call getWoreTime method 33 | const woreTime = await hatsTimeFrameModuleByBigBang.read.getWoreTime([ 34 | taskArgs.wearer, 35 | 0n, 36 | ]); 37 | 38 | console.log(`woreTime: ${woreTime}`); 39 | 40 | console.log( 41 | "################################### [END] ###################################", 42 | ); 43 | }, 44 | ); 45 | -------------------------------------------------------------------------------- /pkgs/contract/tasks/HatsTimeFrameModule/mintHat.ts: -------------------------------------------------------------------------------- 1 | import { task } from "hardhat/config"; 2 | import type { HardhatRuntimeEnvironment } from "hardhat/types"; 3 | import { loadDeployedContractAddresses } from "../../helpers/deploy/contractsJsonHelper"; 4 | 5 | /** 6 | * 【Task】call mintHat of HatsTimeFrameModule 7 | */ 8 | task("mintHat", "mintHat") 9 | .addParam("hatid", "hatid") 10 | .addParam("wearer", "address of wearer") 11 | .setAction( 12 | async ( 13 | taskArgs: { 14 | hatid: bigint; 15 | wearer: `0x${string}`; 16 | }, 17 | hre: HardhatRuntimeEnvironment, 18 | ) => { 19 | console.log( 20 | "################################### [START] ###################################", 21 | ); 22 | 23 | // BigBangコントラクトのアドレスをjsonファイルから取得してくる。 24 | const { 25 | contracts: { HatsTimeFrameModule }, 26 | } = loadDeployedContractAddresses(hre.network.name); 27 | 28 | // create HatsTimeFrameModule instance 29 | const hatsTimeFrameModuleByBigBang = await hre.viem.getContractAt( 30 | "HatsTimeFrameModule", 31 | HatsTimeFrameModule, 32 | ); 33 | 34 | // call mintHat method 35 | const tx = await hatsTimeFrameModuleByBigBang.write.mintHat([ 36 | taskArgs.hatid, 37 | taskArgs.wearer, 38 | 0n, 39 | ]); 40 | 41 | console.log(`tx: ${tx}`); 42 | 43 | console.log( 44 | "################################### [END] ###################################", 45 | ); 46 | }, 47 | ); 48 | -------------------------------------------------------------------------------- /pkgs/contract/tasks/ens/registerSubdomain.ts: -------------------------------------------------------------------------------- 1 | import { task } from "hardhat/config"; 2 | import type { HardhatRuntimeEnvironment } from "hardhat/types"; 3 | import { registerSubdomain } from "../../helpers/ens/function"; 4 | 5 | /** 6 | * 【Task】register subdomain 7 | */ 8 | task("registerSubdomain", "register subdomain") 9 | .addParam("label", "label for subdomain") 10 | .setAction( 11 | async ( 12 | taskArgs: { 13 | label: string; 14 | }, 15 | hre: HardhatRuntimeEnvironment, 16 | ) => { 17 | console.log( 18 | "################################### [START] ###################################", 19 | ); 20 | 21 | // call registerSubdomain method 22 | await registerSubdomain(hre, taskArgs.label); 23 | 24 | console.log( 25 | "################################### [END] ###################################", 26 | ); 27 | }, 28 | ); 29 | -------------------------------------------------------------------------------- /pkgs/contract/tasks/index.ts: -------------------------------------------------------------------------------- 1 | import * as bigbang from "./BigBang/bigbang"; 2 | import * as getWoreTime from "./HatsTimeFrameModule/getWoreTime"; 3 | import * as mintHat from "./HatsTimeFrameModule/mintHat"; 4 | import * as registerSubdomain from "./ens/registerSubdomain"; 5 | import * as getBalance from "./utils/getBalance"; 6 | import * as getChainInfo from "./utils/getChainInfo"; 7 | import * as getContractAddress from "./utils/getContractAddress"; 8 | import * as resetContractAddressesJson from "./utils/resetContractAddressesJson"; 9 | 10 | export { 11 | bigbang, 12 | getBalance, 13 | getChainInfo, 14 | getContractAddress, 15 | getWoreTime, 16 | mintHat, 17 | registerSubdomain, 18 | resetContractAddressesJson, 19 | }; 20 | -------------------------------------------------------------------------------- /pkgs/contract/tasks/utils/getBalance.ts: -------------------------------------------------------------------------------- 1 | import { task } from "hardhat/config"; 2 | import type { HardhatRuntimeEnvironment } from "hardhat/types"; 3 | import { formatEther } from "viem"; 4 | 5 | /** 6 | * 【Task】get the balance of the account 7 | */ 8 | task("getBalance", "getBalance").setAction( 9 | async (_taskArgs: Record, hre: HardhatRuntimeEnvironment) => { 10 | console.log( 11 | "################################### [START] ###################################", 12 | ); 13 | const [bobWalletClient] = await hre.viem.getWalletClients(); 14 | 15 | if (!bobWalletClient.account?.address) { 16 | throw new Error("Wallet client account address is undefined"); 17 | } 18 | 19 | const publicClient = await hre.viem.getPublicClient(); 20 | const bobBalance = await publicClient.getBalance({ 21 | address: bobWalletClient.account.address, 22 | }); 23 | 24 | console.log( 25 | `Balance of ${bobWalletClient.account.address}: ${formatEther( 26 | bobBalance, 27 | )} ETH`, 28 | ); 29 | 30 | console.log( 31 | "################################### [END] ###################################", 32 | ); 33 | }, 34 | ); 35 | -------------------------------------------------------------------------------- /pkgs/contract/tasks/utils/getChainInfo.ts: -------------------------------------------------------------------------------- 1 | import { task } from "hardhat/config"; 2 | import type { HardhatRuntimeEnvironment } from "hardhat/types"; 3 | import { formatEther } from "viem"; 4 | 5 | /** 6 | * 【Task】 getChainInfo of connected chain 7 | */ 8 | task("getChainInfo", "getChainInfo of connected chain").setAction( 9 | async (_taskArgs: Record, hre: HardhatRuntimeEnvironment) => { 10 | console.log( 11 | "################################### [START] ###################################", 12 | ); 13 | 14 | const publicClient = await hre.viem.getPublicClient(); 15 | const chainId = await publicClient.getChainId(); 16 | const blockNumber = await publicClient.getBlockNumber(); 17 | const count = await publicClient.getBlockTransactionCount(); 18 | const gasPrice = await publicClient.getGasPrice(); 19 | const gasPriceInEther = formatEther(gasPrice); 20 | 21 | console.log(` 22 | Chain ID: ${chainId} 23 | Block Number: ${blockNumber} 24 | Transaction Count: ${count} 25 | Gas Price: ${gasPriceInEther} ETH 26 | `); 27 | 28 | console.log( 29 | "################################### [END] ###################################", 30 | ); 31 | }, 32 | ); 33 | -------------------------------------------------------------------------------- /pkgs/contract/tasks/utils/getContractAddress.ts: -------------------------------------------------------------------------------- 1 | import { task } from "hardhat/config"; 2 | import type { HardhatRuntimeEnvironment } from "hardhat/types"; 3 | import { getContractAddress } from "../../helpers/deploy/contractJsonIgnitionHelper"; 4 | 5 | interface GetContractAddressTaskArgs { 6 | contract: string; 7 | } 8 | 9 | /** 10 | * 【Task】 get deployed contract address 11 | */ 12 | task("getContractAddress", "getContractAddress of connected chain") 13 | .addParam("contract", "contract name in Moduel#") 14 | .setAction( 15 | async ( 16 | taskArgs: GetContractAddressTaskArgs, 17 | hre: HardhatRuntimeEnvironment, 18 | ) => { 19 | console.log( 20 | "################################### [START] ###################################", 21 | ); 22 | 23 | const publicClient = await hre.viem.getPublicClient(); 24 | const chainId = (await publicClient.getChainId()).toString(); 25 | // get contract name 26 | const contractName = `${taskArgs.contract}Module#${taskArgs.contract}`; 27 | 28 | // get contract address 29 | const contractAddress = getContractAddress(chainId, contractName); 30 | 31 | console.log(` 32 | ${contractName} 's address is ${contractAddress} 33 | `); 34 | 35 | console.log( 36 | "################################### [END] ###################################", 37 | ); 38 | }, 39 | ); 40 | -------------------------------------------------------------------------------- /pkgs/contract/tasks/utils/resetContractAddressesJson.ts: -------------------------------------------------------------------------------- 1 | import "dotenv/config"; 2 | import { task } from "hardhat/config"; 3 | import type { HardhatRuntimeEnvironment } from "hardhat/types"; 4 | import { resetContractAddressesJson } from "../../helpers/deploy/contractsJsonHelper"; 5 | 6 | task("resetContractAddressesJson", "resetContractAddressesJson").setAction( 7 | async (taskArgs: undefined, hre: HardhatRuntimeEnvironment) => { 8 | // call reset contract address json file 9 | resetContractAddressesJson({ network: hre.network.name }); 10 | }, 11 | ); 12 | -------------------------------------------------------------------------------- /pkgs/contract/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2020", 4 | "module": "commonjs", 5 | "esModuleInterop": true, 6 | "forceConsistentCasingInFileNames": true, 7 | "strict": true, 8 | "skipLibCheck": true, 9 | "resolveJsonModule": true 10 | }, 11 | "exclude": ["node_modules", "artifacts/**/*.json"] 12 | } 13 | -------------------------------------------------------------------------------- /pkgs/document/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | 4 | # Production 5 | /build 6 | 7 | # Generated files 8 | .docusaurus 9 | .cache-loader 10 | 11 | # Misc 12 | .DS_Store 13 | .env.local 14 | .env.development.local 15 | .env.test.local 16 | .env.production.local 17 | 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | -------------------------------------------------------------------------------- /pkgs/document/README.md: -------------------------------------------------------------------------------- 1 | # Website 2 | 3 | This website is built using [Docusaurus](https://docusaurus.io/), a modern static website generator. 4 | 5 | ### Installation 6 | 7 | ``` 8 | $ yarn 9 | ``` 10 | 11 | ### Local Development 12 | 13 | ``` 14 | $ yarn start 15 | ``` 16 | 17 | This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server. 18 | 19 | ### Build 20 | 21 | ``` 22 | $ yarn build 23 | ``` 24 | 25 | This command generates static content into the `build` directory and can be served using any static contents hosting service. 26 | 27 | ### Deployment 28 | 29 | Using SSH: 30 | 31 | ``` 32 | $ USE_SSH=true yarn deploy 33 | ``` 34 | 35 | Not using SSH: 36 | 37 | ``` 38 | $ GIT_USER= yarn deploy 39 | ``` 40 | 41 | If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. 42 | -------------------------------------------------------------------------------- /pkgs/document/docs/Glossary.md: -------------------------------------------------------------------------------- 1 | # 用語集 2 | Toban-当番-に登場する専門用語についての解説ページです。 3 | - 241221 初稿(takerun) 4 | ## 5 | - ユーザー名: 新規登録時にtoban.ethのサブドメイン形式で取得できる個人のアカウント名 6 | - Workspace(s): Toban運用中のプロジェクト名、及びプロジェクト名一覧のこと 7 | - Role: 報酬分配受取の権利となる「役割」。プロジェクトオーナーがRoleを新規作成、メンバーへの付与(Assign)が可能。役割の「概要」「責務」を定義して入力する。Roleを付与されたメンバーは、他人へ「AssistCredit」の送付が可能。 8 | - Split(s): 実行する報酬分配の内訳、及び報酬分配実行履歴一覧のこと。実行するSplit毎に、Roleに対して分配率を設定する(Create Splitter)。Splitsページでは付与されたRoleに基づいて算出された分配率がユーザー毎に可視化される。 9 | - AssistCredit: 自分の持つRoleの範疇でお手伝いしてもらった場合に、自身のRoleを分割する形式で他人に譲渡するトークン。Role同様に報酬分配受取の権利となる。Roleを持ったメンバーはユーザー名またはウォレットアドレスを指定して、決められた割合のAssistCreditを送付できる。 10 | - Splitter: 実行する報酬分配の内訳(「分配率」)を"個人の保有するAssistCredit"に基づいて算出する機能。 11 | - "あるRoleのAssistCredit保有量"に基づいた分配率の計算式は、SQRT(Roleに従事した期間) * Role固有の倍率 * AssistCredit保有率 12 | - Roleに従事した期間[sec] : Roleを剥奪された時刻(または現在時刻) - Roleを付与された時刻 13 |  - 従事した期間が断続的に存在する場合は、それらを加算する。 14 | - Role固有の倍率: コミュニティにおける合意形成のもと、他のRoleとの兼ね合いで決定する値 15 | - AssistCredit保有率: 該当Roleの保有AssistCredit量/該当Roleの発行済AssistCredit総量 16 | - Roleを直接持たずAssistCreditのみ持っているメンバーの場合はAssistCreditをもらったメンバーの従事期間が適用される(暫定) 17 | -------------------------------------------------------------------------------- /pkgs/document/docs/getstarted/admin/assign-role.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 役割を付与 3 | sidebar_position: 3 4 | --- 5 | 6 | # 役割を付与 7 | 8 | ユーザー名もしくはウォレットアドレスを指定して、活動に参加しているメンバーに役割を付与していきます。 9 | 付与した役割はユーザーごとに一時休止と削除ができます。 10 | -------------------------------------------------------------------------------- /pkgs/document/docs/getstarted/admin/create-role.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 役割を作成 3 | sidebar_position: 2 4 | --- 5 | 6 | # 役割を作成 7 | 8 | ワークスペースを作成したら役割を作成します。 9 | 10 | 役割の詳細はあとからでも変更できます。 11 | -------------------------------------------------------------------------------- /pkgs/document/docs/getstarted/admin/create-splitter.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: トークン(報酬)分配コントラクトを作成 3 | sidebar_position: 4 4 | --- 5 | 6 | # トークン(報酬)分配コントラクトを作成 7 | 8 | 報酬を分配する役割・メンバーを選択して分配スマートコントラクトを作成します。 9 | 10 | ## 分配率の計算 11 | 12 | 分配率は以下の変数と式によって算出される個人ごとのスコアに基づきます。 13 | 14 | - 役割の従事期間 15 | - 役割ごとの係数 16 | - アシストクレジットの保有率 17 | 18 | ### 個人のスコア 19 | 20 | 個人 \(i\) が持つすべての役割についてスコアを合計します: 21 | 22 | $$ 23 | \text{個人のスコア}_i = \sum_{j=1}^{m_i} \left( \sqrt{\text{従事期間}_{i,j}} \times \text{役割係数}_{i,j} \times \text{アシストクレジット保有率}_{i,j} \right) 24 | $$ 25 | 26 | - mi: 個人 \(i\) が持つ役割の数 27 | - j: 役割を表すインデックス 28 | 29 | ※役割を直接持たず、アシストクレジットのみ持っているメンバーの場合は、アシストクレジットをもらったメンバーの従事期間が適用されます。 30 | 31 | ### 全員のスコアの合計 32 | 33 | すべての個人のスコアを合計します: 34 | 35 | $$ 36 | \text{全員のスコア合計} = \sum_{i=1}^{n} \text{個人のスコア}_i 37 | $$ 38 | 39 | - n: 全体の人数 40 | 41 | ### 各人の分配率の計算 42 | 43 | 個人 \(i\) の分配率: 44 | 45 | $$ 46 | \text{分配率}_i = \frac{\text{個人のスコア}_i}{\text{全員のスコア合計}} 47 | $$ 48 | -------------------------------------------------------------------------------- /pkgs/document/docs/getstarted/admin/create-workspace.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: ワークスペースを作成 3 | sidebar_position: 1 4 | --- 5 | 6 | # ワークスペースを作成 7 | 8 | まず、ワークスペースを作成します。ここで作成するワークスペースは組織やプロジェクト単位のものになります。 9 | ワークスペースを作成すると、Toban を扱うために必要な各スマートコントラクトが自動的に作成されます。 10 | 11 | ワークスペースの詳細はあとからでも変更できます。 12 | -------------------------------------------------------------------------------- /pkgs/document/docs/getstarted/admin/distribute-rewards.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 報酬(トークン)を分配 3 | sidebar_position: 5 4 | --- 5 | 6 | # 報酬(トークン)を分配 7 | 8 | 任意のトークン(ネイティブトークンまたは ERC20 トークン)をまとめて分配コントラクトに送金することで、分配率に応じて自動的に分配されます。 9 | -------------------------------------------------------------------------------- /pkgs/document/docs/getstarted/admin/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 始める(管理者) 3 | --- 4 | 5 | # 始める (管理者) 6 | 7 | Toban をつかうにあたってのステップ 8 | 9 | 1. ワークスペースを作成 10 | 2. 役割を作成 11 | 3. 役割を付与 12 | 4. 分配コントラクトを作成 13 | 5. 報酬(トークン)を分配 14 | -------------------------------------------------------------------------------- /pkgs/document/docs/getstarted/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Tobanを始める 3 | --- 4 | 5 | # Toban を始める 6 | 7 | ## ユーザー作成 8 | -------------------------------------------------------------------------------- /pkgs/document/docs/getstarted/member/assist-token.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: アシストトークン 3 | sidebar_position: 3 4 | --- 5 | 6 | # アシストトークン 7 | 8 | アシストトークンはすこし手伝ってくれた他のメンバーや、ふらっと現れて助けてくれた人に対してお礼としてわたすことができるトークンです。 9 | ユーザー名もしくはウォレットアドレスをつかって送付しあうことができます。 10 | 11 | アシストトークンをわたすと、受け取る報酬の一部が渡した相手にも分配されるようになります。 12 | -------------------------------------------------------------------------------- /pkgs/document/docs/getstarted/member/check-role.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 役割を確認する 3 | sidebar_position: 2 4 | --- 5 | 6 | # 役割を確認する 7 | 8 | 役割のページには、やらなければならないことや与えられている権限などを確認することができます。 9 | 役割を一時休止したり、やめたりすることもできます。 10 | -------------------------------------------------------------------------------- /pkgs/document/docs/getstarted/member/get-rewards.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 報酬を受け取る 3 | sidebar_position: 4 4 | --- 5 | 6 | # 報酬を受け取る 7 | 8 | Toban ではネイティブトークン、ERC20 トークンを分配する機能があります。 9 | 分配を自動的に処理するスマートコントラクトにトークンが送付されるたびに、自動的に受け取ることができます。 10 | 11 | 分配率の計算式は[こちらを参照してください](/docs/getstarted/admin/create-splitter)。 12 | -------------------------------------------------------------------------------- /pkgs/document/docs/getstarted/member/get-role.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 役割をもらう 3 | sidebar_position: 1 4 | --- 5 | 6 | # 役割をもらう 7 | 8 | ユーザー名もしくはウォレットアドレスを管理者に伝えて、役割を付与してもらいましょう。 9 | 付与された役割はワークスペースのホーム画面などから確認することができます。 10 | -------------------------------------------------------------------------------- /pkgs/document/docs/getstarted/member/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 始める(メンバー) 3 | --- 4 | 5 | # 始める (メンバー) 6 | 7 | Toban をつかうにあたってのステップ 8 | 9 | 1. 役割をもらう 10 | 2. 役割を確認する 11 | 3. アシストトークンをわたす・もらう 12 | 4. 報酬(トークン)を受け取る 13 | -------------------------------------------------------------------------------- /pkgs/document/docs/howToUse.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: How to use 3 | --- 4 | 5 | # How to use 6 | -------------------------------------------------------------------------------- /pkgs/document/docs/supportedNetworks.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Supported Networks 3 | --- 4 | 5 | # Table of Contract Addresses 6 | 7 | ## Sepolia 8 | 9 | | Name | ContractAddress | 10 | | ----------------------- | ------------------------------------------ | 11 | | BigBang | 0x5d7a64Cc808294C516076d371685ed4E6aDd6337 | 12 | | FractionToken | 0xb8f7ca7a5b1e457b8735884419e114f90d53e1d5 | 13 | | SplitsCreatorFactory | 0x8da1c0864962c5e26c99cf839b0dc48e39104568 | 14 | | SplitsCreatorIMPL | 0xda9fbab4436e4124cd6ee6864d4b46d0dd412414 | 15 | | HatsTimeFrameModuleIMPL | 0xd4a66507ea8c8382fa8474ed6cae4163676a434a | 16 | | Hats | 0x3bc1A0Ad72417f2d411118085256fC53CBdDd137 | 17 | | HatsModuleFactory | 0x0a3f85fa597B6a967271286aA0724811acDF5CD9 | 18 | | PullSplitsFactory | 0x80f1B766817D04870f115fEBbcCADF8DBF75E017 | 19 | -------------------------------------------------------------------------------- /pkgs/document/docs/welcome.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Welcome 3 | sidebar_position: 1 4 | --- 5 | 6 | # Welcome 7 | 8 | Toban is a simplest way to record contribution and distribute rewards. 9 | 10 | ![](./../static/img/banner.png) 11 | 12 | Projects that involve a diverse people, such as open source development, collaborative works by multiple creators, and volunteer activities, and in which the people involved change one after another, can be very exciting, but they also have their own unique difficulties. 13 | 14 | # For example 15 | 16 | - **Annoying to Track Works**: 17 | 18 | - It's very tedious to report what you've accomplished for each task 19 | 20 | - Nobody are running a community to measure contributions by weighting each task. 21 | 22 | - We always forget anyway lol 23 | 24 | - **Rewards are required for Long term Contribution**: 25 | 26 | - There is no long-term contribution, just a volunteer spirit 27 | 28 | - There is no money to give out of the blue, and no one starts because of money. 29 | 30 | - Someone needs to do the housework and chores 31 | 32 | - **Ladder for Onboarding to the Community**: 33 | 34 | - Few people can participate on their own 35 | 36 | - It is difficult to understand the community enough to actually be able to do something 37 | 38 | - It's important to have a starting point that makes it easy to contribute something 39 | 40 | Therefore, we created Role Based Rewards Distribution system to track contributions and distribute rewards by role. 41 | 42 | # Core features are 43 | 44 | 1. Manage responsibilities and rights on rolls 45 | 46 | 2. Track little contributions with P2P token transfer 47 | 48 | 3. Determine the rewards rate based on roll and engaged period 49 | 50 | 4. Distribute rewards quickly to a large number of people 51 | 52 | These solutions were combined with ideas from **Hats Protocol**, **Splits**, and **Protocol Guild**. 53 | -------------------------------------------------------------------------------- /pkgs/document/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "document", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "docusaurus": "docusaurus", 7 | "start": "docusaurus start", 8 | "build": "docusaurus build", 9 | "swizzle": "docusaurus swizzle", 10 | "deploy": "docusaurus deploy", 11 | "clear": "docusaurus clear", 12 | "serve": "docusaurus serve", 13 | "write-translations": "docusaurus write-translations", 14 | "write-heading-ids": "docusaurus write-heading-ids", 15 | "typecheck": "tsc" 16 | }, 17 | "dependencies": { 18 | "@docusaurus/core": "3.6.1", 19 | "@docusaurus/preset-classic": "3.6.1", 20 | "@mdx-js/react": "^3.0.0", 21 | "clsx": "^2.0.0", 22 | "prism-react-renderer": "^2.3.0", 23 | "react": "^18.0.0", 24 | "react-dom": "^18.0.0", 25 | "rehype-katex": "7", 26 | "remark-math": "6" 27 | }, 28 | "devDependencies": { 29 | "@docusaurus/module-type-aliases": "3.6.1", 30 | "@docusaurus/tsconfig": "3.6.1", 31 | "@docusaurus/types": "3.6.1", 32 | "typescript": "~5.6.2" 33 | }, 34 | "browserslist": { 35 | "production": [">0.5%", "not dead", "not op_mini all"], 36 | "development": [ 37 | "last 3 chrome version", 38 | "last 3 firefox version", 39 | "last 5 safari version" 40 | ] 41 | }, 42 | "engines": { 43 | "node": ">=18.0" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /pkgs/document/sidebars.ts: -------------------------------------------------------------------------------- 1 | import type { SidebarsConfig } from "@docusaurus/plugin-content-docs"; 2 | 3 | const sidebars: SidebarsConfig = { 4 | // By default, Docusaurus generates a sidebar from the docs folder structure 5 | tutorialSidebar: [{ type: "autogenerated", dirName: "." }], 6 | 7 | // But you can create a sidebar manually 8 | /* 9 | tutorialSidebar: [ 10 | 'intro', 11 | 'hello', 12 | { 13 | type: 'category', 14 | label: 'Tutorial', 15 | items: ['tutorial-basics/create-a-document'], 16 | }, 17 | ], 18 | */ 19 | }; 20 | 21 | export default sidebars; 22 | -------------------------------------------------------------------------------- /pkgs/document/src/css/custom.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Any CSS included here will be global. The classic template 3 | * bundles Infima by default. Infima is a CSS framework designed to 4 | * work well for content-centric websites. 5 | */ 6 | 7 | /* You can override the default Infima variables here. */ 8 | :root { 9 | --ifm-color-primary: #2e8555; 10 | --ifm-color-primary-dark: #29784c; 11 | --ifm-color-primary-darker: #277148; 12 | --ifm-color-primary-darkest: #205d3b; 13 | --ifm-color-primary-light: #33925d; 14 | --ifm-color-primary-lighter: #359962; 15 | --ifm-color-primary-lightest: #3cad6e; 16 | --ifm-code-font-size: 95%; 17 | --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); 18 | } 19 | 20 | /* For readability concerns, you should choose a lighter palette in dark mode. */ 21 | [data-theme="dark"] { 22 | --ifm-color-primary: #25c2a0; 23 | --ifm-color-primary-dark: #21af90; 24 | --ifm-color-primary-darker: #1fa588; 25 | --ifm-color-primary-darkest: #1a8870; 26 | --ifm-color-primary-light: #29d5b0; 27 | --ifm-color-primary-lighter: #32d8b4; 28 | --ifm-color-primary-lightest: #4fddbf; 29 | --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3); 30 | } 31 | -------------------------------------------------------------------------------- /pkgs/document/src/pages/index.module.css: -------------------------------------------------------------------------------- 1 | /** 2 | * CSS files with the .module.css suffix will be treated as CSS modules 3 | * and scoped locally. 4 | */ 5 | 6 | .heroBanner { 7 | padding: 4rem 0; 8 | text-align: center; 9 | position: relative; 10 | overflow: hidden; 11 | } 12 | 13 | @media screen and (max-width: 996px) { 14 | .heroBanner { 15 | padding: 2rem; 16 | } 17 | } 18 | 19 | .buttons { 20 | display: flex; 21 | align-items: center; 22 | justify-content: center; 23 | } 24 | -------------------------------------------------------------------------------- /pkgs/document/src/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import useDocusaurusContext from "@docusaurus/useDocusaurusContext"; 2 | import Layout from "@theme/Layout"; 3 | 4 | /** 5 | * Top Page Component 6 | * @returns 7 | */ 8 | export default function Home(): JSX.Element { 9 | const { siteConfig } = useDocusaurusContext(); 10 | return ( 11 | 12 |
13 |
14 | Toban Banner 15 |
16 |
17 |
18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /pkgs/document/static/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackdays-io/toban/1ffd398b45703d2b9cf6117a5ca161489f3c3663/pkgs/document/static/.nojekyll -------------------------------------------------------------------------------- /pkgs/document/static/img/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackdays-io/toban/1ffd398b45703d2b9cf6117a5ca161489f3c3663/pkgs/document/static/img/banner.png -------------------------------------------------------------------------------- /pkgs/document/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackdays-io/toban/1ffd398b45703d2b9cf6117a5ca161489f3c3663/pkgs/document/static/img/favicon.ico -------------------------------------------------------------------------------- /pkgs/document/static/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackdays-io/toban/1ffd398b45703d2b9cf6117a5ca161489f3c3663/pkgs/document/static/img/logo.png -------------------------------------------------------------------------------- /pkgs/document/static/img/toban-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /pkgs/document/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // This file is not used in compilation. It is here just for a nice editor experience. 3 | "extends": "@docusaurus/tsconfig", 4 | "compilerOptions": { 5 | "baseUrl": "." 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /pkgs/frontend/.env.example: -------------------------------------------------------------------------------- 1 | VITE_CHAIN_ID=11155111 2 | 3 | VITE_PRIVY_APP_ID= 4 | 5 | VITE_BIGBANG_ADDRESS=0x08B4c53b98f46B14E2AD00189C2Aa3b9F3d0c8f3 6 | 7 | VITE_HATS_ADDRESS=0x3bc1A0Ad72417f2d411118085256fC53CBdDd137 8 | 9 | VITE_FRACTION_TOKEN_ADDRESS=0xd921517fdF141d97C289bDb9686f51A1375dCc69 10 | 11 | VITE_SPLITS_CREATOR_ADDRESS=0x6b5d2e27ff74e9adf4d23aebb9efb52867823583 12 | 13 | VITE_PIMLICO_API_KEY= 14 | 15 | VITE_PINATA_JWT= 16 | 17 | VITE_PINATA_GATEWAY= 18 | 19 | VITE_PINATA_GATEWAY_TOKEN="M-iEBglWoUCZWJYsihe1IRrngs7HIGeIr3s5lObVw96hv7GTuCw1QrlmnNtwvuXt" 20 | 21 | VITE_NAMESTONE_API_KEY= 22 | 23 | VITE_GOLDSKY_GRAPHQL_ENDPOINT= -------------------------------------------------------------------------------- /pkgs/frontend/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /** 2 | * This is intended to be a basic starting point for linting in your app. 3 | * It relies on recommended configs out of the box for simplicity, but you can 4 | * and should modify this configuration to best suit your team's needs. 5 | */ 6 | 7 | /** @type {import('eslint').Linter.Config} */ 8 | module.exports = { 9 | root: true, 10 | parserOptions: { 11 | ecmaVersion: "latest", 12 | sourceType: "module", 13 | ecmaFeatures: { 14 | jsx: true, 15 | }, 16 | }, 17 | env: { 18 | browser: true, 19 | commonjs: true, 20 | es6: true, 21 | }, 22 | ignorePatterns: ["!**/.server", "!**/.client"], 23 | 24 | // Base config 25 | extends: ["eslint:recommended"], 26 | 27 | overrides: [ 28 | // React 29 | { 30 | files: ["**/*.{js,jsx,ts,tsx}"], 31 | plugins: ["react", "jsx-a11y"], 32 | extends: [ 33 | "plugin:react/recommended", 34 | "plugin:react/jsx-runtime", 35 | "plugin:react-hooks/recommended", 36 | "plugin:jsx-a11y/recommended", 37 | ], 38 | settings: { 39 | react: { 40 | version: "detect", 41 | }, 42 | formComponents: ["Form"], 43 | linkComponents: [ 44 | { name: "Link", linkAttribute: "to" }, 45 | { name: "NavLink", linkAttribute: "to" }, 46 | ], 47 | "import/resolver": { 48 | typescript: { 49 | project: "pkgs/frontend/tsconfig.json", 50 | }, 51 | }, 52 | }, 53 | }, 54 | 55 | // Typescript 56 | { 57 | files: ["**/*.{ts,tsx}"], 58 | plugins: ["@typescript-eslint", "import"], 59 | parser: "@typescript-eslint/parser", 60 | settings: { 61 | "import/internal-regex": "^~/", 62 | "import/resolver": { 63 | node: { 64 | extensions: [".ts", ".tsx"], 65 | }, 66 | typescript: { 67 | alwaysTryTypes: true, 68 | }, 69 | }, 70 | }, 71 | extends: [ 72 | "plugin:@typescript-eslint/recommended", 73 | "plugin:import/recommended", 74 | "plugin:import/typescript", 75 | ], 76 | }, 77 | 78 | // Node 79 | { 80 | files: [".eslintrc.cjs"], 81 | env: { 82 | node: true, 83 | }, 84 | }, 85 | ], 86 | }; 87 | -------------------------------------------------------------------------------- /pkgs/frontend/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | 3 | /.cache 4 | /build 5 | .env 6 | 7 | .env.local 8 | .env.* 9 | !.env.example 10 | 11 | /downloads 12 | -------------------------------------------------------------------------------- /pkgs/frontend/README.md: -------------------------------------------------------------------------------- 1 | # Welcome to Remix! 2 | 3 | - 📖 [Remix docs](https://remix.run/docs) 4 | 5 | ## Development 6 | 7 | Run the dev server: 8 | 9 | ```shellscript 10 | npm run dev 11 | ``` 12 | 13 | ## Deployment 14 | 15 | First, build your app for production: 16 | 17 | ```sh 18 | npm run build 19 | ``` 20 | 21 | Then run the app in production mode: 22 | 23 | ```sh 24 | npm start 25 | ``` 26 | 27 | Now you'll need to pick a host to deploy it to. 28 | 29 | ### DIY 30 | 31 | If you're familiar with deploying Node applications, the built-in Remix app 32 | server is production-ready. 33 | 34 | Make sure to deploy the output of `npm run build` 35 | 36 | - `build/server` 37 | - `build/client` 38 | -------------------------------------------------------------------------------- /pkgs/frontend/app/components/BasicButton.tsx: -------------------------------------------------------------------------------- 1 | import CommonButton from "./common/CommonButton"; 2 | 3 | interface BasicButtonProps extends React.ComponentProps { 4 | children: React.ReactNode; 5 | } 6 | 7 | export const BasicButton = ({ children, ...props }: BasicButtonProps) => { 8 | return ( 9 | 16 | {children} 17 | 18 | ); 19 | }; 20 | -------------------------------------------------------------------------------- /pkgs/frontend/app/components/ContentContainer.tsx: -------------------------------------------------------------------------------- 1 | import { Box } from "@chakra-ui/react"; 2 | 3 | export const ContentContainer = ({ 4 | children, 5 | }: { 6 | children: React.ReactNode; 7 | }) => { 8 | return ( 9 | 15 | {children} 16 | 17 | ); 18 | }; 19 | -------------------------------------------------------------------------------- /pkgs/frontend/app/components/PageHeader.tsx: -------------------------------------------------------------------------------- 1 | import { Box, HStack, IconButton } from "@chakra-ui/react"; 2 | import { useNavigate } from "@remix-run/react"; 3 | import { type ReactNode, useCallback } from "react"; 4 | import { FaChevronLeft } from "react-icons/fa6"; 5 | 6 | interface Props { 7 | title: string | ReactNode; 8 | backLink?: string | (() => void); 9 | } 10 | 11 | export const PageHeader: React.FC = ({ title, backLink }) => { 12 | const navigate = useNavigate(); 13 | 14 | const handleBack = useCallback(() => { 15 | if (backLink && typeof backLink === "string") { 16 | navigate(backLink); 17 | } else if (backLink && typeof backLink === "function") { 18 | backLink(); 19 | } else { 20 | navigate(-1); 21 | } 22 | }, [backLink, navigate]); 23 | 24 | return ( 25 | 26 | 32 | 33 | 34 | {title} 35 | 36 | ); 37 | }; 38 | -------------------------------------------------------------------------------- /pkgs/frontend/app/components/RoleAttributesList.tsx: -------------------------------------------------------------------------------- 1 | import { Box, Text } from "@chakra-ui/react"; 2 | import type { FC } from "react"; 3 | import type { HatsDetailsAttributes } from "types/hats"; 4 | import { EditRoleAttributeDialog } from "~/components/roleAttributeDialog/EditRoleAttributeDialog"; 5 | 6 | export const RoleAttributesList: FC<{ 7 | items: HatsDetailsAttributes; 8 | setItem: (index: number, value: HatsDetailsAttributes[number]) => void; 9 | deleteItem: (index: number) => void; 10 | }> = ({ items, setItem, deleteItem }) => { 11 | return ( 12 | 13 | {items.map((item, index) => ( 14 | 30 | 31 | {items[index]?.label} 32 | 33 | 34 | 41 | 42 | 43 | ))} 44 | 45 | ); 46 | }; 47 | -------------------------------------------------------------------------------- /pkgs/frontend/app/components/SettingSections.tsx: -------------------------------------------------------------------------------- 1 | import { Box, Text } from "@chakra-ui/react"; 2 | import type { FC } from "react"; 3 | 4 | export const SettingsSection: FC<{ 5 | children: React.ReactNode; 6 | headingText: string; 7 | }> = ({ children, headingText }) => ( 8 | 9 | 10 | {headingText} 11 | 12 | {children} 13 | 14 | ); 15 | 16 | export const SettingsSubSection: FC<{ 17 | children: React.ReactNode; 18 | headingText: string; 19 | }> = ({ children, headingText }) => ( 20 | 21 | 22 | {headingText} 23 | 24 | {children} 25 | 26 | ); 27 | -------------------------------------------------------------------------------- /pkgs/frontend/app/components/SwitchNetwork.tsx: -------------------------------------------------------------------------------- 1 | import { currentChain } from "hooks/useViem"; 2 | import { useActiveWallet } from "hooks/useWallet"; 3 | import { type FC, useEffect } from "react"; 4 | 5 | export const SwitchNetwork: FC = () => { 6 | const { connectedWallet } = useActiveWallet(); 7 | 8 | useEffect(() => { 9 | const switchChain = async () => { 10 | if ( 11 | connectedWallet && 12 | Number(connectedWallet.chainId) !== currentChain.id 13 | ) { 14 | await connectedWallet.switchChain(currentChain.id); 15 | } 16 | }; 17 | 18 | switchChain(); 19 | }, [connectedWallet]); 20 | 21 | return <>; 22 | }; 23 | -------------------------------------------------------------------------------- /pkgs/frontend/app/components/assistcredit/UserList.tsx: -------------------------------------------------------------------------------- 1 | import { HStack, List, Text } from "@chakra-ui/react"; 2 | import type { NameData } from "namestone-sdk"; 3 | import { ipfs2https } from "utils/ipfs"; 4 | import { CommonInput } from "~/components/common/CommonInput"; 5 | import { UserIcon } from "~/components/icon/UserIcon"; 6 | import { Field } from "~/components/ui/field"; 7 | 8 | interface UserListProps { 9 | searchText: string; 10 | setSearchText: (text: string) => void; 11 | users?: NameData[][]; 12 | onSelectUser: (user: NameData) => void; 13 | } 14 | 15 | const UserList = ({ 16 | searchText, 17 | setSearchText, 18 | users, 19 | onSelectUser, 20 | }: UserListProps) => { 21 | return ( 22 | <> 23 | 24 | { 27 | setSearchText(e.target.value); 28 | }} 29 | placeholder="ユーザー名 or ウォレットアドレス" 30 | /> 31 | 32 | 33 | 34 | {users?.flat().map((user) => ( 35 | onSelectUser(user)}> 36 | 37 | 41 | 42 | {user.name 43 | ? `${user.name} (${user.address.slice(0, 6)}...${user.address.slice(-4)})` 44 | : user.address} 45 | 46 | 47 | 48 | ))} 49 | 50 | 51 | ); 52 | }; 53 | 54 | export default UserList; 55 | -------------------------------------------------------------------------------- /pkgs/frontend/app/components/assistcredit/VerticalBar.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | BarElement, 3 | CategoryScale, 4 | Chart as ChartJS, 5 | Legend, 6 | LinearScale, 7 | Title, 8 | Tooltip, 9 | } from "chart.js"; 10 | import { useMemo } from "react"; 11 | import { Bar } from "react-chartjs-2"; 12 | import { 13 | OrderDirection, 14 | TransferFractionToken_OrderBy, 15 | } from "../../../gql/graphql"; 16 | import { useGetTransferFractionTokens } from "../../../hooks/useFractionToken"; 17 | 18 | ChartJS.register( 19 | CategoryScale, 20 | LinearScale, 21 | BarElement, 22 | Title, 23 | Tooltip, 24 | Legend, 25 | ); 26 | 27 | export const VerticalBar = ({ treeId }: { treeId: string }) => { 28 | const { data: gqlData } = useGetTransferFractionTokens({ 29 | where: { 30 | workspaceId: treeId, 31 | }, 32 | orderBy: TransferFractionToken_OrderBy.BlockTimestamp, 33 | orderDirection: OrderDirection.Asc, 34 | first: 100, 35 | }); 36 | 37 | const { labels, amounts } = useMemo(() => { 38 | if (!gqlData?.transferFractionTokens) { 39 | return { labels: [], amounts: [] }; 40 | } 41 | 42 | const dailyAmounts = gqlData.transferFractionTokens.reduce( 43 | (acc: { [key: string]: number }, tx) => { 44 | const date = new Date( 45 | Number(tx.blockTimestamp) * 1000, 46 | ).toLocaleDateString("ja-JP"); 47 | 48 | acc[date] = (acc[date] || 0) + Number(tx.amount); 49 | return acc; 50 | }, 51 | {}, 52 | ); 53 | 54 | return { 55 | labels: Object.keys(dailyAmounts), 56 | amounts: Object.values(dailyAmounts), 57 | }; 58 | }, [gqlData]); 59 | 60 | const options = { 61 | responsive: true, 62 | plugins: { 63 | legend: { 64 | display: false, 65 | }, 66 | title: { 67 | display: true, 68 | text: `${["144", "175"].includes(treeId || "") ? "ケアポイント" : "アシストクレジット"}の日次流通量`, 69 | }, 70 | }, 71 | }; 72 | 73 | const data = { 74 | labels, 75 | datasets: [ 76 | { 77 | label: "流通量", 78 | data: amounts, 79 | backgroundColor: "rgba(255, 99, 132, 0.5)", 80 | }, 81 | ], 82 | }; 83 | 84 | return ; 85 | }; 86 | -------------------------------------------------------------------------------- /pkgs/frontend/app/components/assistcredit/emojiConstants.ts: -------------------------------------------------------------------------------- 1 | export const treatEmojis: { [key: number]: string } = { 2 | 0: "🍪", // クッキー 3 | 200: "🍩", // ドーナツ 4 | 400: "🍫", // チョコレート 5 | 600: "🍰", // ケーキ 6 | 800: "🍦", // アイスクリーム 7 | 1000: "🍭", // キャンディ 8 | 1200: "🥐", // クロワッサン 9 | 1400: "🥨", // プレッツェル 10 | 1600: "🍺", // ビール 11 | 1800: "🍷", // ワイン 12 | 2000: "🎁", // プレゼント 13 | }; 14 | -------------------------------------------------------------------------------- /pkgs/frontend/app/components/common/CommonButton.tsx: -------------------------------------------------------------------------------- 1 | import { Button, type ButtonProps } from "~/components/ui/button"; 2 | 3 | interface CommonButtonProps extends Omit { 4 | children: React.ReactNode; 5 | width?: "full" | number; 6 | backgroundColor?: string; 7 | color?: string; 8 | } 9 | 10 | export const CommonButton = ({ 11 | children, 12 | width = "full", 13 | size = "md", 14 | backgroundColor = "yellow.400", 15 | color = "gray.800", 16 | ...props 17 | }: CommonButtonProps) => { 18 | return ( 19 | 29 | ); 30 | }; 31 | 32 | export default CommonButton; 33 | -------------------------------------------------------------------------------- /pkgs/frontend/app/components/common/CommonDialog.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | DialogContent, 3 | DialogRoot, 4 | DialogTrigger, 5 | } from "~/components/ui/dialog"; 6 | 7 | interface CommonDialogProps { 8 | dialogTriggerReactNode?: React.ReactNode; 9 | children?: React.ReactNode; 10 | } 11 | 12 | export const CommonDialog = ({ 13 | dialogTriggerReactNode, 14 | children, 15 | }: CommonDialogProps) => { 16 | return ( 17 | 18 | {dialogTriggerReactNode} 19 | {children} 20 | 21 | ); 22 | }; 23 | -------------------------------------------------------------------------------- /pkgs/frontend/app/components/common/CommonIcon.tsx: -------------------------------------------------------------------------------- 1 | import { Box, Image } from "@chakra-ui/react"; 2 | import { type ReactNode, useEffect, useState } from "react"; 3 | 4 | interface CommonIconProps { 5 | imageUrl: string | undefined; 6 | size: number | `${number}px` | "full"; 7 | borderRadius?: string; 8 | fallbackIconComponent?: ReactNode; 9 | } 10 | 11 | export const CommonIcon = ({ 12 | size, 13 | imageUrl, 14 | fallbackIconComponent, 15 | borderRadius, 16 | }: CommonIconProps) => { 17 | const [showFallbackIcon, setShowFallbackIcon] = useState(!imageUrl); 18 | 19 | useEffect(() => { 20 | setShowFallbackIcon(!imageUrl); 21 | }, [imageUrl]); 22 | 23 | return ( 24 | 37 | {!showFallbackIcon ? ( 38 | setShowFallbackIcon(true)} 45 | /> 46 | ) : ( 47 | fallbackIconComponent || null 48 | )} 49 | 50 | ); 51 | }; 52 | -------------------------------------------------------------------------------- /pkgs/frontend/app/components/common/CommonInput.tsx: -------------------------------------------------------------------------------- 1 | import { Input, type InputProps } from "@chakra-ui/react"; 2 | import type { FC } from "react"; 3 | 4 | interface CommonInputProps extends Omit { 5 | minHeight?: InputProps["minHeight"]; 6 | value: string | number; 7 | onChange?: (event: React.ChangeEvent) => void; 8 | } 9 | 10 | export const CommonInput: FC = ({ 11 | minHeight, 12 | value, 13 | placeholder, 14 | onChange, 15 | ...props 16 | }: CommonInputProps) => { 17 | return ( 18 | 29 | ); 30 | }; 31 | -------------------------------------------------------------------------------- /pkgs/frontend/app/components/common/CommonTextarea.tsx: -------------------------------------------------------------------------------- 1 | import { Textarea, type TextareaProps } from "@chakra-ui/react"; 2 | 3 | interface CommonTextAreaProps extends Omit { 4 | minHeight?: TextareaProps["minHeight"]; 5 | value: string; 6 | onChange: (event: React.ChangeEvent) => void; 7 | } 8 | 9 | export const CommonTextArea = ({ 10 | minHeight, 11 | value, 12 | placeholder, 13 | onChange, 14 | ...props 15 | }: CommonTextAreaProps) => { 16 | return ( 17 |