├── .babelrc ├── .env ├── .eslintrc ├── .gitattributes ├── .github ├── dependabot.yml ├── perf │ └── layout.svg └── workflows │ ├── build.yml │ └── nextjs_bundle_analysis.yml ├── .gitignore ├── .prettierignore ├── .prettierrc.json ├── .vercelignore ├── README.md ├── __mocks__ ├── fileMock.js └── styleMock.js ├── __tests__ └── depositStake.js ├── abis ├── fold_abi.js └── xfold_abi.js ├── components ├── XFOLD.png ├── addTokensToMetamask.tsx ├── button.tsx ├── connectAccount.tsx ├── customToast.tsx ├── farming │ ├── poolSelect.tsx │ └── withdrawPool.tsx ├── identicon.tsx ├── mint │ └── deposit.tsx ├── mobileMenu.tsx ├── navigation.tsx ├── network.tsx ├── numericalInput.tsx ├── overflowMenu.tsx ├── panel.tsx ├── stake │ ├── depositStake.tsx │ ├── lockStake.tsx │ └── withdrawStake.tsx ├── tabs.ts ├── tokenSelect.tsx ├── walletModal.tsx ├── web3.tsx └── xfold.svg ├── connectors.ts ├── constants ├── chains.ts ├── contracts.ts ├── farming.ts ├── numbers.ts └── tokens.ts ├── contracts ├── DictatorDAO.json ├── ERC20.json ├── FOLD.json ├── Staking.json ├── abi.json └── types │ ├── DOMODAO.ts │ ├── DictatorDAO.d.ts │ ├── DictatorDAO.ts │ ├── DictatorDAOFactory.ts │ ├── ERC20.d.ts │ ├── ERC20.ts │ ├── FOLD.ts │ ├── Staking.d.ts │ ├── common.ts │ ├── commons.ts │ ├── factories │ ├── DOMODAO__factory.ts │ ├── ERC20__factory.ts │ └── FOLD__factory.ts │ └── index.ts ├── data └── qa.json ├── hooks ├── getSigner.ts ├── isAddress.ts ├── useActiveWeb3React.ts ├── useAddTokenToMetaMask.ts ├── useBestBuy.ts ├── useBlockNumber.ts ├── useContract.ts ├── useENSName.ts ├── useEagerConnect.ts ├── useFathom.ts ├── useFoldAmount.ts ├── useFormattedBigNumber.ts ├── useGetFOLDAmountOut.ts ├── useInput.ts ├── useKeepSWRDataLiveAsBlocksArrive.ts ├── useMetaMaskOnboarding.ts ├── useTimer.ts ├── useTotalSupply.ts ├── useWalletModal.ts ├── useWeb3Store.ts └── view │ ├── GetFoldAmountOut.ts │ ├── useEpochDates.ts │ ├── useGetPoolTokens.ts │ ├── useGetTokenAmountOut.ts │ ├── useGetxFOLDAmountOut.ts │ ├── useIsOperatorInitialized.ts │ ├── useStakingBalanceLocked.ts │ ├── useTokenAllowance.ts │ ├── useTokenBalance.ts │ ├── useUserLockedUntil.ts │ └── usexFOLDStaked.ts ├── jest.config.js ├── jest.setup.js ├── lib ├── coingecko.ts └── connectors │ ├── metamask.ts │ └── walletconnect.ts ├── next-env.d.ts ├── next.config.js ├── package.json ├── pages ├── 404.tsx ├── _app.tsx ├── _document.tsx ├── faqs.tsx ├── index.tsx ├── mint.tsx ├── stake.tsx └── vote.tsx ├── pnpm-lock.yaml ├── postcss.config.js ├── public ├── android-chrome-192x192.png ├── android-chrome-384x384.png ├── apple-touch-icon.png ├── browserconfig.xml ├── favicon-16x16.png ├── favicon-32x32.png ├── favicon.ico ├── logo.png ├── mstile-310x310.png ├── safari-pinned-tab.svg ├── site.webmanifest └── tokens │ ├── ETH.png │ ├── ETH@32.png │ ├── FOLD.png │ ├── FOLD@32.png │ ├── MANIFOLD.png │ ├── SUSHI.png │ ├── SUSHI@32.png │ ├── USDC.png │ ├── USDC@32.png │ ├── USDM.png │ ├── XFOLD.png │ ├── XFOLD@32.png │ ├── logo.png │ └── openmev.png ├── scripts ├── atomic.cjs ├── git-hash.sh └── version.sh ├── styles └── globals.css ├── svgs ├── MetaMaskOutline.tsx └── WalletConnect.tsx ├── tailwind.config.js ├── tsconfig.json ├── utils ├── bn.ts ├── calculateLockupMultiplier.ts ├── escapeRegExp.ts ├── formatNumber.ts ├── getExplorerLink.ts ├── getFutureTimestamp.ts ├── handleError.ts ├── hexlify.ts ├── isEqual.ts ├── isMobile.ts ├── normalizeChainId.ts └── shortenAddress.ts ├── vercel.json ├── views ├── mint.tsx └── stake.tsx └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "next/babel", 5 | { 6 | "preset-env": {}, 7 | "transform-runtime": {}, 8 | "styled-jsx": {}, 9 | "class-properties": {} 10 | } 11 | ] 12 | ], 13 | "plugins": [] 14 | } 15 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | INFURA_ID=3ff0c675dc614116aa126b14f6368971 2 | #INFURA_ID=3ff0c675dc614116aa126b14f6368971 3 | SKIP_PREFLIGHT_CHECK=true 4 | REACT_APP_VERSION= 5 | COMMIT_SHA=6c2dcc6 6 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["next", "next/core-web-vitals", "prettier"], 3 | "rules": { 4 | "@next/next/no-img-element": "off" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # gitattirbutes 2 | # linguist directive to reduce diff churn on PR 3 | docs/ linguist-generated=true -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: 'npm' 4 | directory: '/' 5 | schedule: 6 | interval: 'daily' 7 | allow: 8 | # only production dependencies get updates 9 | - dependency-type: 'production' 10 | commit-message: 11 | # Prefix all commit messages with "npm" 12 | prefix: 'npm' 13 | include: 'scope' 14 | labels: 15 | - 'dependency' 16 | # Add default Kodiak `merge.automerge_label` 17 | - 'automerge' 18 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: nodejs 2 | 3 | on: 4 | push: 5 | paths: 6 | - '**/**' 7 | - '!**/*.md/**' 8 | env: 9 | CI: '' 10 | COMMIT_SHA: ${{ github.event.pull_request.head.sha }} 11 | PULL_NUMBER: ${{ github.event.pull_request.number }} 12 | RUN_ID: ${{ github.run_id }} 13 | FORCE_COLOR: 2 14 | NEXT_TELEMETRY_DISABLED: 1 15 | 16 | jobs: 17 | pipeline: 18 | name: Node ${{ matrix.node-version }} on ${{ matrix.os }} 19 | runs-on: ${{ matrix.os }} 20 | 21 | strategy: 22 | fail-fast: false 23 | matrix: 24 | node-version: ['20.x'] 25 | os: ['ubuntu-latest'] 26 | 27 | steps: 28 | - uses: actions/checkout@v3 29 | - name: Use Node.js ${{ matrix.node-version }} 30 | uses: actions/setup-node@v4.1.0 31 | with: 32 | node-version: ${{ matrix.node-version }} 33 | 34 | - name: Get yarn cache directory path 35 | id: yarn-cache-dir-path 36 | run: echo "::set-output name=dir::$(yarn cache dir)" 37 | 38 | - uses: actions/cache@v4 39 | id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) 40 | with: 41 | path: ${{ steps.yarn-cache-dir-path.outputs.dir }} 42 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 43 | restore-keys: | 44 | ${{ runner.os }}-yarn- 45 | - name: Install project dependencies 46 | run: yarn --prefer-offline 47 | id: install 48 | 49 | - name: Install project dependencies 50 | run: yarn run build 51 | id: production 52 | -------------------------------------------------------------------------------- /.github/workflows/nextjs_bundle_analysis.yml: -------------------------------------------------------------------------------- 1 | name: 'Next.js Bundle Analysis' 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - trunk 8 | workflow_dispatch: 9 | 10 | defaults: 11 | run: 12 | # change this if your nextjs app does not live at the root of the repo 13 | working-directory: ./ 14 | 15 | jobs: 16 | analyze: 17 | name: Analyze running on Node ${{ matrix.node-version }} on ${{ matrix.os }} 18 | runs-on: ${{ matrix.os }} 19 | 20 | strategy: 21 | fail-fast: false 22 | matrix: 23 | node-version: ['16.x'] 24 | os: ['ubuntu-latest'] 25 | 26 | steps: 27 | - uses: actions/checkout@v3 28 | - name: Use Node.js ${{ matrix.node-version }} 29 | uses: actions/setup-node@v3 30 | with: 31 | node-version: ${{ matrix.node-version }} 32 | 33 | - name: Install dependencies 34 | uses: bahmutov/npm-install@v1 35 | with: 36 | install-command: yarn --prefer-offline 37 | 38 | - name: Restore next build 39 | uses: actions/cache@v2 40 | id: restore-build-cache 41 | env: 42 | cache-name: cache-next-build 43 | with: 44 | # if you use a custom build directory, replace all instances of `.next` in this file with your build directory 45 | # ex: if your app builds to `dist`, replace `.next` with `dist` 46 | path: .next/cache 47 | # change this if you prefer a more strict cache 48 | key: ${{ runner.os }}-build-${{ env.cache-name }} 49 | 50 | - name: Build next.js app 51 | # change this if your site requires a custom build command 52 | run: ./node_modules/.bin/next build 53 | 54 | # Here's the first place where next-bundle-analysis' own script is used 55 | # This step pulls the raw bundle stats for the current bundle 56 | - name: Analyze bundle 57 | run: npx -p nextjs-bundle-analysis report 58 | 59 | - name: Upload bundle 60 | uses: actions/upload-artifact@v2 61 | with: 62 | name: bundle 63 | path: .next/analyze/__bundle_analysis.json 64 | 65 | - name: Download base branch bundle stats 66 | uses: dawidd6/action-download-artifact@v2 67 | if: success() && github.event.number 68 | with: 69 | workflow: nextjs_bundle_analysis.yml 70 | branch: ${{ github.event.pull_request.base.ref }} 71 | path: .next/analyze/base 72 | 73 | # And here's the second place - this runs after we have both the current and 74 | # base branch bundle stats, and will compare them to determine what changed. 75 | # There are two configurable arguments that come from package.json: 76 | # 77 | # - budget: optional, set a budget (bytes) against which size changes are measured 78 | # it's set to 350kb here by default, as informed by the following piece: 79 | # https://infrequently.org/2021/03/the-performance-inequality-gap/ 80 | # 81 | # - red-status-percentage: sets the percent size increase where you get a red 82 | # status indicator, defaults to 20% 83 | # 84 | # Either of these arguments can be changed or removed by editing the `nextBundleAnalysis` 85 | # entry in your package.json file. 86 | - name: Compare with base branch bundle 87 | if: success() && github.event.number 88 | run: ls -laR .next/analyze/base && npx -p nextjs-bundle-analysis compare 89 | 90 | - name: Get comment body 91 | id: get-comment-body 92 | if: success() && github.event.number 93 | run: | 94 | body=$(cat .next/analyze/__bundle_analysis_comment.txt) 95 | body="${body//'%'/'%25'}" 96 | body="${body//$'\n'/'%0A'}" 97 | body="${body//$'\r'/'%0D'}" 98 | echo ::set-output name=body::$body 99 | 100 | - name: Find Comment 101 | uses: peter-evans/find-comment@v2 102 | if: success() && github.event.number 103 | id: fc 104 | with: 105 | issue-number: ${{ github.event.number }} 106 | comment-author: 'github-actions[bot]' 107 | body-includes: '' 108 | 109 | - name: Create Comment 110 | uses: peter-evans/create-or-update-comment@v2 111 | if: success() && github.event.number && steps.fc.outputs.comment-id == 0 112 | with: 113 | issue-number: ${{ github.event.number }} 114 | body: ${{ steps.get-comment-body.outputs.body }} 115 | 116 | - name: Update Comment 117 | uses: peter-evans/create-or-update-comment@v2 118 | if: success() && github.event.number && steps.fc.outputs.comment-id != 0 119 | with: 120 | issue-number: ${{ github.event.number }} 121 | body: ${{ steps.get-comment-body.outputs.body }} 122 | comment-id: ${{ steps.fc.outputs.comment-id }} 123 | edit-mode: replace 124 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .DS_Store 3 | node_modules 4 | /.pnp 5 | .pnp.js 6 | tsconfig.tsbuildinfo 7 | # testing 8 | /coverage 9 | 10 | # next.js 11 | /.next/ 12 | /out/ 13 | 14 | # production 15 | /build 16 | 17 | *.pem 18 | 19 | # debug 20 | npm-debug.log* 21 | yarn-debug.log* 22 | yarn-error.log* 23 | 24 | # local env files 25 | .env.local 26 | .env.development.local 27 | .env.test.local 28 | .env.production.local 29 | 30 | # vercel 31 | .vercel 32 | 33 | # eslint 34 | .eslintcache 35 | 36 | !/contracts/types 37 | 38 | # Local Netlify folder 39 | .netlify 40 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .next/ 2 | .cache 3 | public/ 4 | build/ 5 | out/ 6 | dist/ 7 | *.min.js 8 | *.svg 9 | *.png 10 | *.css 11 | *.scss 12 | *.html 13 | *.htm 14 | *.xml 15 | *.xhtml 16 | *.uml 17 | *.json 18 | *.js 19 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/prettierrc", 3 | "arrowParens": "always", 4 | "bracketSpacing": true, 5 | "jsxBracketSameLine": false, 6 | "jsxSingleQuote": false, 7 | "printWidth": 80, 8 | "proseWrap": "always", 9 | "quoteProps": "as-needed", 10 | "semi": true, 11 | "singleQuote": true, 12 | "tabWidth": 2, 13 | "trailingComma": "all", 14 | "useTabs": false 15 | } 16 | -------------------------------------------------------------------------------- /.vercelignore: -------------------------------------------------------------------------------- 1 | .github/ 2 | *.md 3 | __tests__ 4 | __mocks__ 5 | scripts/ 6 | docs/ 7 | doc/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [Manifold Finance Staking DApp](https://staking.manifoldfinance.com) 2 | 3 | --- 4 | 5 | [![nodejs](https://github.com/manifoldfinance/staking/actions/workflows/build.yml/badge.svg)](https://github.com/manifoldfinance/staking/actions/workflows/build.yml) 6 | 7 | ## Overview 8 | 9 | ## License 10 | 11 | MIT 12 | -------------------------------------------------------------------------------- /__mocks__/fileMock.js: -------------------------------------------------------------------------------- 1 | // __mocks__/fileMock.js 2 | module.exports = 'test-file-stub' 3 | -------------------------------------------------------------------------------- /__mocks__/styleMock.js: -------------------------------------------------------------------------------- 1 | // __mocks__/styleMock.js 2 | module.exports = {} 3 | -------------------------------------------------------------------------------- /__tests__/depositStake.js: -------------------------------------------------------------------------------- 1 | // import {render, screen} from '@testing-library/react'; 2 | // import DepositStake from "@/components/stake/depositStake" 3 | // import foldAbi from "@/contracts/FOLD.json" 4 | // import Web3 from 'web3' 5 | const foldAbi = require('../abis/fold_abi') 6 | const xFoldAbi = require('../abis/xfold_abi') 7 | const Web3 = require('web3') 8 | 9 | const unlockedAddress = "0x231e6f628aaa2b01b48e0f58ca326068d2047108" 10 | const foldAddress = "0xd084944d3c05cd115c09d072b9f44ba3e0e45921" 11 | const xFoldAddress = "0x454BD9E2B29EB5963048cC1A8BD6fD44e89899Cb" 12 | const web3 = new Web3("http://localhost:8545") 13 | const fold = new web3.eth.Contract(foldAbi, foldAddress) 14 | const xfold = new web3.eth.Contract(xFoldAbi, xFoldAddress) 15 | 16 | const foldBalance = fold.methods.balanceOf(unlockedAddress).call().then(data => data).catch(e => e) 17 | const xFoldBalance = xfold.methods.balanceOf(unlockedAddress).call().then(data => data).catch(e => e) 18 | console.log(foldBalance) 19 | console.log(xFoldBalance) 20 | 21 | // describe("DepositStake", () => { 22 | // it("checks if there is a submit button", () => { 23 | // const {getByText} = render(); 24 | // expect(getByText('Permit FOLD to mint xFOLD')).toBeInTheDocument(); 25 | // }) 26 | // }) 27 | 28 | -------------------------------------------------------------------------------- /abis/fold_abi.js: -------------------------------------------------------------------------------- 1 | module.exports = [{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"},{"internalType":"address","name":"_owner","type":"address"},{"internalType":"uint256","name":"_initialSupply","type":"uint256"}],"name":"getInitData","outputs":[{"internalType":"bytes","name":"_data","type":"bytes"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"init","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"initToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"},{"internalType":"address","name":"_owner","type":"address"},{"internalType":"uint256","name":"_initialSupply","type":"uint256"}],"name":"initToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner_","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokenTemplate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}] -------------------------------------------------------------------------------- /abis/xfold_abi.js: -------------------------------------------------------------------------------- 1 | module.exports = [{"inputs":[{"internalType":"string","name":"sharesSymbol","type":"string"},{"internalType":"string","name":"sharesName","type":"string"},{"internalType":"contract IERC20","name":"token_","type":"address"},{"internalType":"address","name":"initialOperator","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"txHash","type":"bytes32"},{"indexed":true,"internalType":"address","name":"target","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"}],"name":"CancelTransaction","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"txHash","type":"bytes32"},{"indexed":true,"internalType":"address","name":"target","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"}],"name":"ExecuteTransaction","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"txHash","type":"bytes32"},{"indexed":true,"internalType":"address","name":"target","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"},{"indexed":false,"internalType":"uint256","name":"eta","type":"uint256"}],"name":"QueueTransaction","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"DELAY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"GRACE_PERIOD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"burn","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"burnFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"cancelTransaction","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"executeTransaction","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"operatorVote","type":"address"}],"name":"mint","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"operator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOperator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOperatorTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner_","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"queueTransaction","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"queuedTransactions","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOperator","type":"address"}],"name":"setOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"userVote","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"users","outputs":[{"internalType":"uint128","name":"balance","type":"uint128"},{"internalType":"uint128","name":"lockedUntil","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operatorVote","type":"address"}],"name":"vote","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"votes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}] -------------------------------------------------------------------------------- /components/XFOLD.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manifoldfinance/staking/b6f68dbcad9d4fa8267bffcbfc9dd05c6a67f090/components/XFOLD.png -------------------------------------------------------------------------------- /components/addTokensToMetamask.tsx: -------------------------------------------------------------------------------- 1 | import MetaMaskOutline from '@/svgs/MetaMaskOutline'; 2 | import useAddTokenToMetaMask from '@/hooks/useAddTokenToMetaMask'; 3 | import useWeb3Store from '@/hooks/useWeb3Store'; 4 | 5 | export default function AddTokensToMetaMask() { 6 | const library = useWeb3Store((state) => state.library); 7 | 8 | const { addToken } = useAddTokenToMetaMask(); 9 | 10 | if (library && library.provider.isMetaMask) { 11 | return ( 12 | 45 | ); 46 | } 47 | 48 | return null; 49 | } 50 | -------------------------------------------------------------------------------- /components/button.tsx: -------------------------------------------------------------------------------- 1 | import { TokenNames, TOKEN_BUY_LINKS } from '@/constants/tokens'; 2 | import classNames from 'classnames'; 3 | 4 | type ButtonProps = { 5 | small?: boolean; 6 | } & React.DetailedHTMLProps< 7 | React.ButtonHTMLAttributes, 8 | HTMLButtonElement 9 | >; 10 | 11 | export default function Button({ 12 | type = 'button', 13 | small = false, 14 | className = '', 15 | disabled, 16 | ...rest 17 | }: ButtonProps) { 18 | const cachedClassNames = classNames( 19 | className, 20 | small ? 'px-4 py-2' : 'p-4 text-lg leading-5', 21 | disabled ? 'bg-primary-300' : 'bg-white text-primary', 22 | 'w-full', 23 | 'rounded-md font-medium', 24 | 'focus:outline-none focus:ring-4', 25 | ); 26 | 27 | return ( 28 | 56 | ); 57 | } 58 | 59 | type BuyLinkProps = { 60 | tokenSymbol: keyof typeof TokenNames; 61 | } & React.DetailedHTMLProps< 62 | React.AnchorHTMLAttributes, 63 | HTMLAnchorElement 64 | >; 65 | 66 | export function BuyLink({ 67 | tokenSymbol, 68 | type = 'button', 69 | className = '', 70 | ...rest 71 | }: BuyLinkProps) { 72 | const cachedClassNames = classNames( 73 | className, 74 | 'text-indigo-500 focus:outline-none focus:underline hover:underline', 75 | ); 76 | 77 | const href = TOKEN_BUY_LINKS[tokenSymbol as keyof typeof TokenNames]; 78 | 79 | return ( 80 | 87 | {`(Buy)`} 88 | 89 | ); 90 | } 91 | -------------------------------------------------------------------------------- /components/connectAccount.tsx: -------------------------------------------------------------------------------- 1 | import Button from './button'; 2 | import useWalletModal from '@/hooks/useWalletModal'; 3 | process.env.NEXT_PUBLIC_COMMIT; 4 | 5 | export default function ConnectAccount() { 6 | const openWalletModal = useWalletModal((state) => state.open); 7 | 8 | return ( 9 |
10 |
11 |
12 |
13 |

14 | Manifold Finance v1 15 |

16 | 17 |
18 |
19 |
20 |

21 | Use your FOLD to mint xFOLD and get staking rewards 22 |

23 |
24 |
25 | 26 |

🌀 💊 🔵 🍣

27 |

28 |
29 | 30 | 31 |
32 |
33 |
34 |
35 | ); 36 | } 37 | -------------------------------------------------------------------------------- /components/customToast.tsx: -------------------------------------------------------------------------------- 1 | import getExplorerLink, { ExplorerDataType } from '@/utils/getExplorerLink'; 2 | import { ExternalLink } from 'react-feather'; 3 | 4 | export function TransactionToast({ 5 | message, 6 | hash, 7 | chainId, 8 | }: { 9 | chainId?: number; 10 | message: string; 11 | hash?: string; 12 | }) { 13 | if (typeof chainId === 'undefined' || typeof hash === 'undefined') { 14 | return {message}; 15 | } 16 | 17 | return ( 18 |
19 | {message}{' '} 20 | 26 | 27 | 28 |
29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /components/farming/poolSelect.tsx: -------------------------------------------------------------------------------- 1 | import { FarmingPool, LP_FARMING_POOLS } from '@/constants/farming'; 2 | import useWeb3Store from '@/hooks/useWeb3Store'; 3 | import { Listbox } from '@headlessui/react'; 4 | import classNames from 'classnames'; 5 | import type { Dispatch, SetStateAction } from 'react'; 6 | import { ChevronDown } from 'react-feather'; 7 | 8 | type PoolSelectProps = { 9 | value: FarmingPool; 10 | onChange: Dispatch>; 11 | }; 12 | 13 | export default function PoolSelect({ value, onChange }: PoolSelectProps) { 14 | const chainId = useWeb3Store((state) => state.chainId); 15 | 16 | return ( 17 | 18 |
19 | 25 | 26 | {value ? value.name : 'Select a pool'} 27 | 28 | 29 | 30 | 32 | 33 | 34 | 35 | {(LP_FARMING_POOLS[chainId] as FarmingPool[]).map( 36 | (farmingPool, farmingPoolIndex) => ( 37 | 40 | classNames( 41 | 'cursor-default select-none relative p-2 rounded text-white', 42 | active ? 'bg-white/[0.10]' : '', 43 | ) 44 | } 45 | value={farmingPool} 46 | disabled={farmingPool === value} 47 | > 48 | {({ selected }) => ( 49 |
55 |
56 | 62 | {farmingPool.name} 63 | 64 |
65 |
66 | )} 67 |
68 | ), 69 | )} 70 |
71 |
72 |
73 | ); 74 | } 75 | -------------------------------------------------------------------------------- /components/farming/withdrawPool.tsx: -------------------------------------------------------------------------------- 1 | import Button, { MaxButton } from '@/components/button'; 2 | import NumericalInput from '@/components/numericalInput'; 3 | import { TokenPair } from '@/components/tokenSelect'; 4 | import { FarmingPool, FARMING_LP_SYMBOL } from '@/constants/farming'; 5 | import { MIN_INPUT_VALUE } from '@/constants/numbers'; 6 | import { useStaking } from '@/hooks/useContract'; 7 | import useFormattedBigNumber from '@/hooks/useFormattedBigNumber'; 8 | import useInput from '@/hooks/useInput'; 9 | import useWeb3Store from '@/hooks/useWeb3Store'; 10 | import useStakingBalanceLocked from '@/hooks/view/useStakingBalanceLocked'; 11 | import useTokenBalance from '@/hooks/view/useTokenBalance'; 12 | import handleError from '@/utils/handleError'; 13 | import { formatUnits, parseUnits } from '@ethersproject/units'; 14 | import type { FormEvent } from 'react'; 15 | import { ExternalLink } from 'react-feather'; 16 | import toast from 'react-hot-toast'; 17 | import { TransactionToast } from '../customToast'; 18 | 19 | export default function WithdrawPool({ pool }: { pool: FarmingPool }) { 20 | const account = useWeb3Store((state) => state.account); 21 | const chainId = useWeb3Store((state) => state.chainId); 22 | 23 | const staking = useStaking(); 24 | 25 | const withdrawInput = useInput(); 26 | 27 | const { mutate: poolTokenBalanceMutate } = useTokenBalance( 28 | account, 29 | pool?.address, 30 | ); 31 | 32 | const { data: poolTokenBalanceLocked, mutate: poolTokenBalanceLockedMutate } = 33 | useStakingBalanceLocked(account, pool?.address); 34 | 35 | const fmPoolTokenBalanceLocked = useFormattedBigNumber( 36 | poolTokenBalanceLocked, 37 | 4, 38 | ); 39 | 40 | async function withdrawPoolToken(event: FormEvent) { 41 | event.preventDefault(); 42 | 43 | const _id = toast.loading('Waiting for confirmation'); 44 | 45 | try { 46 | const withdrawAmount = withdrawInput.value; 47 | 48 | if (Number(withdrawAmount) <= MIN_INPUT_VALUE) { 49 | throw new Error( 50 | `Minium Withdraw: ${MIN_INPUT_VALUE} ${FARMING_LP_SYMBOL[chainId]}`, 51 | ); 52 | } 53 | 54 | const amount = parseUnits(withdrawAmount); 55 | 56 | const transaction = await staking.withdraw(pool?.address, amount); 57 | 58 | withdrawInput.clear(); 59 | 60 | toast.loading( 61 | , 66 | { 67 | id: _id, 68 | }, 69 | ); 70 | 71 | await transaction.wait(); 72 | 73 | toast.success( 74 | , 79 | { 80 | id: _id, 81 | }, 82 | ); 83 | 84 | poolTokenBalanceMutate(); 85 | poolTokenBalanceLockedMutate(); 86 | } catch (error) { 87 | handleError(error, _id); 88 | } 89 | } 90 | 91 | const withdrawInputIsMax = 92 | poolTokenBalanceLocked && 93 | withdrawInput.value === formatUnits(poolTokenBalanceLocked); 94 | const setWithdrawMax = () => { 95 | withdrawInput.setValue(formatUnits(poolTokenBalanceLocked)); 96 | }; 97 | 98 | return ( 99 |
100 |
101 |
102 |

Withdraw {pool?.name}

103 | 104 | {!!pool && ( 105 | 111 | 112 | 113 | )} 114 |
115 | 116 |
117 |
118 | 122 | 123 |
124 | 127 | 128 | 134 |
135 |
136 | 137 |

138 | {poolTokenBalanceLocked && fmPoolTokenBalanceLocked ? ( 139 | <> 140 | {`Available: ${fmPoolTokenBalanceLocked} ${FARMING_LP_SYMBOL[chainId]}`}{' '} 141 | {!withdrawInputIsMax && } 142 | 143 | ) : null} 144 |

145 |
146 | 147 |
148 | 151 |
152 |
153 |
154 | ); 155 | } 156 | -------------------------------------------------------------------------------- /components/identicon.tsx: -------------------------------------------------------------------------------- 1 | import Davatar from '@davatar/react'; 2 | 3 | export default function Identicon({ address }: { address: string }) { 4 | return ; 5 | } 6 | -------------------------------------------------------------------------------- /components/mobileMenu.tsx: -------------------------------------------------------------------------------- 1 | import Link from 'next/link'; 2 | import { Menu } from '@headlessui/react'; 3 | import { Menu as MenuIcon } from 'react-feather'; 4 | import cn from 'classnames'; 5 | 6 | const menuItemClassNames = 7 | 'flex rounded items-center w-full p-2 text-sm focus:outline-none'; 8 | 9 | function NextLink(props) { 10 | let { active = false, href, children, ...rest } = props; 11 | 12 | return ( 13 | 14 | 18 | {children} 19 | 20 | 21 | ); 22 | } 23 | 24 | export default function MobileMenu() { 25 | return ( 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | {({ active }) => ( 34 | 38 | FOLD 39 | 40 | )} 41 | 42 | 43 | {({ active }) => ( 44 | 45 | Stake 46 | 47 | )} 48 | 49 | 50 | {({ active }) => ( 51 | 52 | Forums 53 | 54 | )} 55 | 56 | 57 | {({ active }) => ( 58 | 62 | Support 63 | 64 | )} 65 | 66 | 67 | 68 | ); 69 | } 70 | -------------------------------------------------------------------------------- /components/navigation.tsx: -------------------------------------------------------------------------------- 1 | import classNames from 'classnames'; 2 | import { useRouter } from 'next/router'; 3 | import Link from 'next/link'; 4 | import AddTokensToMetaMask from './addTokensToMetamask'; 5 | import NetworkIndicator from './network'; 6 | import dynamic from 'next/dynamic'; 7 | import { Account } from './web3'; 8 | import OverflowMenu from './overflowMenu'; 9 | import MobileMenu from './mobileMenu'; 10 | 11 | function NavigationItem({ text, href }: { text: string; href: string }) { 12 | const { asPath } = useRouter(); 13 | 14 | const cachedClassNames = classNames( 15 | 'font-medium leading-5 flex items-center justify-center py-2 px-2 text-center leading-5 focus:outline-none focus:text-gray-300 rounded transition-colors', 16 | asPath === href ? 'text-white ' : 'text-gray-500', 17 | ); 18 | 19 | return ( 20 | 21 | {text} 22 | 23 | ); 24 | } 25 | 26 | const WalletModal = dynamic(() => import('../components/walletModal'), { 27 | ssr: false, 28 | }); 29 | 30 | export default function Navigation() { 31 | return ( 32 | 111 | ); 112 | } 113 | -------------------------------------------------------------------------------- /components/network.tsx: -------------------------------------------------------------------------------- 1 | import { ChainInfo, CHAIN_INFO, SupportedChainId } from '@/constants/chains'; 2 | import useWeb3Store from '@/hooks/useWeb3Store'; 3 | import { useMemo } from 'react'; 4 | 5 | export default function NetworkIndicator() { 6 | const chainId = useWeb3Store((state) => state.chainId); 7 | 8 | const info: ChainInfo = useMemo(() => { 9 | return CHAIN_INFO[chainId]; 10 | }, [chainId]); 11 | 12 | if ( 13 | typeof chainId === 'undefined' || 14 | chainId === SupportedChainId.MAINNET || 15 | !info 16 | ) { 17 | return null; 18 | } 19 | 20 | return ( 21 |
22 | {info.label} 23 |
24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /components/numericalInput.tsx: -------------------------------------------------------------------------------- 1 | import escapeRegExp, { INPUT_REGEX } from '@/utils/escapeRegExp'; 2 | import classNames from 'classnames'; 3 | import type { Dispatch, SetStateAction } from 'react'; 4 | 5 | type NumericalInputProps = { 6 | value: string | number; 7 | onChange: Dispatch>; 8 | } & Omit, 'ref' | 'onChange' | 'as'>; 9 | 10 | export default function NumericalInput({ 11 | value, 12 | onChange, 13 | placeholder, 14 | ...rest 15 | }: NumericalInputProps) { 16 | const cachedClassNames = classNames( 17 | 'appearance-none bg-transparent', 18 | 'text-right text-2xl font-normal font-mono', 19 | 'w-full h-10', 20 | 'focus:outline-none', 21 | ); 22 | 23 | const enforcer = (nextUserInput: string) => { 24 | if (nextUserInput === '' || INPUT_REGEX.test(escapeRegExp(nextUserInput))) { 25 | onChange(nextUserInput); 26 | } 27 | }; 28 | 29 | return ( 30 | enforcer(event.target.value.replace(/,/g, '.'))} 35 | // universal input options 36 | inputMode="decimal" 37 | autoComplete="off" 38 | autoCorrect="off" 39 | // text-specific options 40 | type="text" 41 | pattern="^[0-9]*[.,]?[0-9]*$" 42 | placeholder={placeholder || '0.0'} 43 | minLength={1} 44 | maxLength={79} 45 | spellCheck="false" 46 | /> 47 | ); 48 | } 49 | -------------------------------------------------------------------------------- /components/overflowMenu.tsx: -------------------------------------------------------------------------------- 1 | import { SUSHI_SWAP_LINKS } from '@/constants/tokens'; 2 | import { Menu } from '@headlessui/react'; 3 | import cn from 'classnames'; 4 | import Link from 'next/link'; 5 | import { MoreHorizontal } from 'react-feather'; 6 | 7 | function NextLink(props) { 8 | let { href, children, ...rest } = props; 9 | 10 | return ( 11 | 12 | {children} 13 | 14 | ); 15 | } 16 | 17 | const menuItemClassNames = 18 | 'flex rounded items-center w-full p-2 text-sm focus:outline-none'; 19 | 20 | export default function OverflowMenu() { 21 | return ( 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | {({ active }) => ( 30 | 36 | Buy FOLD on SushiSwap 37 | 38 | )} 39 | 40 | 41 | {({ active }) => ( 42 | 48 | About 49 | 50 | )} 51 | 52 | 53 | {({ active }) => ( 54 | 60 | Developers 61 | 62 | )} 63 | 64 | 65 | {({ active }) => ( 66 | 72 | Forums 73 | 74 | )} 75 | 76 | 77 | 78 | ); 79 | } 80 | -------------------------------------------------------------------------------- /components/panel.tsx: -------------------------------------------------------------------------------- 1 | import classNames from 'classnames'; 2 | import { forwardRef } from 'react'; 3 | 4 | type PanelProps = { 5 | light?: boolean; 6 | padding?: false | 'p-1' | 'p-2' | 'p-3' | 'p-4'; 7 | } & JSX.IntrinsicElements['div']; 8 | 9 | const Panel = forwardRef( 10 | ({ light = false, className = '', padding = 'p-4', ...rest }, ref) => { 11 | const cachedClassNames = classNames( 12 | className, 13 | 'rounded-xl ring-1 ring-inset ring-white', 14 | light 15 | ? 'bg-primary-300 ring-opacity-20' 16 | : 'bg-primary-400 ring-opacity-10', 17 | padding, 18 | ); 19 | 20 | return
; 21 | }, 22 | ); 23 | 24 | Panel.displayName = 'Panel'; 25 | 26 | export default Panel; 27 | -------------------------------------------------------------------------------- /components/stake/depositStake.tsx: -------------------------------------------------------------------------------- 1 | import Button, { MaxButton } from '../button'; 2 | import { MIN_INPUT_VALUE, MaxUint256 } from '@/constants/numbers'; 3 | import { formatUnits, parseUnits } from '@ethersproject/units'; 4 | import { useFoldToken, useDictatorDAO } from '@/hooks/useContract'; 5 | 6 | import { CONTRACT_ADDRESSES } from '@/constants/contracts'; 7 | import type { FormEvent } from 'react'; 8 | import NumericalInput from '../numericalInput'; 9 | import { TOKEN_ADDRESSES } from '@/constants/tokens'; 10 | import { TokenSingle } from '../tokenSelect'; 11 | import { TransactionToast } from '../customToast'; 12 | import handleError from '@/utils/handleError'; 13 | import toast from 'react-hot-toast'; 14 | import useFormattedBigNumber from '@/hooks/useFormattedBigNumber'; 15 | import useInput from '@/hooks/useInput'; 16 | import { useMemo } from 'react'; 17 | import useTokenAllowance from '@/hooks/view/useTokenAllowance'; 18 | import useTokenBalance from '@/hooks/view/useTokenBalance'; 19 | import useWeb3Store from '@/hooks/useWeb3Store'; 20 | import { useXFOLDStaked } from '@/hooks/view/usexFOLDStaked'; 21 | 22 | export default function DepositStake() { 23 | const account = useWeb3Store((state) => state.account); 24 | const chainId = useWeb3Store((state) => state.chainId); 25 | 26 | const DOMO_DAO = useDictatorDAO(); 27 | 28 | const { data: xfoldBalance, mutate: xfoldBalanceMutate } = useTokenBalance( 29 | account, 30 | TOKEN_ADDRESSES.FOLD[chainId], 31 | ); 32 | 33 | const { mutate: xfoldStakedMutate } = useXFOLDStaked(); 34 | 35 | const formattedFOLDBalance = useFormattedBigNumber(xfoldBalance); 36 | 37 | const depositInput = useInput(); 38 | 39 | const foldContract = useFoldToken(); 40 | 41 | const { data: foldAllowance, mutate: xfoldAllowanceMutate } = 42 | useTokenAllowance( 43 | TOKEN_ADDRESSES.FOLD[chainId], 44 | account, 45 | CONTRACT_ADDRESSES.DictatorDAO[chainId], 46 | ); 47 | 48 | const foldNeedsApproval = useMemo(() => { 49 | if (!!foldAllowance && depositInput.hasValue) { 50 | return foldAllowance.isZero(); 51 | } 52 | 53 | return; 54 | }, [foldAllowance, depositInput.hasValue]); 55 | 56 | async function mintXFOLD(event: FormEvent) { 57 | event.preventDefault(); 58 | 59 | const _id = toast.loading('Waiting for confirmation'); 60 | 61 | try { 62 | const depositAmount = depositInput.value; 63 | 64 | if (Number(depositAmount) <= MIN_INPUT_VALUE) { 65 | throw new Error(`Minimum Deposit: ${MIN_INPUT_VALUE} FOLD`); 66 | } 67 | 68 | const amount = parseUnits(depositAmount); 69 | 70 | if (amount.gt(xfoldBalance)) { 71 | throw new Error(`Maximum Deposit: ${formattedFOLDBalance} FOLD`); 72 | } 73 | 74 | // await foldContract.approve('0x454BD9E2B29EB5963048cC1A8BD6fD44e89899Cb', amount) 75 | depositInput.clear(); 76 | 77 | let transaction = await DOMO_DAO.approve( 78 | '0xd084944d3c05CD115C09d072B9F44bA3E0E45921', 79 | amount, 80 | ); 81 | toast.loading( 82 | , 87 | { id: _id }, 88 | ); 89 | 90 | await transaction.wait(); 91 | 92 | toast.success( 93 | , 98 | { id: _id }, 99 | ); 100 | 101 | transaction = await DOMO_DAO.mint( 102 | amount, 103 | '0xA0766B65A4f7B1da79a1AF79aC695456eFa28644', 104 | ); 105 | 106 | toast.loading( 107 | , 112 | { id: _id }, 113 | ); 114 | 115 | await transaction.wait(); 116 | 117 | toast.success( 118 | , 123 | { id: _id }, 124 | ); 125 | 126 | // // const transaction = await STAKING_CONTRACT.deposit('0xA0766B65A4f7B1da79a1AF79aC695456eFa28644', amount); 127 | // const transaction = await FOLD_ERC20.approve('0xd084944d3c05CD115C09d072B9F44bA3E0E45921', amount); 128 | 129 | // depositInput.clear(); 130 | // toast.loading( 131 | // , 136 | // { id: _id }, 137 | // ); 138 | 139 | // await transaction.wait(); 140 | 141 | // toast.success( 142 | // , 147 | // { id: _id }, 148 | // ); 149 | 150 | xfoldStakedMutate(); 151 | 152 | xfoldBalanceMutate(); 153 | } catch (error) { 154 | handleError(error, _id); 155 | } 156 | } 157 | 158 | async function approveXFOLD() { 159 | const _id = toast.loading('Waiting for confirmation'); 160 | 161 | const depositAmount = depositInput.value; 162 | 163 | if (Number(depositAmount) <= MIN_INPUT_VALUE) { 164 | throw new Error(`Minimum Deposit: ${MIN_INPUT_VALUE} FOLD`); 165 | } 166 | 167 | const amount = parseUnits(depositAmount); 168 | 169 | if (amount.gt(xfoldBalance)) { 170 | throw new Error(`Maximum Deposit: ${formattedFOLDBalance} FOLD`); 171 | } 172 | 173 | try { 174 | const transaction = await foldContract.approve( 175 | CONTRACT_ADDRESSES.DictatorDAO[chainId], 176 | amount, 177 | ); 178 | 179 | toast.loading(`Approve FOLD`, { id: _id }); 180 | 181 | await transaction.wait(); 182 | 183 | toast.success(`Approve FOLD`, { id: _id }); 184 | 185 | xfoldAllowanceMutate(); 186 | } catch (error) { 187 | handleError(error, _id); 188 | } 189 | } 190 | 191 | const inputIsMax = 192 | !!xfoldBalance && depositInput.value === formatUnits(xfoldBalance); 193 | 194 | const setMax = () => { 195 | depositInput.setValue(formatUnits(xfoldBalance)); 196 | }; 197 | 198 | return ( 199 |
200 |
201 |

Unlock FOLD

202 |
203 | 204 |
205 |
206 | 207 | 208 |
209 | 212 | 213 | 219 |
220 |
221 | 222 |

223 | {xfoldBalance && formattedFOLDBalance ? ( 224 | <> 225 | {`Balance: ${formattedFOLDBalance} FOLD`}{' '} 226 | {!inputIsMax && } 227 | 228 | ) : null} 229 |

230 |
231 | 232 |
233 | {foldNeedsApproval && ( 234 | 237 | )} 238 | 239 | 245 |
246 |
247 | ); 248 | } 249 | -------------------------------------------------------------------------------- /components/stake/lockStake.tsx: -------------------------------------------------------------------------------- 1 | import * as Slider from '@radix-ui/react-slider'; 2 | 3 | import { FormEvent, useMemo } from 'react'; 4 | import { useFoldToken, useTokenContract } from '@/hooks/useContract'; 5 | 6 | import { BigNumber } from '@ethersproject/bignumber'; 7 | import Button from '../button'; 8 | import { TransactionToast } from '../customToast'; 9 | import calculateLockupMultiplier from '@/utils/calculateLockupMultiplier'; 10 | import dayjs from 'dayjs'; 11 | import getFutureTimestamp from '@/utils/getFutureTimestamp'; 12 | import handleError from '@/utils/handleError'; 13 | import toast from 'react-hot-toast'; 14 | import { useDictatorDAO } from '@/hooks/useContract'; 15 | import useInput from '@/hooks/useInput'; 16 | import useUserLockedUntil from '@/hooks/view/useUserLockedUntil'; 17 | import useWeb3Store from '@/hooks/useWeb3Store'; 18 | import { useXFOLDStaked } from '@/hooks/view/usexFOLDStaked'; 19 | 20 | export default function LockStake() { 21 | const chainId = useWeb3Store((state) => state.chainId); 22 | 23 | const lockupPeriod = useInput(); 24 | 25 | const FOLD_ERC20 = useFoldToken(); 26 | 27 | const { data: userLockedUntil, mutate: userLockedUntilMutate } = 28 | useUserLockedUntil(); 29 | 30 | const { data: xfoldStaked } = useXFOLDStaked(); 31 | 32 | const isLockupPeriodAfterCurrentLockedTimestamp = useMemo(() => { 33 | if (typeof userLockedUntil === 'undefined') { 34 | return; 35 | } 36 | 37 | const newLockupPeriod = dayjs().add(Number(lockupPeriod.value) - 1, 'days'); 38 | 39 | return newLockupPeriod.isAfter(dayjs.unix(userLockedUntil.timestamp)); 40 | }, [userLockedUntil, lockupPeriod.value]); 41 | 42 | async function lockXFOLD(event: FormEvent) { 43 | event.preventDefault(); 44 | 45 | const _id = toast.loading('Waiting for confirmation'); 46 | 47 | try { 48 | if (xfoldStaked.isZero()) { 49 | throw new Error('No Balance To Lock'); 50 | } 51 | 52 | const days = Number(lockupPeriod.value); 53 | 54 | if (days > 365 * 2) { 55 | throw new Error('Max Lockup Time Is 2 Years (730 Days)'); 56 | } 57 | 58 | /** 59 | * Account for today if the days is equal to 2 years exact, so remove a day 60 | */ 61 | const futureTimestamp = getFutureTimestamp( 62 | days === 365 * 2 ? days - 1 : days, 63 | ); 64 | 65 | const transaction = await FOLD_ERC20.lock( 66 | BigNumber.from(futureTimestamp), 67 | ); 68 | 69 | lockupPeriod.clear(); 70 | 71 | toast.loading( 72 | , 77 | { id: _id }, 78 | ); 79 | 80 | await transaction.wait(); 81 | 82 | toast.success( 83 | , 88 | { id: _id }, 89 | ); 90 | 91 | userLockedUntilMutate(); 92 | } catch (error) { 93 | handleError(error, _id); 94 | } 95 | } 96 | 97 | const lockupPeriodMultiplier = useMemo( 98 | () => calculateLockupMultiplier(lockupPeriod.value), 99 | [lockupPeriod.value], 100 | ); 101 | 102 | return ( 103 |
104 |
105 |
106 |

Lock Stake

107 |
108 | 109 |
110 |
111 |
112 |
113 | 120 | 121 | 128 | 129 | 136 |
137 | 138 |
139 | 152 | lockupPeriod.setValue( 153 | event.target.valueAsNumber >= 356 * 2 154 | ? String(365 * 2) 155 | : event.target.value, 156 | ) 157 | } 158 | /> 159 | Days 160 |
161 |
162 | 163 | 171 | lockupPeriod.setValue(String(value[0])) 172 | } 173 | > 174 | 175 | 176 | 177 | 178 | 179 |
180 |
181 | 182 |
183 |
184 |

Rewards Multiplier

185 | 186 |

{`${lockupPeriodMultiplier}x`}

187 |
188 |
189 | 190 | {userLockedUntil && userLockedUntil.isLocked && ( 191 | <> 192 |
193 | 194 |
195 |

Currently Locked Until

196 | 197 |

198 | {userLockedUntil.formatted}{' '} 199 | ({`${userLockedUntil.multiplier}x`}) 200 |

201 |
202 | 203 | )} 204 | 205 |
206 | 215 |
216 |
217 | 218 | ); 219 | } 220 | -------------------------------------------------------------------------------- /components/stake/withdrawStake.tsx: -------------------------------------------------------------------------------- 1 | import Button, { MaxButton } from '../button'; 2 | import { formatUnits, parseUnits } from '@ethersproject/units'; 3 | import { getxFOLDStaked, useXFOLDStaked } from '@/hooks/view/usexFOLDStaked'; 4 | import { useMemo } from 'react'; 5 | import type { FormEvent } from 'react'; 6 | import { MIN_INPUT_VALUE } from '@/constants/numbers'; 7 | import NumericalInput from '../numericalInput'; 8 | import { TOKEN_ADDRESSES } from '@/constants/tokens'; 9 | import { CONTRACT_ADDRESSES } from '@/constants/contracts'; 10 | import { TokenSingle } from '../tokenSelect'; 11 | import { TransactionToast } from '../customToast'; 12 | import dayjs from 'dayjs'; 13 | import handleError from '@/utils/handleError'; 14 | import relativeTime from 'dayjs/plugin/relativeTime'; 15 | import toast from 'react-hot-toast'; 16 | import { 17 | useDictatorDAO, 18 | useOperatorAddress, 19 | useFoldToken, 20 | } from '@/hooks/useContract'; 21 | import useFormattedBigNumber from '@/hooks/useFormattedBigNumber'; 22 | import useInput from '@/hooks/useInput'; 23 | import useTokenAllowance from '@/hooks/view/useTokenAllowance'; 24 | import useTokenBalance from '@/hooks/view/useTokenBalance'; 25 | import useWeb3Store from '@/hooks/useWeb3Store'; 26 | import { ethers } from 'ethers'; 27 | 28 | dayjs.extend(relativeTime); 29 | 30 | export default function WithdrawStake() { 31 | const account = useWeb3Store((state) => state.account); 32 | const chainId = useWeb3Store((state) => state.chainId); 33 | 34 | const { mutate: xfoldBalanceMutate } = useTokenBalance( 35 | account, 36 | TOKEN_ADDRESSES.xFOLD[chainId], 37 | ); 38 | 39 | const { data: xfoldStaked, mutate: xfoldStakedMutate } = useXFOLDStaked(); 40 | 41 | const DOMO_DAO = useDictatorDAO(); 42 | const FOLD_ERC20 = useFoldToken(); 43 | 44 | const withdrawInput = useInput(); 45 | 46 | const formattedXFOLDStaked = useFormattedBigNumber(xfoldStaked); 47 | 48 | 49 | const { data: xfoldAllowance, mutate: xfoldAllowanceMutate } = 50 | useTokenAllowance( 51 | CONTRACT_ADDRESSES.DictatorDAO[chainId], 52 | account, 53 | TOKEN_ADDRESSES.FOLD[chainId], 54 | ); 55 | 56 | const convertedxFoldAllowance = xfoldAllowance ? ethers.utils.formatEther(xfoldAllowance) : "0"; 57 | 58 | const xfoldNeedsApproval = useMemo(() => { 59 | if (parseFloat(convertedxFoldAllowance) < parseFloat(withdrawInput.value) && withdrawInput.hasValue) { 60 | return true; 61 | } 62 | 63 | return; 64 | }, [withdrawInput.value, convertedxFoldAllowance, withdrawInput.hasValue]); 65 | 66 | async function withdrawXFOLD(event: FormEvent) { 67 | event.preventDefault(); 68 | 69 | const _id = toast.loading('Waiting for confirmation'); 70 | 71 | try { 72 | const withdrawAmount = withdrawInput.value; 73 | 74 | if (Number(withdrawAmount) <= MIN_INPUT_VALUE) { 75 | throw new Error(`Minimum Withdraw: ${MIN_INPUT_VALUE} XFOLD`); 76 | } 77 | 78 | const amount = parseUnits(withdrawAmount); 79 | 80 | if (amount.gt(xfoldStaked)) { 81 | throw new Error(`Maximum Withdraw: ${formattedXFOLDStaked} XFOLD`); 82 | } 83 | 84 | withdrawInput.clear(); 85 | 86 | const transaction = await DOMO_DAO.burn(account, amount); 87 | 88 | // const transaction = await XFOLD.burn( 89 | // // @ts-ignore 90 | // to, 91 | // // @ts-ignore 92 | // shares, 93 | // ); 94 | 95 | toast.loading( 96 | , 101 | { id: _id }, 102 | ); 103 | 104 | await transaction.wait(); 105 | 106 | toast.success( 107 | , 112 | { id: _id }, 113 | ); 114 | 115 | xfoldStakedMutate(); 116 | xfoldBalanceMutate(); 117 | } catch (error) { 118 | handleError(error, _id); 119 | } 120 | } 121 | 122 | const inputIsMax = 123 | !!xfoldStaked && withdrawInput.value === formatUnits(xfoldStaked); 124 | 125 | const setMax = () => { 126 | withdrawInput.setValue(formatUnits(xfoldStaked)); 127 | }; 128 | 129 | async function approveXFOLD() { 130 | const _id = toast.loading('Waiting for confirmation'); 131 | 132 | const depositAmount = withdrawInput.value; 133 | 134 | if (Number(depositAmount) <= MIN_INPUT_VALUE) { 135 | throw new Error(`Minimum Withdraw: ${MIN_INPUT_VALUE} XFOLD`); 136 | } 137 | 138 | const amount = parseUnits(depositAmount); 139 | 140 | if (amount.gt(xfoldStaked)) { 141 | throw new Error(`Maximum Withdraw: ${formattedXFOLDStaked} XFOLD`); 142 | } 143 | 144 | try { 145 | const transaction = await DOMO_DAO.approve( 146 | CONTRACT_ADDRESSES.FOLD[chainId], 147 | amount, 148 | ); 149 | 150 | toast.loading(`Approve XFOLD`, { id: _id }); 151 | 152 | await transaction.wait(); 153 | 154 | toast.success(`Approve XFOLD`, { id: _id }); 155 | 156 | xfoldAllowanceMutate(); 157 | } catch (error) { 158 | handleError(error, _id); 159 | } 160 | }; 161 | 162 | return ( 163 |
164 |
165 |

Withdraw Stake

166 |
167 | 168 |
169 |
170 | 171 | 172 |
173 | 176 | 177 | 183 |
184 |
185 | 186 |

187 | {xfoldStaked && formattedXFOLDStaked ? ( 188 | <> 189 | {`Available: ${formattedXFOLDStaked} xFOLD`}{' '} 190 | {!inputIsMax && } 191 | 192 | ) : null} 193 |

194 |
195 |
196 | {xfoldNeedsApproval && ( 197 | 200 | )} 201 |
202 | 203 |
204 | 207 |
208 |
209 | ); 210 | } 211 | -------------------------------------------------------------------------------- /components/tabs.ts: -------------------------------------------------------------------------------- 1 | import classNames from 'classnames'; 2 | 3 | export const tabPanelClassNames = 'focus:outline-none focus:ring-opacity-20'; 4 | 5 | export const tabClassNames = ({ selected }: { selected: boolean }) => 6 | classNames( 7 | 'w-full py-2.5 text-sm leading-5 font-medium rounded-lg', 8 | 'focus:outline-none focus:ring-4', 9 | selected 10 | ? 'bg-white shadow text-primary' 11 | : 'text-gray-300 hover:bg-primary-300 hover:text-white', 12 | ); 13 | -------------------------------------------------------------------------------- /components/tokenSelect.tsx: -------------------------------------------------------------------------------- 1 | import { TokenNames } from '@/constants/tokens'; 2 | import { Listbox } from '@headlessui/react'; 3 | import classNames from 'classnames'; 4 | import type { Dispatch, SetStateAction } from 'react'; 5 | import { ChevronDown } from 'react-feather'; 6 | 7 | export type Token = { 8 | symbol: keyof typeof TokenNames; 9 | address: string; 10 | out?: bigint; 11 | }; 12 | 13 | type TokenSelectProps = { 14 | value: Token; 15 | onChange: Dispatch< 16 | SetStateAction<{ 17 | symbol: string; 18 | address: string; 19 | }> 20 | >; 21 | tokens: Token[]; 22 | order: 'ASC' | 'DESC'; 23 | }; 24 | 25 | export default function TokenSelect({ 26 | value, 27 | onChange, 28 | tokens, 29 | order, 30 | }: TokenSelectProps) { 31 | const sortTokens = (a: Token, b: Token) => { 32 | if (order === 'ASC') { 33 | return a.out < b.out ? -1 : a.out > b.out ? 1 : 0; 34 | } else { 35 | return a.out > b.out ? -1 : a.out < b.out ? 1 : 0; 36 | } 37 | }; 38 | 39 | return ( 40 | 41 |
42 | 48 | {value && ( 49 | {value?.symbol} 58 | )} 59 | 60 | 61 | {value ? value.symbol : 'Protocol Activity'} 62 | 63 | 64 | 65 | 67 | 68 | 69 | 70 | {tokens?.sort(sortTokens)?.map((token, tokenIndex) => ( 71 | 74 | classNames( 75 | 'cursor-default select-none p-2 rounded text-white', 76 | active ? 'bg-white/[0.10]' : '', 77 | ) 78 | } 79 | value={token} 80 | disabled={token === value} 81 | > 82 | {({ selected }) => ( 83 |
89 |
90 | {token.symbol} 99 | 100 | 106 | {token.symbol} 107 | 108 |
109 | 110 | {tokenIndex === 0 && typeof token.out === 'bigint' && ( 111 |
112 | Best Returns 113 |
114 | )} 115 |
116 | )} 117 |
118 | ))} 119 |
120 |
121 |
122 | ); 123 | } 124 | 125 | export function TokenSingle({ symbol }: { symbol: string }) { 126 | return ( 127 |
128 | {symbol} 135 | 136 | {symbol} 137 |
138 | ); 139 | } 140 | 141 | export function TokenPair({ 142 | pairs, 143 | symbol, 144 | }: { 145 | pairs: string[]; 146 | symbol: string; 147 | }) { 148 | return ( 149 |
150 |
151 | {!!pairs ? ( 152 | pairs?.map((pair, pairIndex) => ( 153 |
154 |
155 | 156 | {pair} 163 |
164 | )) 165 | ) : ( 166 | <> 167 |
168 |
169 | 170 | )} 171 |
172 | 173 | {symbol} 174 |
175 | ); 176 | } 177 | -------------------------------------------------------------------------------- /components/walletModal.tsx: -------------------------------------------------------------------------------- 1 | import useMetaMaskOnboarding from '@/hooks/useMetaMaskOnboarding'; 2 | import useWalletModal from '@/hooks/useWalletModal'; 3 | import { injected } from '@/lib/connectors/metamask'; 4 | import { walletconnect } from '@/lib/connectors/walletconnect'; 5 | import MetaMaskOutline from '@/svgs/MetaMaskOutline'; 6 | import WalletConnect from '@/svgs/WalletConnect'; 7 | import { Dialog, Transition } from '@headlessui/react'; 8 | import { Fragment } from 'react'; 9 | import { X } from 'react-feather'; 10 | import toast from 'react-hot-toast'; 11 | 12 | export default function WalletModal() { 13 | const isOpen = useWalletModal((state) => state.isOpen); 14 | const close = useWalletModal((state) => state.close); 15 | 16 | const { isMetaMaskInstalled, isWeb3Available, startOnboarding } = 17 | useMetaMaskOnboarding(); 18 | 19 | async function activateWalletConnect() { 20 | try { 21 | await walletconnect.activate(); 22 | 23 | close(); 24 | } catch (error) { 25 | console.error(error); 26 | 27 | toast.error(error.message); 28 | } 29 | } 30 | 31 | async function activateInjected() { 32 | try { 33 | await injected.activate(); 34 | 35 | close(); 36 | } catch (error) { 37 | console.error(error); 38 | 39 | toast.error(error.message); 40 | } 41 | } 42 | 43 | return ( 44 | 45 | 50 |
51 | 60 | 61 | 62 | 63 | {/* This element is to trick the browser into centering the modal contents. */} 64 | 70 | 79 |
80 |
81 | 82 | Connect Wallet 83 | 84 | 85 | 91 |
92 | 93 |
94 | 110 | 111 | 121 |
122 |
123 |
124 |
125 |
126 |
127 | ); 128 | } 129 | -------------------------------------------------------------------------------- /components/web3.tsx: -------------------------------------------------------------------------------- 1 | import { TOKEN_ADDRESSES } from '@/constants/tokens'; 2 | import useENSName from '@/hooks/useENSName'; 3 | import useWalletModal from '@/hooks/useWalletModal'; 4 | import useWeb3Store from '@/hooks/useWeb3Store'; 5 | import useTokenBalance from '@/hooks/view/useTokenBalance'; 6 | import shortenAddress from '@/utils/shortenAddress'; 7 | import { formatUnits } from '@ethersproject/units'; 8 | import { Menu } from '@headlessui/react'; 9 | import cn from 'classnames'; 10 | import { useCallback, useMemo } from 'react'; 11 | import Identicon from './identicon'; 12 | 13 | const menuItemClassNames = 14 | 'flex rounded items-center w-full p-2 text-sm focus:outline-none'; 15 | 16 | export function Account() { 17 | const account = useWeb3Store((state) => state.account); 18 | const connector = useWeb3Store((state) => state.connector); 19 | const chainId = useWeb3Store((state) => state.chainId); 20 | const reset = useWeb3Store((state) => state.reset); 21 | 22 | const openWalletModal = useWalletModal((state) => state.open); 23 | 24 | const ENSName = useENSName(account); 25 | 26 | const { data: foldBalance } = useTokenBalance( 27 | account, 28 | TOKEN_ADDRESSES.FOLD[chainId], 29 | ); 30 | 31 | const formattedFOLDBalance = useMemo( 32 | () => Number(formatUnits(foldBalance ?? 0)).toFixed(2), 33 | [foldBalance], 34 | ); 35 | 36 | const disconnect = useCallback(() => { 37 | connector?.deactivate(); 38 | 39 | reset(); 40 | }, [connector, reset]); 41 | 42 | if (account) 43 | return ( 44 | 45 |
46 | {foldBalance && ( 47 |
48 | {`${formattedFOLDBalance} FOLD`} 49 |
50 | )} 51 | 52 | 53 | 54 | 55 | {ENSName ?? shortenAddress(account)} 56 | 57 |
58 | 59 | 60 | 61 | {({ active }) => ( 62 | 68 | )} 69 | 70 | 71 |
72 | ); 73 | 74 | return ( 75 | 81 | ); 82 | } 83 | -------------------------------------------------------------------------------- /components/xfold.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /connectors.ts: -------------------------------------------------------------------------------- 1 | import { InjectedConnector } from '@wwwr/injectedprovider'; 2 | 3 | export const injected = new InjectedConnector({ 4 | supportedChainIds: [1, 4], 5 | }); 6 | -------------------------------------------------------------------------------- /constants/chains.ts: -------------------------------------------------------------------------------- 1 | // TODO Replace INFURA_ID 2 | export const INFURA_ID = '3ff0c675dc614116aa126b14f6368971'; 3 | 4 | export enum SupportedChainId { 5 | MAINNET = 1, 6 | RINKEBY = 4, 7 | } 8 | 9 | export interface ChainInfo { 10 | explorer: string; 11 | label: string; 12 | } 13 | 14 | export const CHAIN_INFO: Record = { 15 | [SupportedChainId.MAINNET]: { 16 | explorer: 'https://etherscan.io/', 17 | label: 'Mainnet', 18 | }, 19 | [SupportedChainId.RINKEBY]: { 20 | explorer: 'https://rinkeby.etherscan.io/', 21 | label: 'Rinkeby', 22 | }, 23 | }; 24 | -------------------------------------------------------------------------------- /constants/contracts.ts: -------------------------------------------------------------------------------- 1 | import { SupportedChainId } from './chains'; 2 | 3 | export enum ContractNames { 4 | STAKING = 'Staking', 5 | DOMODAO = 'DictatorDAO', 6 | OPERATOR_ADDRESS = 'Operator', 7 | FOLD = 'FOLD', 8 | FOLD_ADDRESS = 'FOLD', 9 | } 10 | 11 | type AddressMap = Record; 12 | 13 | type ContractAddresses = Record; 14 | 15 | export const CONTRACT_ADDRESSES: ContractAddresses = { 16 | [ContractNames.FOLD]: { 17 | [SupportedChainId.MAINNET]: '0xd084944d3c05CD115C09d072B9F44bA3E0E45921', 18 | [SupportedChainId.RINKEBY]: '0xd084944d3c05CD115C09d072B9F44bA3E0E45921', 19 | }, 20 | [ContractNames.FOLD_ADDRESS]: { 21 | [SupportedChainId.MAINNET]: '0xd084944d3c05CD115C09d072B9F44bA3E0E45921', 22 | [SupportedChainId.RINKEBY]: '0xd084944d3c05CD115C09d072B9F44bA3E0E45921', 23 | }, 24 | [ContractNames.STAKING]: { 25 | [SupportedChainId.MAINNET]: '0x454BD9E2B29EB5963048cC1A8BD6fD44e89899Cb', 26 | [SupportedChainId.RINKEBY]: '0x454BD9E2B29EB5963048cC1A8BD6fD44e89899Cb', 27 | }, 28 | [ContractNames.DOMODAO]: { 29 | [SupportedChainId.MAINNET]: '0x454BD9E2B29EB5963048cC1A8BD6fD44e89899Cb', 30 | [SupportedChainId.RINKEBY]: '0x454BD9E2B29EB5963048cC1A8BD6fD44e89899Cb', 31 | }, 32 | [ContractNames.OPERATOR_ADDRESS]: { 33 | [SupportedChainId.MAINNET]: '0xA0766B65A4f7B1da79a1AF79aC695456eFa28644', 34 | [SupportedChainId.RINKEBY]: '0xA0766B65A4f7B1da79a1AF79aC695456eFa28644', 35 | }, 36 | }; 37 | 38 | export const OPERATOR: AddressMap = { 39 | [SupportedChainId.MAINNET]: '0xA0766B65A4f7B1da79a1AF79aC695456eFa28644', 40 | [SupportedChainId.RINKEBY]: '0xA0766B65A4f7B1da79a1AF79aC695456eFa28644', 41 | }; 42 | -------------------------------------------------------------------------------- /constants/farming.ts: -------------------------------------------------------------------------------- 1 | import { SupportedChainId } from './chains'; 2 | 3 | export type FarmingPool = { 4 | address: string; 5 | name: string; 6 | pairs: string[]; 7 | link: string; 8 | }; 9 | 10 | export const FARMING_LP_SYMBOL = { 11 | [SupportedChainId.MAINNET]: 'SLP', 12 | [SupportedChainId.RINKEBY]: 'UNI-V2', 13 | }; 14 | 15 | export const LP_FARMING_POOLS: Record = { 16 | [SupportedChainId.MAINNET]: [ 17 | { 18 | address: '0x454BD9E2B29EB5963048cC1A8BD6fD44e89899Cb', 19 | name: 'xFOLD', 20 | pairs: ['FOLD', 'xFOLD'], 21 | link: 'https://analytics.sushi.com/pairs/0xa914a9b9e03b6af84f9c6bd2e0e8d27d405695db', 22 | }, 23 | { 24 | address: '0xd084944d3c05cd115c09d072b9f44ba3e0e45921', 25 | name: 'SushiSwap FOLD/USDC LP', 26 | pairs: ['FOLD', 'USDC'], 27 | link: 'https://app.sushi.com/add/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48/0xd084944d3c05CD115C09d072B9F44bA3E0E45921', 28 | }, 29 | ], 30 | 4: undefined, 31 | }; 32 | -------------------------------------------------------------------------------- /constants/numbers.ts: -------------------------------------------------------------------------------- 1 | import { SupportedChainId } from './chains'; 2 | 3 | // 604800 = 7 Days 4 | // 86400 = 1 days 5 | export const EPOCH_DURATION = 86400; 6 | 7 | /** 8 | * example: export const EPOCH_REWARDS = 2403846.153; 9 | 10 | */ 11 | export const EPOCH_REWARDS = 0; 12 | 13 | /** 14 | *LP Rewards 15 | * example: export const LP_EPOCH_REWARDS = 96153.84; 16 | */ 17 | export const LP_EPOCH_REWARDS = 0; 18 | 19 | export const DAO_THRESHOLD = { 20 | [SupportedChainId.MAINNET]: 100_000, 21 | // [SupportedChainId.RINKEBY]: 100_000, 22 | }; 23 | 24 | export const MaxUint256 = BigInt( 25 | '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 26 | ); 27 | 28 | export const MIN_INPUT_VALUE = 0.00000000000000001; 29 | 30 | export const MAX_FOLD_MINTABLE = 2_000_000; 31 | -------------------------------------------------------------------------------- /constants/tokens.ts: -------------------------------------------------------------------------------- 1 | import { SupportedChainId } from './chains'; 2 | 3 | export enum TokenNames { 4 | FOLD = 'FOLD', 5 | XFOLD = 'xFOLD', 6 | xFOLD = 'xFOLD', 7 | ETH = 'WETH', 8 | SLP = 'SLP', 9 | USDC = 'USDC', 10 | } 11 | 12 | type AddressMap = Record; 13 | 14 | type TokenAddresses = Partial>; 15 | 16 | export const TOKEN_ADDRESSES: TokenAddresses = { 17 | [TokenNames.FOLD]: { 18 | [SupportedChainId.MAINNET]: '0xd084944d3c05CD115C09d072B9F44bA3E0E45921', 19 | [SupportedChainId.RINKEBY]: '0xd084944d3c05CD115C09d072B9F44bA3E0E45921', 20 | }, 21 | [TokenNames.XFOLD]: { 22 | [SupportedChainId.MAINNET]: '0x454BD9E2B29EB5963048cC1A8BD6fD44e89899Cb', 23 | [SupportedChainId.RINKEBY]: '0x454BD9E2B29EB5963048cC1A8BD6fD44e89899Cb', 24 | }, 25 | [TokenNames.xFOLD]: { 26 | [SupportedChainId.MAINNET]: '0x454BD9E2B29EB5963048cC1A8BD6fD44e89899Cb', 27 | [SupportedChainId.RINKEBY]: '0x454BD9E2B29EB5963048cC1A8BD6fD44e89899Cb', 28 | }, 29 | }; 30 | 31 | export const TOKEN_NAMES_BY_ADDRESS: Record = { 32 | '0xd084944d3c05CD115C09d072B9F44bA3E0E45921': 'FOLD', 33 | '0x454BD9E2B29EB5963048cC1A8BD6fD44e89899Cb': 'XFOLD', 34 | // @ts-ignore 35 | '0x454BD9E2B29EB5963048cC1A8BD6fD44e89899Cb': 'xFOLD', 36 | '0x57ab1ec28d129707052df4df418d58a2d46d5f51': 'USDC', 37 | '0x5e74c9036fb86bd7ecdcb084a0673efc32ea31cb': 'ETH', 38 | '0x6a22e5e94388464181578aa7a6b869e00fe27846': 'SLP', 39 | }; 40 | 41 | type TokenCategories = Partial< 42 | Record 43 | >; 44 | 45 | export const TOKEN_CATEGORY_BY_SYMBOL: TokenCategories = { 46 | [TokenNames.FOLD]: 'CURRENCY', 47 | [TokenNames.USDC]: 'CURRENCY', 48 | [TokenNames.SLP]: 'CRYPTO', 49 | [TokenNames.ETH]: 'COMMODITY', 50 | [TokenNames.XFOLD]: 'COMMODITY', 51 | [TokenNames.xFOLD]: 'COMMODITY', 52 | }; 53 | 54 | export const TOKEN_COLORS = [ 55 | 'bg-green-500', 56 | 'bg-blue-500', 57 | 'bg-purple-500', 58 | 'bg-yellow-500', 59 | 'bg-indigo-500', 60 | 'bg-pink-500', 61 | ]; 62 | 63 | export type TokenAsset = { 64 | address: string; 65 | decimals: number; 66 | symbol: string; 67 | }; 68 | 69 | type TokenAssets = Partial< 70 | Record> 71 | >; 72 | 73 | export const TOKEN_ASSETS: TokenAssets = { 74 | [TokenNames.XFOLD]: { 75 | [SupportedChainId.MAINNET]: { 76 | address: TOKEN_ADDRESSES[TokenNames.XFOLD][SupportedChainId.MAINNET], 77 | decimals: 18, 78 | symbol: TokenNames.XFOLD, 79 | }, 80 | [SupportedChainId.RINKEBY]: { 81 | address: TOKEN_ADDRESSES[TokenNames.XFOLD][SupportedChainId.RINKEBY], 82 | decimals: 18, 83 | symbol: TokenNames.XFOLD, 84 | }, 85 | }, 86 | [TokenNames.xFOLD]: { 87 | [SupportedChainId.MAINNET]: { 88 | address: TOKEN_ADDRESSES[TokenNames.xFOLD][SupportedChainId.MAINNET], 89 | decimals: 18, 90 | symbol: TokenNames.xFOLD, 91 | }, 92 | [SupportedChainId.RINKEBY]: { 93 | address: TOKEN_ADDRESSES[TokenNames.xFOLD][SupportedChainId.RINKEBY], 94 | decimals: 18, 95 | symbol: TokenNames.xFOLD, 96 | }, 97 | }, 98 | [TokenNames.FOLD]: { 99 | [SupportedChainId.MAINNET]: { 100 | address: TOKEN_ADDRESSES[TokenNames.FOLD][SupportedChainId.MAINNET], 101 | decimals: 18, 102 | symbol: TokenNames.FOLD, 103 | }, 104 | [SupportedChainId.RINKEBY]: { 105 | address: TOKEN_ADDRESSES[TokenNames.FOLD][SupportedChainId.RINKEBY], 106 | decimals: 18, 107 | symbol: TokenNames.FOLD, 108 | }, 109 | }, 110 | }; 111 | 112 | export const SUSHI_SWAP_LINKS: Partial> = { 113 | [TokenNames.FOLD]: 114 | 'https://app.sushi.com/swap?outputCurrency=0xd084944d3c05CD115C09d072B9F44bA3E0E45921', 115 | [TokenNames.XFOLD]: 116 | 'https://app.sushi.com/swap?outputCurrency=0x454BD9E2B29EB5963048cC1A8BD6fD44e89899Cb', 117 | [TokenNames.xFOLD]: 118 | 'https://app.sushi.com/swap?outputCurrency=0x454BD9E2B29EB5963048cC1A8BD6fD44e89899Cb', 119 | }; 120 | 121 | export const TOKEN_BUY_LINKS: Partial> = { 122 | [TokenNames.FOLD]: 123 | 'https://app.sushi.com/swap?outputCurrency=0xd084944d3c05CD115C09d072B9F44bA3E0E45921', 124 | [TokenNames.USDC]: '#', 125 | [TokenNames.ETH]: '#', 126 | [TokenNames.SLP]: '#', 127 | [TokenNames.XFOLD]: 128 | 'https://etherscan.io/token/0x454BD9E2B29EB5963048cC1A8BD6fD44e89899Cb', 129 | }; 130 | -------------------------------------------------------------------------------- /contracts/ERC20.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "constant": true, 4 | "inputs": [], 5 | "name": "name", 6 | "outputs": [{ "name": "", "type": "string" }], 7 | "payable": false, 8 | "stateMutability": "view", 9 | "type": "function" 10 | }, 11 | { 12 | "constant": false, 13 | "inputs": [ 14 | { "name": "_spender", "type": "address" }, 15 | { "name": "_value", "type": "uint256" } 16 | ], 17 | "name": "approve", 18 | "outputs": [{ "name": "", "type": "bool" }], 19 | "payable": false, 20 | "stateMutability": "nonpayable", 21 | "type": "function" 22 | }, 23 | { 24 | "constant": true, 25 | "inputs": [], 26 | "name": "totalSupply", 27 | "outputs": [{ "name": "", "type": "uint256" }], 28 | "payable": false, 29 | "stateMutability": "view", 30 | "type": "function" 31 | }, 32 | { 33 | "constant": false, 34 | "inputs": [ 35 | { "name": "_from", "type": "address" }, 36 | { "name": "_to", "type": "address" }, 37 | { "name": "_value", "type": "uint256" } 38 | ], 39 | "name": "transferFrom", 40 | "outputs": [{ "name": "", "type": "bool" }], 41 | "payable": false, 42 | "stateMutability": "nonpayable", 43 | "type": "function" 44 | }, 45 | { 46 | "constant": true, 47 | "inputs": [], 48 | "name": "decimals", 49 | "outputs": [{ "name": "", "type": "uint8" }], 50 | "payable": false, 51 | "stateMutability": "view", 52 | "type": "function" 53 | }, 54 | { 55 | "constant": true, 56 | "inputs": [{ "name": "_owner", "type": "address" }], 57 | "name": "balanceOf", 58 | "outputs": [{ "name": "balance", "type": "uint256" }], 59 | "payable": false, 60 | "stateMutability": "view", 61 | "type": "function" 62 | }, 63 | { 64 | "constant": true, 65 | "inputs": [], 66 | "name": "symbol", 67 | "outputs": [{ "name": "", "type": "string" }], 68 | "payable": false, 69 | "stateMutability": "view", 70 | "type": "function" 71 | }, 72 | { 73 | "constant": false, 74 | "inputs": [ 75 | { "name": "_to", "type": "address" }, 76 | { "name": "_value", "type": "uint256" } 77 | ], 78 | "name": "transfer", 79 | "outputs": [{ "name": "", "type": "bool" }], 80 | "payable": false, 81 | "stateMutability": "nonpayable", 82 | "type": "function" 83 | }, 84 | { 85 | "constant": true, 86 | "inputs": [ 87 | { "name": "_owner", "type": "address" }, 88 | { "name": "_spender", "type": "address" } 89 | ], 90 | "name": "allowance", 91 | "outputs": [{ "name": "", "type": "uint256" }], 92 | "payable": false, 93 | "stateMutability": "view", 94 | "type": "function" 95 | }, 96 | { "payable": true, "stateMutability": "payable", "type": "fallback" }, 97 | { 98 | "anonymous": false, 99 | "inputs": [ 100 | { "indexed": true, "name": "owner", "type": "address" }, 101 | { "indexed": true, "name": "spender", "type": "address" }, 102 | { "indexed": false, "name": "value", "type": "uint256" } 103 | ], 104 | "name": "Approval", 105 | "type": "event" 106 | }, 107 | { 108 | "anonymous": false, 109 | "inputs": [ 110 | { "indexed": true, "name": "from", "type": "address" }, 111 | { "indexed": true, "name": "to", "type": "address" }, 112 | { "indexed": false, "name": "value", "type": "uint256" } 113 | ], 114 | "name": "Transfer", 115 | "type": "event" 116 | } 117 | ] 118 | -------------------------------------------------------------------------------- /contracts/FOLD.json: -------------------------------------------------------------------------------- 1 | [{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"},{"internalType":"address","name":"_owner","type":"address"},{"internalType":"uint256","name":"_initialSupply","type":"uint256"}],"name":"getInitData","outputs":[{"internalType":"bytes","name":"_data","type":"bytes"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"init","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"initToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"},{"internalType":"address","name":"_owner","type":"address"},{"internalType":"uint256","name":"_initialSupply","type":"uint256"}],"name":"initToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner_","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokenTemplate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}] -------------------------------------------------------------------------------- /contracts/Staking.json: -------------------------------------------------------------------------------- 1 | [ 2 | { "inputs": [], "stateMutability": "nonpayable", "type": "constructor" }, 3 | { 4 | "anonymous": false, 5 | "inputs": [ 6 | { 7 | "indexed": true, 8 | "internalType": "address", 9 | "name": "user", 10 | "type": "address" 11 | }, 12 | { 13 | "indexed": true, 14 | "internalType": "address", 15 | "name": "tokenAddress", 16 | "type": "address" 17 | }, 18 | { 19 | "indexed": false, 20 | "internalType": "uint256", 21 | "name": "amount", 22 | "type": "uint256" 23 | } 24 | ], 25 | "name": "Deposit", 26 | "type": "event" 27 | }, 28 | { 29 | "anonymous": false, 30 | "inputs": [ 31 | { 32 | "indexed": true, 33 | "internalType": "address", 34 | "name": "user", 35 | "type": "address" 36 | }, 37 | { 38 | "indexed": true, 39 | "internalType": "address", 40 | "name": "tokenAddress", 41 | "type": "address" 42 | }, 43 | { 44 | "indexed": false, 45 | "internalType": "uint256", 46 | "name": "amount", 47 | "type": "uint256" 48 | } 49 | ], 50 | "name": "EmergencyWithdraw", 51 | "type": "event" 52 | }, 53 | { 54 | "anonymous": false, 55 | "inputs": [ 56 | { 57 | "indexed": true, 58 | "internalType": "address", 59 | "name": "caller", 60 | "type": "address" 61 | }, 62 | { 63 | "indexed": true, 64 | "internalType": "uint128", 65 | "name": "epochId", 66 | "type": "uint128" 67 | }, 68 | { 69 | "indexed": false, 70 | "internalType": "address[]", 71 | "name": "tokens", 72 | "type": "address[]" 73 | } 74 | ], 75 | "name": "InitEpochForTokens", 76 | "type": "event" 77 | }, 78 | { 79 | "anonymous": false, 80 | "inputs": [ 81 | { 82 | "indexed": true, 83 | "internalType": "address", 84 | "name": "user", 85 | "type": "address" 86 | }, 87 | { 88 | "indexed": true, 89 | "internalType": "address", 90 | "name": "tokenAddress", 91 | "type": "address" 92 | }, 93 | { 94 | "indexed": false, 95 | "internalType": "uint256", 96 | "name": "amount", 97 | "type": "uint256" 98 | } 99 | ], 100 | "name": "Withdraw", 101 | "type": "event" 102 | }, 103 | { 104 | "inputs": [ 105 | { "internalType": "address", "name": "user", "type": "address" }, 106 | { "internalType": "address", "name": "token", "type": "address" } 107 | ], 108 | "name": "balanceLocked", 109 | "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], 110 | "stateMutability": "view", 111 | "type": "function" 112 | }, 113 | { 114 | "inputs": [ 115 | { "internalType": "uint256", "name": "prevBalance", "type": "uint256" }, 116 | { 117 | "internalType": "uint128", 118 | "name": "prevMultiplier", 119 | "type": "uint128" 120 | }, 121 | { "internalType": "uint256", "name": "amount", "type": "uint256" }, 122 | { 123 | "internalType": "uint128", 124 | "name": "currentMultiplier", 125 | "type": "uint128" 126 | } 127 | ], 128 | "name": "computeNewMultiplier", 129 | "outputs": [{ "internalType": "uint128", "name": "", "type": "uint128" }], 130 | "stateMutability": "pure", 131 | "type": "function" 132 | }, 133 | { 134 | "inputs": [], 135 | "name": "currentEpochMultiplier", 136 | "outputs": [{ "internalType": "uint128", "name": "", "type": "uint128" }], 137 | "stateMutability": "view", 138 | "type": "function" 139 | }, 140 | { 141 | "inputs": [ 142 | { "internalType": "address", "name": "tokenAddress", "type": "address" }, 143 | { "internalType": "uint256", "name": "amount", "type": "uint256" } 144 | ], 145 | "name": "deposit", 146 | "outputs": [], 147 | "stateMutability": "nonpayable", 148 | "type": "function" 149 | }, 150 | { 151 | "inputs": [ 152 | { "internalType": "address", "name": "tokenAddress", "type": "address" } 153 | ], 154 | "name": "emergencyWithdraw", 155 | "outputs": [], 156 | "stateMutability": "nonpayable", 157 | "type": "function" 158 | }, 159 | { 160 | "inputs": [], 161 | "name": "epoch1Start", 162 | "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], 163 | "stateMutability": "view", 164 | "type": "function" 165 | }, 166 | { 167 | "inputs": [], 168 | "name": "epochDuration", 169 | "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], 170 | "stateMutability": "view", 171 | "type": "function" 172 | }, 173 | { 174 | "inputs": [ 175 | { "internalType": "address", "name": "token", "type": "address" }, 176 | { "internalType": "uint128", "name": "epochId", "type": "uint128" } 177 | ], 178 | "name": "epochIsInitialized", 179 | "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], 180 | "stateMutability": "view", 181 | "type": "function" 182 | }, 183 | { 184 | "inputs": [], 185 | "name": "getCurrentEpoch", 186 | "outputs": [{ "internalType": "uint128", "name": "", "type": "uint128" }], 187 | "stateMutability": "view", 188 | "type": "function" 189 | }, 190 | { 191 | "inputs": [ 192 | { "internalType": "address", "name": "tokenAddress", "type": "address" }, 193 | { "internalType": "uint128", "name": "epochId", "type": "uint128" } 194 | ], 195 | "name": "getEpochPoolSize", 196 | "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], 197 | "stateMutability": "view", 198 | "type": "function" 199 | }, 200 | { 201 | "inputs": [ 202 | { "internalType": "address", "name": "user", "type": "address" }, 203 | { "internalType": "address", "name": "token", "type": "address" }, 204 | { "internalType": "uint128", "name": "epochId", "type": "uint128" } 205 | ], 206 | "name": "getEpochUserBalance", 207 | "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], 208 | "stateMutability": "view", 209 | "type": "function" 210 | }, 211 | { 212 | "inputs": [ 213 | { "internalType": "address[]", "name": "tokensLP", "type": "address[]" }, 214 | { "internalType": "uint128", "name": "epochId", "type": "uint128" } 215 | ], 216 | "name": "initEpochForTokens", 217 | "outputs": [], 218 | "stateMutability": "nonpayable", 219 | "type": "function" 220 | }, 221 | { 222 | "inputs": [ 223 | { "internalType": "address", "name": "_epochClock", "type": "address" } 224 | ], 225 | "name": "initialize", 226 | "outputs": [], 227 | "stateMutability": "nonpayable", 228 | "type": "function" 229 | }, 230 | { 231 | "inputs": [ 232 | { "internalType": "address", "name": "tokenAddress", "type": "address" }, 233 | { "internalType": "uint256", "name": "amount", "type": "uint256" } 234 | ], 235 | "name": "withdraw", 236 | "outputs": [], 237 | "stateMutability": "nonpayable", 238 | "type": "function" 239 | } 240 | ] 241 | -------------------------------------------------------------------------------- /contracts/types/common.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | import type { Listener } from '@ethersproject/providers'; 5 | import type { Event, EventFilter } from 'ethers'; 6 | 7 | export interface TypedEvent< 8 | TArgsArray extends Array = any, 9 | TArgsObject = any, 10 | > extends Event { 11 | args: TArgsArray & TArgsObject; 12 | } 13 | 14 | export interface TypedEventFilter<_TEvent extends TypedEvent> 15 | extends EventFilter {} 16 | 17 | export interface TypedListener { 18 | (...listenerArg: [...__TypechainArgsArray, TEvent]): void; 19 | } 20 | 21 | type __TypechainArgsArray = T extends TypedEvent ? U : never; 22 | 23 | export interface OnEvent { 24 | ( 25 | eventFilter: TypedEventFilter, 26 | listener: TypedListener, 27 | ): TRes; 28 | (eventName: string, listener: Listener): TRes; 29 | } 30 | 31 | export type MinEthersFactory = { 32 | deploy(...a: ARGS[]): Promise; 33 | }; 34 | 35 | export type GetContractTypeFromFactory = F extends MinEthersFactory< 36 | infer C, 37 | any 38 | > 39 | ? C 40 | : never; 41 | 42 | export type GetARGsTypeFromFactory = F extends MinEthersFactory 43 | ? Parameters 44 | : never; 45 | -------------------------------------------------------------------------------- /contracts/types/commons.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | 5 | import { EventFilter, Event } from 'ethers'; 6 | import { Result } from '@ethersproject/abi'; 7 | 8 | export interface TypedEventFilter<_EventArgsArray, _EventArgsObject> 9 | extends EventFilter {} 10 | 11 | export interface TypedEvent extends Event { 12 | args: EventArgs; 13 | } 14 | 15 | export type TypedListener< 16 | EventArgsArray extends Array, 17 | EventArgsObject, 18 | > = ( 19 | ...listenerArg: [ 20 | ...EventArgsArray, 21 | TypedEvent, 22 | ] 23 | ) => void; 24 | 25 | export type MinEthersFactory = { 26 | deploy(...a: ARGS[]): Promise; 27 | }; 28 | export type GetContractTypeFromFactory = F extends MinEthersFactory< 29 | infer C, 30 | any 31 | > 32 | ? C 33 | : never; 34 | export type GetARGsTypeFromFactory = F extends MinEthersFactory 35 | ? Parameters 36 | : never; 37 | -------------------------------------------------------------------------------- /contracts/types/factories/ERC20__factory.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | 5 | import { Contract, Signer, utils } from 'ethers'; 6 | import { Provider } from '@ethersproject/providers'; 7 | import type { ERC20, ERC20Interface } from '../ERC20'; 8 | 9 | const _abi = [ 10 | { 11 | constant: true, 12 | inputs: [], 13 | name: 'name', 14 | outputs: [ 15 | { 16 | name: '', 17 | type: 'string', 18 | }, 19 | ], 20 | payable: false, 21 | stateMutability: 'view', 22 | type: 'function', 23 | }, 24 | { 25 | constant: false, 26 | inputs: [ 27 | { 28 | name: '_spender', 29 | type: 'address', 30 | }, 31 | { 32 | name: '_value', 33 | type: 'uint256', 34 | }, 35 | ], 36 | name: 'approve', 37 | outputs: [ 38 | { 39 | name: '', 40 | type: 'bool', 41 | }, 42 | ], 43 | payable: false, 44 | stateMutability: 'nonpayable', 45 | type: 'function', 46 | }, 47 | { 48 | constant: true, 49 | inputs: [], 50 | name: 'totalSupply', 51 | outputs: [ 52 | { 53 | name: '', 54 | type: 'uint256', 55 | }, 56 | ], 57 | payable: false, 58 | stateMutability: 'view', 59 | type: 'function', 60 | }, 61 | { 62 | constant: false, 63 | inputs: [ 64 | { 65 | name: '_from', 66 | type: 'address', 67 | }, 68 | { 69 | name: '_to', 70 | type: 'address', 71 | }, 72 | { 73 | name: '_value', 74 | type: 'uint256', 75 | }, 76 | ], 77 | name: 'transferFrom', 78 | outputs: [ 79 | { 80 | name: '', 81 | type: 'bool', 82 | }, 83 | ], 84 | payable: false, 85 | stateMutability: 'nonpayable', 86 | type: 'function', 87 | }, 88 | { 89 | constant: true, 90 | inputs: [], 91 | name: 'decimals', 92 | outputs: [ 93 | { 94 | name: '', 95 | type: 'uint8', 96 | }, 97 | ], 98 | payable: false, 99 | stateMutability: 'view', 100 | type: 'function', 101 | }, 102 | { 103 | constant: true, 104 | inputs: [ 105 | { 106 | name: '_owner', 107 | type: 'address', 108 | }, 109 | ], 110 | name: 'balanceOf', 111 | outputs: [ 112 | { 113 | name: 'balance', 114 | type: 'uint256', 115 | }, 116 | ], 117 | payable: false, 118 | stateMutability: 'view', 119 | type: 'function', 120 | }, 121 | { 122 | constant: true, 123 | inputs: [], 124 | name: 'symbol', 125 | outputs: [ 126 | { 127 | name: '', 128 | type: 'string', 129 | }, 130 | ], 131 | payable: false, 132 | stateMutability: 'view', 133 | type: 'function', 134 | }, 135 | { 136 | constant: false, 137 | inputs: [ 138 | { 139 | name: '_to', 140 | type: 'address', 141 | }, 142 | { 143 | name: '_value', 144 | type: 'uint256', 145 | }, 146 | ], 147 | name: 'transfer', 148 | outputs: [ 149 | { 150 | name: '', 151 | type: 'bool', 152 | }, 153 | ], 154 | payable: false, 155 | stateMutability: 'nonpayable', 156 | type: 'function', 157 | }, 158 | { 159 | constant: true, 160 | inputs: [ 161 | { 162 | name: '_owner', 163 | type: 'address', 164 | }, 165 | { 166 | name: '_spender', 167 | type: 'address', 168 | }, 169 | ], 170 | name: 'allowance', 171 | outputs: [ 172 | { 173 | name: '', 174 | type: 'uint256', 175 | }, 176 | ], 177 | payable: false, 178 | stateMutability: 'view', 179 | type: 'function', 180 | }, 181 | { 182 | payable: true, 183 | stateMutability: 'payable', 184 | type: 'fallback', 185 | }, 186 | { 187 | anonymous: false, 188 | inputs: [ 189 | { 190 | indexed: true, 191 | name: 'owner', 192 | type: 'address', 193 | }, 194 | { 195 | indexed: true, 196 | name: 'spender', 197 | type: 'address', 198 | }, 199 | { 200 | indexed: false, 201 | name: 'value', 202 | type: 'uint256', 203 | }, 204 | ], 205 | name: 'Approval', 206 | type: 'event', 207 | }, 208 | { 209 | anonymous: false, 210 | inputs: [ 211 | { 212 | indexed: true, 213 | name: 'from', 214 | type: 'address', 215 | }, 216 | { 217 | indexed: true, 218 | name: 'to', 219 | type: 'address', 220 | }, 221 | { 222 | indexed: false, 223 | name: 'value', 224 | type: 'uint256', 225 | }, 226 | ], 227 | name: 'Transfer', 228 | type: 'event', 229 | }, 230 | ]; 231 | 232 | export class ERC20__factory { 233 | static readonly abi = _abi; 234 | static createInterface(): ERC20Interface { 235 | return new utils.Interface(_abi) as ERC20Interface; 236 | } 237 | static connect(address: string, signerOrProvider: Signer | Provider): ERC20 { 238 | return new Contract(address, _abi, signerOrProvider) as ERC20; 239 | } 240 | } 241 | -------------------------------------------------------------------------------- /contracts/types/factories/FOLD__factory.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | 5 | import { Contract, Signer, utils } from 'ethers'; 6 | import { Provider } from '@ethersproject/providers'; 7 | import type { FOLD, FOLDInterface } from '../FOLD'; 8 | 9 | const _abi = [ 10 | { 11 | anonymous: false, 12 | inputs: [ 13 | { 14 | indexed: true, 15 | internalType: 'address', 16 | name: 'owner', 17 | type: 'address', 18 | }, 19 | { 20 | indexed: true, 21 | internalType: 'address', 22 | name: 'spender', 23 | type: 'address', 24 | }, 25 | { 26 | indexed: false, 27 | internalType: 'uint256', 28 | name: 'value', 29 | type: 'uint256', 30 | }, 31 | ], 32 | name: 'Approval', 33 | type: 'event', 34 | }, 35 | { 36 | anonymous: false, 37 | inputs: [ 38 | { 39 | indexed: true, 40 | internalType: 'address', 41 | name: 'from', 42 | type: 'address', 43 | }, 44 | { 45 | indexed: true, 46 | internalType: 'address', 47 | name: 'to', 48 | type: 'address', 49 | }, 50 | { 51 | indexed: false, 52 | internalType: 'uint256', 53 | name: 'value', 54 | type: 'uint256', 55 | }, 56 | ], 57 | name: 'Transfer', 58 | type: 'event', 59 | }, 60 | { 61 | inputs: [], 62 | name: 'DOMAIN_SEPARATOR', 63 | outputs: [ 64 | { 65 | internalType: 'bytes32', 66 | name: '', 67 | type: 'bytes32', 68 | }, 69 | ], 70 | stateMutability: 'view', 71 | type: 'function', 72 | }, 73 | { 74 | inputs: [ 75 | { 76 | internalType: 'address', 77 | name: 'owner', 78 | type: 'address', 79 | }, 80 | { 81 | internalType: 'address', 82 | name: 'spender', 83 | type: 'address', 84 | }, 85 | ], 86 | name: 'allowance', 87 | outputs: [ 88 | { 89 | internalType: 'uint256', 90 | name: '', 91 | type: 'uint256', 92 | }, 93 | ], 94 | stateMutability: 'view', 95 | type: 'function', 96 | }, 97 | { 98 | inputs: [ 99 | { 100 | internalType: 'address', 101 | name: 'spender', 102 | type: 'address', 103 | }, 104 | { 105 | internalType: 'uint256', 106 | name: 'amount', 107 | type: 'uint256', 108 | }, 109 | ], 110 | name: 'approve', 111 | outputs: [ 112 | { 113 | internalType: 'bool', 114 | name: '', 115 | type: 'bool', 116 | }, 117 | ], 118 | stateMutability: 'nonpayable', 119 | type: 'function', 120 | }, 121 | { 122 | inputs: [ 123 | { 124 | internalType: 'address', 125 | name: 'account', 126 | type: 'address', 127 | }, 128 | ], 129 | name: 'balanceOf', 130 | outputs: [ 131 | { 132 | internalType: 'uint256', 133 | name: '', 134 | type: 'uint256', 135 | }, 136 | ], 137 | stateMutability: 'view', 138 | type: 'function', 139 | }, 140 | { 141 | inputs: [], 142 | name: 'decimals', 143 | outputs: [ 144 | { 145 | internalType: 'uint8', 146 | name: '', 147 | type: 'uint8', 148 | }, 149 | ], 150 | stateMutability: 'view', 151 | type: 'function', 152 | }, 153 | { 154 | inputs: [ 155 | { 156 | internalType: 'address', 157 | name: 'spender', 158 | type: 'address', 159 | }, 160 | { 161 | internalType: 'uint256', 162 | name: 'subtractedValue', 163 | type: 'uint256', 164 | }, 165 | ], 166 | name: 'decreaseAllowance', 167 | outputs: [ 168 | { 169 | internalType: 'bool', 170 | name: '', 171 | type: 'bool', 172 | }, 173 | ], 174 | stateMutability: 'nonpayable', 175 | type: 'function', 176 | }, 177 | { 178 | inputs: [ 179 | { 180 | internalType: 'string', 181 | name: '_name', 182 | type: 'string', 183 | }, 184 | { 185 | internalType: 'string', 186 | name: '_symbol', 187 | type: 'string', 188 | }, 189 | { 190 | internalType: 'address', 191 | name: '_owner', 192 | type: 'address', 193 | }, 194 | { 195 | internalType: 'uint256', 196 | name: '_initialSupply', 197 | type: 'uint256', 198 | }, 199 | ], 200 | name: 'getInitData', 201 | outputs: [ 202 | { 203 | internalType: 'bytes', 204 | name: '_data', 205 | type: 'bytes', 206 | }, 207 | ], 208 | stateMutability: 'pure', 209 | type: 'function', 210 | }, 211 | { 212 | inputs: [ 213 | { 214 | internalType: 'address', 215 | name: 'spender', 216 | type: 'address', 217 | }, 218 | { 219 | internalType: 'uint256', 220 | name: 'addedValue', 221 | type: 'uint256', 222 | }, 223 | ], 224 | name: 'increaseAllowance', 225 | outputs: [ 226 | { 227 | internalType: 'bool', 228 | name: '', 229 | type: 'bool', 230 | }, 231 | ], 232 | stateMutability: 'nonpayable', 233 | type: 'function', 234 | }, 235 | { 236 | inputs: [ 237 | { 238 | internalType: 'bytes', 239 | name: '_data', 240 | type: 'bytes', 241 | }, 242 | ], 243 | name: 'init', 244 | outputs: [], 245 | stateMutability: 'payable', 246 | type: 'function', 247 | }, 248 | { 249 | inputs: [ 250 | { 251 | internalType: 'bytes', 252 | name: '_data', 253 | type: 'bytes', 254 | }, 255 | ], 256 | name: 'initToken', 257 | outputs: [], 258 | stateMutability: 'nonpayable', 259 | type: 'function', 260 | }, 261 | { 262 | inputs: [ 263 | { 264 | internalType: 'string', 265 | name: '_name', 266 | type: 'string', 267 | }, 268 | { 269 | internalType: 'string', 270 | name: '_symbol', 271 | type: 'string', 272 | }, 273 | { 274 | internalType: 'address', 275 | name: '_owner', 276 | type: 'address', 277 | }, 278 | { 279 | internalType: 'uint256', 280 | name: '_initialSupply', 281 | type: 'uint256', 282 | }, 283 | ], 284 | name: 'initToken', 285 | outputs: [], 286 | stateMutability: 'nonpayable', 287 | type: 'function', 288 | }, 289 | { 290 | inputs: [], 291 | name: 'name', 292 | outputs: [ 293 | { 294 | internalType: 'string', 295 | name: '', 296 | type: 'string', 297 | }, 298 | ], 299 | stateMutability: 'view', 300 | type: 'function', 301 | }, 302 | { 303 | inputs: [ 304 | { 305 | internalType: 'address', 306 | name: '', 307 | type: 'address', 308 | }, 309 | ], 310 | name: 'nonces', 311 | outputs: [ 312 | { 313 | internalType: 'uint256', 314 | name: '', 315 | type: 'uint256', 316 | }, 317 | ], 318 | stateMutability: 'view', 319 | type: 'function', 320 | }, 321 | { 322 | inputs: [ 323 | { 324 | internalType: 'address', 325 | name: 'owner_', 326 | type: 'address', 327 | }, 328 | { 329 | internalType: 'address', 330 | name: 'spender', 331 | type: 'address', 332 | }, 333 | { 334 | internalType: 'uint256', 335 | name: 'value', 336 | type: 'uint256', 337 | }, 338 | { 339 | internalType: 'uint256', 340 | name: 'deadline', 341 | type: 'uint256', 342 | }, 343 | { 344 | internalType: 'uint8', 345 | name: 'v', 346 | type: 'uint8', 347 | }, 348 | { 349 | internalType: 'bytes32', 350 | name: 'r', 351 | type: 'bytes32', 352 | }, 353 | { 354 | internalType: 'bytes32', 355 | name: 's', 356 | type: 'bytes32', 357 | }, 358 | ], 359 | name: 'permit', 360 | outputs: [], 361 | stateMutability: 'nonpayable', 362 | type: 'function', 363 | }, 364 | { 365 | inputs: [], 366 | name: 'symbol', 367 | outputs: [ 368 | { 369 | internalType: 'string', 370 | name: '', 371 | type: 'string', 372 | }, 373 | ], 374 | stateMutability: 'view', 375 | type: 'function', 376 | }, 377 | { 378 | inputs: [], 379 | name: 'tokenTemplate', 380 | outputs: [ 381 | { 382 | internalType: 'uint256', 383 | name: '', 384 | type: 'uint256', 385 | }, 386 | ], 387 | stateMutability: 'view', 388 | type: 'function', 389 | }, 390 | { 391 | inputs: [], 392 | name: 'totalSupply', 393 | outputs: [ 394 | { 395 | internalType: 'uint256', 396 | name: '', 397 | type: 'uint256', 398 | }, 399 | ], 400 | stateMutability: 'view', 401 | type: 'function', 402 | }, 403 | { 404 | inputs: [ 405 | { 406 | internalType: 'address', 407 | name: 'recipient', 408 | type: 'address', 409 | }, 410 | { 411 | internalType: 'uint256', 412 | name: 'amount', 413 | type: 'uint256', 414 | }, 415 | ], 416 | name: 'transfer', 417 | outputs: [ 418 | { 419 | internalType: 'bool', 420 | name: '', 421 | type: 'bool', 422 | }, 423 | ], 424 | stateMutability: 'nonpayable', 425 | type: 'function', 426 | }, 427 | { 428 | inputs: [ 429 | { 430 | internalType: 'address', 431 | name: 'sender', 432 | type: 'address', 433 | }, 434 | { 435 | internalType: 'address', 436 | name: 'recipient', 437 | type: 'address', 438 | }, 439 | { 440 | internalType: 'uint256', 441 | name: 'amount', 442 | type: 'uint256', 443 | }, 444 | ], 445 | name: 'transferFrom', 446 | outputs: [ 447 | { 448 | internalType: 'bool', 449 | name: '', 450 | type: 'bool', 451 | }, 452 | ], 453 | stateMutability: 'nonpayable', 454 | type: 'function', 455 | }, 456 | ]; 457 | 458 | export class FOLD__factory { 459 | static readonly abi = _abi; 460 | static createInterface(): FOLDInterface { 461 | return new utils.Interface(_abi) as FOLDInterface; 462 | } 463 | static connect(address: string, signerOrProvider: Signer | Provider): FOLD { 464 | return new Contract(address, _abi, signerOrProvider) as FOLD; 465 | } 466 | } 467 | -------------------------------------------------------------------------------- /contracts/types/index.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | export type { DOMODAO } from './DOMODAO'; 5 | export type { ERC20 } from './ERC20'; 6 | export type { FOLD } from './FOLD'; 7 | 8 | export { DOMODAO__factory } from './factories/DOMODAO__factory'; 9 | export { ERC20__factory } from './factories/ERC20__factory'; 10 | export { FOLD__factory } from './factories/FOLD__factory'; 11 | -------------------------------------------------------------------------------- /data/qa.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "question": "What is the xFOLD?", 4 | "answer": "" 5 | }, 6 | { 7 | "question": "When do tributes pay out?", 8 | "answer": "" 9 | } 10 | ] 11 | -------------------------------------------------------------------------------- /hooks/getSigner.ts: -------------------------------------------------------------------------------- 1 | import { JsonRpcSigner, Web3Provider } from '@ethersproject/providers'; 2 | import { AddressZero } from '@ethersproject/constants'; 3 | import { Contract } from '@ethersproject/contracts'; 4 | import { isAddress } from './isAddress'; 5 | 6 | // account is not optional 7 | export function getSigner( 8 | library: Web3Provider, 9 | account: string, 10 | ): JsonRpcSigner { 11 | return library.getSigner(account).connectUnchecked(); 12 | } 13 | 14 | // account is optional 15 | export function getProviderOrSigner( 16 | library: Web3Provider, 17 | account?: string, 18 | ): Web3Provider | JsonRpcSigner { 19 | return account ? getSigner(library, account) : library; 20 | } 21 | 22 | // account is optional 23 | export function getContract( 24 | address: string, 25 | ABI: any, 26 | library: Web3Provider, 27 | account?: string, 28 | ): Contract { 29 | if (!isAddress(address) || address === AddressZero) { 30 | throw Error(`Invalid 'address' parameter '${address}'.`); 31 | } 32 | 33 | return new Contract( 34 | address, 35 | ABI, 36 | getProviderOrSigner(library, account) as any, 37 | ); 38 | } 39 | -------------------------------------------------------------------------------- /hooks/isAddress.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from 'ethers'; 2 | import { getAddress } from '@ethersproject/address'; 3 | 4 | /** 5 | * Returns true if the string value is zero in hex 6 | * @param hexNumberString 7 | */ 8 | export function isZero(hexNumberString: string): boolean { 9 | return /^0x0*$/.test(hexNumberString); 10 | } 11 | 12 | export const isEmptyValue = (text: string) => 13 | BigNumber.isBigNumber(text) 14 | ? BigNumber.from(text).isZero() 15 | : text === '' || text.replace(/0/g, '').replace(/\./, '') === ''; 16 | 17 | // returns the checksummed address if the address is valid, otherwise returns false 18 | export function isAddress(value: any): string | false { 19 | try { 20 | return getAddress(value); 21 | } catch { 22 | return false; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /hooks/useActiveWeb3React.ts: -------------------------------------------------------------------------------- 1 | import { ChainId } from '@sushiswap/sdk'; 2 | import { Web3Provider } from '@ethersproject/providers'; 3 | import { Web3ReactContextInterface } from '@web3-react/core/dist/types'; 4 | import { useWeb3React as useWeb3ReactCore } from '@web3-react/core'; 5 | 6 | export const NetworkContextName = 'NETWORK'; 7 | 8 | export function useActiveWeb3React(): Web3ReactContextInterface & { 9 | chainId?: ChainId; 10 | } { 11 | // replace with address to impersonate 12 | const impersonate = false; 13 | const context = useWeb3ReactCore(); 14 | const contextNetwork = useWeb3ReactCore(NetworkContextName); 15 | return context.active 16 | ? { ...context, account: impersonate || context.account } 17 | : { ...contextNetwork, account: impersonate || contextNetwork.account }; 18 | } 19 | 20 | export default useActiveWeb3React; 21 | -------------------------------------------------------------------------------- /hooks/useAddTokenToMetaMask.ts: -------------------------------------------------------------------------------- 1 | import { TOKEN_ASSETS } from '@/constants/tokens'; 2 | import { useCallback, useState } from 'react'; 3 | import useWeb3Store from './useWeb3Store'; 4 | 5 | export default function useAddTokenToMetaMask(): { 6 | addToken: () => void; 7 | success: boolean | undefined; 8 | } { 9 | const chainId = useWeb3Store((state) => state.chainId); 10 | const library = useWeb3Store((state) => state.library); 11 | 12 | const [success, setSuccess] = useState(); 13 | 14 | const addToken = useCallback(async () => { 15 | try { 16 | if (library && library.provider.isMetaMask && library.provider.request) { 17 | const XFOLD = TOKEN_ASSETS.xFOLD[chainId]; 18 | 19 | const FOLD = TOKEN_ASSETS.FOLD[chainId]; 20 | 21 | await library.provider.request({ 22 | method: 'wallet_watchAsset', 23 | params: { 24 | //@ts-ignore 25 | type: 'ERC20', 26 | options: { 27 | address: XFOLD.address, 28 | symbol: XFOLD.symbol, 29 | decimals: XFOLD.decimals, 30 | }, 31 | }, 32 | }); 33 | 34 | await library.provider.request({ 35 | method: 'wallet_watchAsset', 36 | params: { 37 | //@ts-ignore 38 | type: 'ERC20', 39 | options: { 40 | address: FOLD.address, 41 | symbol: FOLD.symbol, 42 | decimals: FOLD.decimals, 43 | }, 44 | }, 45 | }); 46 | 47 | setSuccess(true); 48 | } else { 49 | setSuccess(false); 50 | } 51 | } catch (error) { 52 | console.error(error); 53 | 54 | setSuccess(false); 55 | } 56 | }, [library, chainId]); 57 | 58 | return { 59 | addToken, 60 | success, 61 | }; 62 | } 63 | -------------------------------------------------------------------------------- /hooks/useBestBuy.ts: -------------------------------------------------------------------------------- 1 | import useSWR from 'swr'; 2 | 3 | const BASE = ``; 4 | 5 | function getBestBuy() { 6 | type Data = Record; 7 | 8 | return async (): Promise => { 9 | const r = await fetch(BASE, { 10 | headers: { 11 | Accept: 'application/json', 12 | }, 13 | }); 14 | 15 | if (r.ok) { 16 | return r.json(); 17 | } 18 | 19 | throw new Error(r.status + '' + r.statusText); 20 | }; 21 | } 22 | 23 | export default function useBestBuy() { 24 | const shouldFetch = true; 25 | 26 | return useSWR(shouldFetch ? ['BestBuy'] : null, getBestBuy(), { 27 | shouldRetryOnError: false, 28 | }); 29 | } 30 | -------------------------------------------------------------------------------- /hooks/useBlockNumber.ts: -------------------------------------------------------------------------------- 1 | import type { Web3Provider } from '@ethersproject/providers'; 2 | import useSWR from 'swr'; 3 | import useWeb3Store from './useWeb3Store'; 4 | 5 | function getBlockNumber(library: Web3Provider) { 6 | return async () => { 7 | return library.getBlockNumber(); 8 | }; 9 | } 10 | 11 | export default function useBlockNumber() { 12 | const library = useWeb3Store((state) => state.library); 13 | 14 | const shouldFetch = !!library; 15 | 16 | return useSWR(shouldFetch ? ['BlockNumber'] : null, getBlockNumber(library), { 17 | refreshInterval: 10 * 1000, 18 | }); 19 | } 20 | -------------------------------------------------------------------------------- /hooks/useContract.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | DOMODAO, 3 | DOMODAO as DictatorDAO, 4 | ERC20, 5 | FOLD, 6 | } from '@/contracts/types'; 7 | import useWeb3Store, { State } from './useWeb3Store'; 8 | 9 | import { CONTRACT_ADDRESSES } from '@/constants/contracts'; 10 | import { Contract } from 'ethers'; 11 | import DOMODAO_ABI from '@/contracts/DictatorDAO.json'; 12 | import ERC20_ABI from '@/contracts/ERC20.json'; 13 | import FOLD_ABI from '@/contracts/FOLD.json'; 14 | import STAKING_ABI from '@/contracts/Staking.json'; 15 | import { useMemo } from 'react'; 16 | import { AddressZero } from '@ethersproject/constants'; 17 | 18 | const chainIdSelector = (state: State) => state.chainId; 19 | const accountSelector = (state: State) => state.account; 20 | const librarySelector = (state: State) => state.library; 21 | 22 | export default function useContract( 23 | address: string, 24 | ABI: any, 25 | ): T | null { 26 | const account = useWeb3Store(accountSelector); 27 | const library = useWeb3Store(librarySelector); 28 | 29 | return useMemo(() => { 30 | if (!address || address === AddressZero || !ABI || !library) return null; 31 | try { 32 | return new Contract(address, ABI, library.getSigner(account)); 33 | } catch (error) { 34 | console.error('Failed To Get Contract', error); 35 | 36 | return null; 37 | } 38 | }, [address, ABI, library, account]) as T; 39 | } 40 | 41 | export function useTokenContract(tokenAddress?: string) { 42 | return useContract(tokenAddress, ERC20_ABI); 43 | } 44 | 45 | export function useOperatorAddress() { 46 | const chainId = useWeb3Store(chainIdSelector); 47 | 48 | return useContract( 49 | CONTRACT_ADDRESSES.Operator[chainId], 50 | DOMODAO_ABI, 51 | ); 52 | } 53 | 54 | export function useFoldToken() { 55 | const chainId = useWeb3Store(chainIdSelector); 56 | 57 | return useContract(CONTRACT_ADDRESSES.FOLD[chainId], FOLD_ABI); 58 | } 59 | 60 | export function useDictatorDAO() { 61 | const chainId = useWeb3Store(chainIdSelector); 62 | 63 | return useContract( 64 | CONTRACT_ADDRESSES.DictatorDAO[chainId], 65 | DOMODAO_ABI, 66 | ); 67 | } 68 | 69 | // TODO - useFutureDate, getBalanceOf = calculate user 70 | export function useFOLDUSDCRewards() { 71 | const chainId = useWeb3Store(chainIdSelector); 72 | 73 | return useContract(CONTRACT_ADDRESSES.FOLD[chainId], FOLD_ABI); 74 | } 75 | 76 | // TODO - getBalanceOf 77 | export function useStaking() { 78 | const chainId = useWeb3Store(chainIdSelector); 79 | 80 | return useContract(CONTRACT_ADDRESSES.FOLD[chainId], FOLD_ABI); 81 | } 82 | 83 | export function useStakingContract() { 84 | const chainId = useWeb3Store(chainIdSelector); 85 | 86 | return useContract(CONTRACT_ADDRESSES.Staking[chainId], STAKING_ABI); 87 | } 88 | -------------------------------------------------------------------------------- /hooks/useENSName.ts: -------------------------------------------------------------------------------- 1 | import type { Web3Provider } from '@ethersproject/providers'; 2 | import useSWR from 'swr'; 3 | import useWeb3Store from './useWeb3Store'; 4 | 5 | function getENSName(library: Web3Provider) { 6 | return async (_: string, account: string) => { 7 | const lookupAddress = await library.lookupAddress(account); 8 | 9 | return lookupAddress; 10 | }; 11 | } 12 | 13 | export default function useENSName(account: string) { 14 | const library = useWeb3Store((state) => state.library); 15 | 16 | const shouldFetch = !!library && typeof account === 'string'; 17 | 18 | const result = useSWR( 19 | shouldFetch ? ['ENSName', account] : null, 20 | getENSName(library), 21 | ); 22 | 23 | return result?.data; 24 | } 25 | -------------------------------------------------------------------------------- /hooks/useEagerConnect.ts: -------------------------------------------------------------------------------- 1 | import { injected } from '@/lib/connectors/metamask'; 2 | import isMobile from '@/utils/isMobile'; 3 | import { useEffect, useState } from 'react'; 4 | import useWeb3Store, { State } from './useWeb3Store'; 5 | 6 | const selector = (state: State) => state.active; 7 | 8 | export function useEagerConnect() { 9 | const active = useWeb3Store(selector); 10 | 11 | const [tried, setTried] = useState(false); 12 | 13 | useEffect(() => { 14 | if (!active) { 15 | injected.isAuthorized().then((isAuthorized) => { 16 | if (isAuthorized) { 17 | injected.activate().catch(() => { 18 | setTried(true); 19 | }); 20 | } else { 21 | if (isMobile && window.ethereum) { 22 | injected.activate().catch(() => { 23 | setTried(true); 24 | }); 25 | } else { 26 | setTried(true); 27 | } 28 | } 29 | }); 30 | } 31 | }, [active]); 32 | 33 | useEffect(() => { 34 | if (active) { 35 | setTried(true); 36 | } 37 | }, [active]); 38 | 39 | return tried; 40 | } 41 | -------------------------------------------------------------------------------- /hooks/useFathom.ts: -------------------------------------------------------------------------------- 1 | import * as Fathom from 'fathom-client'; 2 | import { useRouter } from 'next/router'; 3 | import { useEffect } from 'react'; 4 | 5 | const SITE_ID = 'BDJWGKKF'; 6 | 7 | export default function useFathom() { 8 | const router = useRouter(); 9 | 10 | useEffect(() => { 11 | Fathom.load(SITE_ID, { 12 | includedDomains: ['app.manifold.finance'], 13 | }); 14 | 15 | function onRouteChangeComplete() { 16 | Fathom.trackPageview(); 17 | } 18 | 19 | router.events.on('routeChangeComplete', onRouteChangeComplete); 20 | 21 | return () => { 22 | router.events.off('routeChangeComplete', onRouteChangeComplete); 23 | }; 24 | }, []); 25 | } 26 | -------------------------------------------------------------------------------- /hooks/useFoldAmount.ts: -------------------------------------------------------------------------------- 1 | import type { FOLD } from '@/contracts/types'; 2 | import useSWR from 'swr'; 3 | import { useFoldToken } from './useContract'; 4 | import { BigNumberish } from 'ethers'; 5 | 6 | const useFoldAmount = 7 | (contract: FOLD) => 8 | async ( 9 | _: string, 10 | value: BigNumberish, 11 | spender: 0x454bd9e2b29eb5963048cc1a8bd6fd44e89899cb, 12 | ) => { 13 | const amount = await contract.amount(value, spender); 14 | 15 | return amount; 16 | }; 17 | 18 | export default function useFoldPermit( 19 | tokenAddress: 0xd084944d3c05cd115c09d072b9f44ba3e0e45921, 20 | value: BigNumberish, 21 | spender: 0x454bd9e2b29eb5963048cc1a8bd6fd44e89899cb, 22 | ) { 23 | const contract = useFoldToken(); 24 | 25 | const shouldFetch = 26 | !!contract && 27 | typeof value === 'string' && 28 | typeof tokenAddress === 'string' && 29 | typeof spender === 'string'; 30 | 31 | return useSWR( 32 | shouldFetch ? ['FoldAmount', tokenAddress, value, spender] : null, 33 | useFoldAmount(contract), 34 | ); 35 | } 36 | -------------------------------------------------------------------------------- /hooks/useFormattedBigNumber.ts: -------------------------------------------------------------------------------- 1 | import type { BigNumber } from '@ethersproject/bignumber'; 2 | import { commify, formatUnits } from '@ethersproject/units'; 3 | import { useMemo } from 'react'; 4 | 5 | export default function useFormattedBigNumber(value: BigNumber, decimals = 2) { 6 | return useMemo(() => { 7 | if (value) { 8 | return commify(Number(formatUnits(value)).toFixed(decimals)); 9 | } 10 | 11 | return Number(0).toFixed(decimals); 12 | }, [value, decimals]); 13 | } 14 | -------------------------------------------------------------------------------- /hooks/useGetFOLDAmountOut.ts: -------------------------------------------------------------------------------- 1 | import type { FOLD } from '@/contracts/types'; 2 | import { parseUnits } from '@ethersproject/units'; 3 | import useSWR from 'swr'; 4 | import { useFoldToken } from './useContract'; 5 | 6 | function getFoldAmountOut(contract: FOLD) { 7 | return async (_: string, depositAmount: string, depositToken: string) => { 8 | const getFoldAmountOutSingle = await contract.getFoldAmountOutSingle( 9 | depositToken, 10 | parseUnits(depositAmount), 11 | 1, 12 | ); 13 | 14 | return getFoldAmountOutSingle; 15 | }; 16 | } 17 | 18 | export default function useGetFoldAmountOut( 19 | depositToken: string, 20 | depositAmount: string, 21 | ) { 22 | const contract = useFoldToken(); 23 | 24 | const shouldFetch = 25 | !!contract && 26 | typeof depositAmount === 'string' && 27 | typeof depositToken === 'string'; 28 | 29 | return useSWR( 30 | shouldFetch ? ['GetFoldAmountOut', depositAmount, depositToken] : null, 31 | getFoldAmountOut(contract), 32 | { 33 | shouldRetryOnError: false, 34 | }, 35 | ); 36 | } 37 | -------------------------------------------------------------------------------- /hooks/useInput.ts: -------------------------------------------------------------------------------- 1 | import { useCallback, useMemo, useState } from 'react'; 2 | 3 | export function useInput(initial: string | number | boolean = '') { 4 | const stringified = initial.toString(); 5 | const [value, setValue] = useState(stringified); 6 | const onChange = useCallback((e) => setValue(e.target.value), []); 7 | 8 | const clear = useCallback(() => setValue(''), []); 9 | 10 | return useMemo( 11 | () => ({ 12 | value, 13 | setValue, 14 | hasValue: value !== undefined && value !== null && value.trim() !== '', 15 | clear, 16 | onChange, 17 | eventBind: { 18 | onChange, 19 | value, 20 | }, 21 | valueBind: { 22 | onChange: setValue, 23 | value, 24 | }, 25 | }), 26 | [clear, onChange, value], 27 | ); 28 | } 29 | 30 | export default useInput; 31 | -------------------------------------------------------------------------------- /hooks/useKeepSWRDataLiveAsBlocksArrive.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef } from 'react'; 2 | import useBlockNumber from './useBlockNumber'; 3 | 4 | export default function useKeepSWRDataLiveAsBlocksArrive( 5 | mutate: () => Promise, 6 | ) { 7 | // because we don't care about the referential identity of mutate, just bind it to a ref 8 | const mutateRef = useRef(mutate); 9 | 10 | useEffect(() => { 11 | mutateRef.current = mutate; 12 | }); 13 | 14 | // then, whenever a new block arrives, trigger a mutation 15 | const { data } = useBlockNumber(); 16 | 17 | useEffect(() => { 18 | mutateRef.current(); 19 | }, [data]); 20 | } 21 | -------------------------------------------------------------------------------- /hooks/useMetaMaskOnboarding.ts: -------------------------------------------------------------------------------- 1 | import detectEthereumProvider from '@metamask/detect-provider'; 2 | import { useEffect, useState } from 'react'; 3 | 4 | export default function useMetaMaskOnboarding() { 5 | const [isMetaMaskInstalled, isMetaMaskInstalledSet] = useState(); 6 | 7 | useEffect(() => { 8 | if (typeof window === 'undefined') { 9 | return; 10 | } 11 | 12 | async function checkForMetaMask() { 13 | const provider = await detectEthereumProvider({ 14 | timeout: 1000, 15 | mustBeMetaMask: true, 16 | }); 17 | 18 | if (provider) { 19 | isMetaMaskInstalledSet(true); 20 | } else { 21 | isMetaMaskInstalledSet(false); 22 | } 23 | } 24 | 25 | checkForMetaMask(); 26 | }, []); 27 | 28 | async function startOnboarding() { 29 | const MetaMaskOnboarding = await import('@metamask/onboarding').then( 30 | (m) => m.default, 31 | ); 32 | 33 | const onboarding = new MetaMaskOnboarding(); 34 | 35 | onboarding.startOnboarding(); 36 | } 37 | 38 | const isWeb3Available = typeof window !== 'undefined' && window?.ethereum; 39 | 40 | return { 41 | startOnboarding, 42 | isMetaMaskInstalled, 43 | isWeb3Available, 44 | }; 45 | } 46 | -------------------------------------------------------------------------------- /hooks/useTimer.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | 3 | const convertTimeDiff = (diff: number) => ({ 4 | dd: Math.floor(diff / (1000 * 60 * 60 * 24)), 5 | hh: Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)), 6 | mm: Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60)), 7 | ss: Math.floor((diff % (1000 * 60)) / 1000), 8 | }); 9 | 10 | export default function useTimer(timestamp?: number) { 11 | const [timer, setTimer] = useState<{ 12 | dd: number; 13 | hh: number; 14 | mm: number; 15 | ss: number; 16 | }>(); 17 | 18 | useEffect(() => { 19 | if (typeof timestamp === 'undefined') { 20 | return; 21 | } 22 | 23 | const intervalId = setInterval(() => { 24 | setTimer(convertTimeDiff(timestamp - Date.now())); 25 | }, 1000); 26 | 27 | setTimer(convertTimeDiff(timestamp - Date.now())); 28 | 29 | return () => clearInterval(intervalId); 30 | }, [timestamp]); 31 | 32 | return timer; 33 | } 34 | -------------------------------------------------------------------------------- /hooks/useTotalSupply.ts: -------------------------------------------------------------------------------- 1 | import { ERC20 } from '@/contracts/types'; 2 | import useSWR from 'swr'; 3 | import { useTokenContract } from './useContract'; 4 | 5 | function getTotalSupply(contract: ERC20) { 6 | return async () => { 7 | const totalSupply = await contract.totalSupply(); 8 | 9 | return totalSupply; 10 | }; 11 | } 12 | 13 | export default function useTotalSupply(tokenAddress: string) { 14 | const contract = useTokenContract(tokenAddress); 15 | 16 | const shouldFetch = !!contract; 17 | 18 | return useSWR( 19 | shouldFetch ? ['TotalSupply', tokenAddress] : null, 20 | getTotalSupply(contract), 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /hooks/useWalletModal.ts: -------------------------------------------------------------------------------- 1 | import create from 'zustand'; 2 | 3 | export type State = { 4 | isOpen: boolean; 5 | open: () => void; 6 | close: () => void; 7 | toggle: () => void; 8 | }; 9 | 10 | const useWalletModal = create((set, get) => ({ 11 | isOpen: false, 12 | open: () => set({ isOpen: true }), 13 | close: () => set({ isOpen: false }), 14 | toggle: () => set({ isOpen: !get().isOpen }), 15 | })); 16 | 17 | export default useWalletModal; 18 | -------------------------------------------------------------------------------- /hooks/useWeb3Store.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @constant State 3 | * @since 0.7.0 4 | */ 5 | import type MetaMaskConnector from '@/lib/connectors/metamask'; 6 | import type WalletConnectConnector from '@/lib/connectors/walletconnect'; 7 | import type { Web3Provider } from '@ethersproject/providers'; 8 | import create from 'zustand'; 9 | import { omit } from 'omit-ts'; 10 | 11 | export type State = { 12 | account?: string; 13 | chainId?: number; 14 | connector?: MetaMaskConnector | WalletConnectConnector; 15 | library?: Web3Provider; 16 | active: boolean; 17 | reset: () => void; 18 | }; 19 | 20 | const useWeb3Store = create((set, get) => ({ 21 | active: 22 | get()?.connector !== undefined && 23 | get()?.chainId !== undefined && 24 | get()?.account !== undefined, 25 | reset: () => 26 | set( 27 | (state) => omit(state, ['account', 'chainId', 'connector', 'library']), 28 | true, 29 | ), 30 | })); 31 | 32 | export default useWeb3Store; 33 | /** @export useWeb3Store */ 34 | -------------------------------------------------------------------------------- /hooks/view/GetFoldAmountOut.ts: -------------------------------------------------------------------------------- 1 | import { useFoldToken, useDictatorDAO } from '@/hooks/useContract'; 2 | import type { DOMODAO as DictatorDAO } from '@/contracts/types'; 3 | import { parseUnits } from '@ethersproject/units'; 4 | import useSWR from 'swr'; 5 | 6 | function getFoldAmountOut(contract: DictatorDAO) { 7 | return async (_: string, amount: string, operatorAddress: string) => { 8 | const getFoldAmountOutSingle = await contract.mint(amount, operatorAddress); 9 | 10 | return getFoldAmountOutSingle; 11 | }; 12 | } 13 | 14 | export default function useGetFoldAmountOut( 15 | amount: string, 16 | operatorAddress: string, 17 | ) { 18 | const contract = useDictatorDAO(); 19 | 20 | const shouldFetch = 21 | !!contract && 22 | typeof amount === 'string' && 23 | typeof operatorAddress === 'string'; 24 | 25 | return useSWR( 26 | shouldFetch ? ['GetFoldAmountOut', amount, operatorAddress] : null, 27 | getFoldAmountOut(contract), 28 | { 29 | shouldRetryOnError: false, 30 | }, 31 | ); 32 | } 33 | -------------------------------------------------------------------------------- /hooks/view/useEpochDates.ts: -------------------------------------------------------------------------------- 1 | import type { DOMODAO } from '@/contracts/types'; 2 | import { EPOCH_DURATION } from '@/constants/numbers'; 3 | import dayjs from 'dayjs'; 4 | import relativeTime from 'dayjs/plugin/relativeTime'; 5 | import { useDictatorDAO } from '../useContract'; 6 | import useSWR from 'swr'; 7 | 8 | dayjs.extend(relativeTime); 9 | 10 | function getEpochDates(contract: DOMODAO) { 11 | return async (_: string) => { 12 | const epochStart = await contract.GRACE_PERIOD(); 13 | 14 | const currentEpoch = await contract.DELAY(); 15 | 16 | const startDate = 17 | epochStart.toNumber() + (currentEpoch.toNumber() - 1) * EPOCH_DURATION; 18 | 19 | const endDate = 20 | epochStart.toNumber() + currentEpoch.toNumber() * EPOCH_DURATION; 21 | 22 | const progress = 23 | ((Date.now() - startDate * 1_000) / (EPOCH_DURATION * 1_000)) * 100; 24 | 25 | return { 26 | startDate, 27 | endDate, 28 | progress, 29 | relative: dayjs.unix(endDate).fromNow(true), 30 | }; 31 | }; 32 | } 33 | 34 | export default function useEpochDates() { 35 | const contract = useDictatorDAO(); 36 | 37 | const shouldFetch = !!contract; 38 | 39 | return useSWR(shouldFetch ? ['EpochDates'] : null, getEpochDates(contract)); 40 | } 41 | -------------------------------------------------------------------------------- /hooks/view/useGetPoolTokens.ts: -------------------------------------------------------------------------------- 1 | import { Token } from '@/components/tokenSelect'; 2 | import { CONTRACT_ADDRESSES } from '@/constants/contracts'; 3 | import { TOKEN_NAMES_BY_ADDRESS } from '@/constants/tokens'; 4 | import { FOLD } from '@/contracts/types'; 5 | import useSWR from 'swr'; 6 | // import useBestBuy from '../useBestBuy'; 7 | import { useFoldToken } from '../useContract'; 8 | 9 | function getFoldTokens(contract: FOLD) { 10 | return async (_: string, bestBuy?: Record) => { 11 | const values = await contract.getFoldTokens(); 12 | 13 | const formatted: Token[] = values 14 | .map((addr) => addr.toLowerCase()) 15 | .map((address) => { 16 | return { 17 | address: address, 18 | symbol: TOKEN_NAMES_BY_ADDRESS[address], 19 | out: BigInt(bestBuy?.[address] ?? 0), 20 | }; 21 | }); 22 | 23 | return formatted; 24 | }; 25 | } 26 | 27 | export default function useGetFoldTokens() { 28 | const { data: balanceOf } = useFoldToken(); 29 | 30 | const contract = useFoldToken(); 31 | 32 | const shouldFetch = !!contract; 33 | 34 | return useSWR( 35 | shouldFetch ? ['GetFoldTokens', balanceOf] : null, 36 | getFoldTokens(contract), 37 | ); 38 | } 39 | -------------------------------------------------------------------------------- /hooks/view/useGetTokenAmountOut.ts: -------------------------------------------------------------------------------- 1 | import { DictatorDAO } from './../../contracts/types/DictatorDAO.d'; 2 | import { CONTRACT_ADDRESSES } from '@/constants/contracts'; 3 | import { TOKEN_ADDRESSES } from '@/constants/tokens'; 4 | import { ERC20, FOLD, DOMODAO } from '@/contracts/types'; 5 | import { BigNumber } from '@ethersproject/bignumber'; 6 | import useSWR from 'swr'; 7 | import { useTokenContract } from '../useContract'; 8 | import useWeb3Store from '../useWeb3Store'; 9 | import useTokenBalance from './useTokenBalance'; 10 | 11 | function getGetTokenAmountOut( 12 | poolDictatorDao: DOMODAO, 13 | burnTokenContract: ERC20, 14 | ) { 15 | return async ( 16 | _: string, 17 | burnToken: string, 18 | foldAmountIn: BigNumber, 19 | chainId: number, 20 | ) => { 21 | const getTokenAmountOutBurn = await poolDictatorDao.burn( 22 | burnToken, 23 | foldAmountIn, 24 | ); 25 | 26 | const poolBalance = await burnTokenContract.balanceOf( 27 | CONTRACT_ADDRESSES[chainId], 28 | ); 29 | 30 | const maxWithdraw = poolBalance.div(3); 31 | 32 | if (getTokenAmountOutBurn.hash) { 33 | return maxWithdraw; 34 | } 35 | 36 | return getTokenAmountOutBurn; 37 | }; 38 | } 39 | 40 | export default function useGetTokenAmountOut(burnToken: string) { 41 | const account = useWeb3Store((state) => state.account); 42 | const chainId = useWeb3Store((state) => state.chainId); 43 | 44 | const { data: foldBalance } = useTokenBalance( 45 | account, 46 | TOKEN_ADDRESSES.FOLD[chainId], 47 | ); 48 | 49 | const poolDictatorDao = DictatorDAO; 50 | 51 | const burnTokenContract = useTokenContract(burnToken); 52 | 53 | const shouldFetch = 54 | !!poolDictatorDao && 55 | !!burnTokenContract && 56 | !!foldBalance && 57 | typeof burnToken === 'string'; 58 | 59 | return useSWR( 60 | shouldFetch ? ['GetTokenAmountOut', burnToken, foldBalance, chainId] : null, 61 | // @ts-ignore 62 | getGetTokenAmountOut(burnToken, burnTokenContract), 63 | ); 64 | } 65 | -------------------------------------------------------------------------------- /hooks/view/useGetxFOLDAmountOut.ts: -------------------------------------------------------------------------------- 1 | import type { BigNumberish } from '@ethersproject/bignumber'; 2 | import type { DOMODAO } from '@/contracts/types'; 3 | import { parseUnits } from '@ethersproject/units'; 4 | import { useDictatorDAO } from '../useContract'; 5 | import useSWR from 'swr'; 6 | 7 | function getXFoldAmountOut(contract: DOMODAO) { 8 | return async (_: string, amount: BigNumberish, operatorVote: string) => { 9 | const getXFoldAmountOutSingle = await contract.mint(amount, operatorVote); 10 | 11 | return getXFoldAmountOutSingle; 12 | }; 13 | } 14 | 15 | export default function useGetFoldAmountOut( 16 | amount: BigNumberish, 17 | operatorVote: string, 18 | ) { 19 | const contract = useDictatorDAO(); 20 | 21 | const shouldFetch = 22 | !!contract && 23 | typeof amount === 'string' && 24 | typeof operatorVote === 'string'; 25 | 26 | return useSWR( 27 | shouldFetch ? ['GetFoldAmountOut', amount, operatorVote] : null, 28 | getXFoldAmountOut(contract), 29 | { 30 | shouldRetryOnError: false, 31 | }, 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /hooks/view/useIsOperatorInitialized.ts: -------------------------------------------------------------------------------- 1 | import type { DOMODAO as DictatorDAO } from '@/contracts/types'; 2 | import useSWR from 'swr'; 3 | //import { DOMODAO } from '../useContract'; 4 | 5 | function getIsOperatorInitialized(contract: DictatorDAO) { 6 | return async () => { 7 | const currentOperator = await contract.pendingOperator(); 8 | 9 | const pendingOperatorTime = await contract.pendingOperatorTime(); 10 | 11 | return currentOperator.toString() === pendingOperatorTime.toString(); 12 | }; 13 | } 14 | 15 | export default function useIsOperatorInitialized() { 16 | const contract = useDOMODAO(); 17 | 18 | return useSWR( 19 | // @ts-ignore 20 | !!contract ? ['IsOperatorInitialized'] : null, 21 | // @ts-ignore 22 | getIsOperatorInitialized(contract), 23 | ); 24 | } 25 | function useDOMODAO() { 26 | throw new Error('Function not implemented.'); 27 | } 28 | -------------------------------------------------------------------------------- /hooks/view/useStakingBalanceLocked.ts: -------------------------------------------------------------------------------- 1 | import type { DOMODAO as DictatorDAO } from '@/contracts/types'; 2 | import useSWR from 'swr'; 3 | import { useDictatorDAO } from '../useContract'; 4 | 5 | function balanceOf(contract: DictatorDAO) { 6 | return async (_: string, user: string, token: string) => { 7 | const balanceLocked = await contract.balanceOf(user); 8 | 9 | return balanceLocked; 10 | }; 11 | } 12 | 13 | export default function getStakingBalance(user: string, token: string) { 14 | const contract = useDictatorDAO(); 15 | 16 | const shouldFetch = 17 | !!contract && typeof user === 'string' && typeof token === 'string'; 18 | 19 | return useSWR( 20 | shouldFetch ? ['balanceOf', user, token] : null, 21 | getStakingBalance(user, token), 22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /hooks/view/useTokenAllowance.ts: -------------------------------------------------------------------------------- 1 | import type { ERC20 } from '@/contracts/types'; 2 | import useSWR from 'swr'; 3 | import { useTokenContract } from '../useContract'; 4 | 5 | const getTokenAllowance = 6 | (contract: ERC20) => 7 | async (_: string, __: string, owner: string, spender: string) => { 8 | const value = await contract.allowance(owner, spender); 9 | 10 | return value; 11 | }; 12 | 13 | export default function useTokenAllowance( 14 | tokenAddress: string, 15 | owner: string, 16 | spender: string, 17 | ) { 18 | const contract = useTokenContract(tokenAddress); 19 | 20 | const shouldFetch = 21 | !!contract && 22 | typeof owner === 'string' && 23 | typeof tokenAddress === 'string' && 24 | typeof spender === 'string'; 25 | 26 | return useSWR( 27 | shouldFetch ? ['TokenBalance', tokenAddress, owner, spender] : null, 28 | getTokenAllowance(contract), 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /hooks/view/useTokenBalance.ts: -------------------------------------------------------------------------------- 1 | import type { ERC20 } from '@/contracts/types'; 2 | import useSWR from 'swr'; 3 | import { useTokenContract } from '../useContract'; 4 | import useKeepSWRDataLiveAsBlocksArrive from '../useKeepSWRDataLiveAsBlocksArrive'; 5 | 6 | const getTokenBalance = 7 | (contract: ERC20) => async (_: string, address: string) => { 8 | const value = await contract.balanceOf(address); 9 | 10 | return value; 11 | }; 12 | 13 | export default function useTokenBalance(address: string, tokenAddress: string) { 14 | const contract = useTokenContract(tokenAddress); 15 | 16 | const shouldFetch = 17 | !!contract && 18 | typeof address === 'string' && 19 | typeof tokenAddress === 'string'; 20 | 21 | const result = useSWR( 22 | shouldFetch ? ['TokenBalance', address, tokenAddress] : null, 23 | getTokenBalance(contract), 24 | ); 25 | 26 | useKeepSWRDataLiveAsBlocksArrive(result.mutate); 27 | 28 | return result; 29 | } 30 | -------------------------------------------------------------------------------- /hooks/view/useUserLockedUntil.ts: -------------------------------------------------------------------------------- 1 | import { useDictatorDAO } from '@/hooks/useContract'; 2 | import type { DOMODAO } from '@/contracts/types'; 3 | import calculateLockupMultiplier from '@/utils/calculateLockupMultiplier'; 4 | import dayjs from 'dayjs'; 5 | import utc from 'dayjs/plugin/utc'; 6 | import useSWR from 'swr'; 7 | import { useFoldToken } from '../useContract'; 8 | import useWeb3Store from '../useWeb3Store'; 9 | 10 | dayjs.extend(utc); 11 | 12 | function getUserLockedUntil(contract: DOMODAO) { 13 | return async (_: string, address: string) => { 14 | const lockedUntilTimestamp = await contract.GRACE_PERIOD(); 15 | 16 | const timestamp = lockedUntilTimestamp.toNumber(); 17 | 18 | const isLocked = dayjs.unix(timestamp).isAfter(dayjs()); 19 | 20 | const formatted = dayjs.unix(timestamp).format('MMM D, YYYY'); 21 | 22 | const multiplier = calculateLockupMultiplier( 23 | dayjs.unix(timestamp).diff(dayjs(), 'days'), 24 | ); 25 | 26 | return { 27 | timestamp, 28 | formatted, 29 | multiplier, 30 | isLocked, 31 | }; 32 | }; 33 | } 34 | 35 | export default function useUserLockedUntil() { 36 | const account = useWeb3Store((state) => state.account); 37 | const contract = useDictatorDAO(); 38 | 39 | const shouldFetch = !!contract && typeof account === 'string'; 40 | 41 | return useSWR( 42 | shouldFetch ? ['UserLockedUntil', account] : null, 43 | getUserLockedUntil(contract), 44 | ); 45 | } 46 | -------------------------------------------------------------------------------- /hooks/view/usexFOLDStaked.ts: -------------------------------------------------------------------------------- 1 | import type { DOMODAO as DictatorDAO } from '@/contracts/types'; 2 | import { useDictatorDAO } from '@/hooks/useContract'; 3 | import useSWR from 'swr'; 4 | import useWeb3Store from '../useWeb3Store'; 5 | 6 | export function getxFOLDStaked(contract: DictatorDAO) { 7 | return async (_: string, user: string) => { 8 | const value = await contract.balanceOf(user); 9 | 10 | return value; 11 | }; 12 | } 13 | 14 | export function useXFOLDStaked() { 15 | const account = useWeb3Store((state) => state.account); 16 | 17 | const contract = useDictatorDAO(); 18 | 19 | const shouldFetch = !!contract && typeof account === 'string'; 20 | 21 | return useSWR( 22 | shouldFetch ? ['balanceOf', account] : null, 23 | getxFOLDStaked(contract), 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | // jest.config.js 2 | const { pathsToModuleNameMapper } = require('ts-jest'); 3 | const { compilerOptions } = require('./tsconfig') 4 | module.exports = { 5 | testPathIgnorePatterns: ['/.next', '/node_modules'], 6 | collectCoverageFrom: [ 7 | '**/*.{js,jsx,ts,tsx}', 8 | '!**/*.d.ts', 9 | '!**/node_modules/**', 10 | ], 11 | moduleNameMapper: { 12 | // Handle CSS imports (with CSS modules) 13 | // https://jestjs.io/docs/webpack#mocking-css-modules 14 | '^.+\\.module\\.(css|sass|scss)$': 'identity-obj-proxy', 15 | 16 | // Handle CSS imports (without CSS modules) 17 | '^.+\\.(css|sass|scss)$': '/__mocks__/styleMock.js', 18 | 19 | // Handle image imports 20 | // https://jestjs.io/docs/webpack#handling-static-assets 21 | '^.+\\.(jpg|jpeg|png|gif|webp|avif|svg)$': `/__mocks__/fileMock.js`, 22 | 23 | // Handle module aliases 24 | '^@/components/(.*)$': '/components/$1', 25 | }, 26 | setupFilesAfterEnv: ['/jest.setup.js'], 27 | testPathIgnorePatterns: ['/node_modules/', '/.next/'], 28 | testEnvironment: 'jsdom', 29 | transform: { 30 | // Use babel-jest to transpile tests with the next/babel preset 31 | // https://jestjs.io/docs/configuration#transform-objectstring-pathtotransformer--pathtotransformer-object 32 | '^.+\\.(js|jsx|ts|tsx)$': ['babel-jest', { presets: ['next/babel'] }], 33 | }, 34 | transformIgnorePatterns: [ 35 | '/node_modules/', 36 | '^.+\\.module\\.(css|sass|scss)$', 37 | ], 38 | testEnvironment: 'jest-environment-jsdom', 39 | } -------------------------------------------------------------------------------- /jest.setup.js: -------------------------------------------------------------------------------- 1 | import '@testing-library/jest-dom' -------------------------------------------------------------------------------- /lib/coingecko.ts: -------------------------------------------------------------------------------- 1 | const BASE = `https://api.coingecko.com/api/v3`; 2 | 3 | export async function getETHPrice(): Promise { 4 | const COINGECKO_API = `${BASE}/simple/price?ids=ethereum&vs_currencies=USD`; 5 | 6 | type Data = { 7 | ethereum: { 8 | usd: number; 9 | }; 10 | }; 11 | 12 | try { 13 | const r = await fetch(COINGECKO_API); 14 | 15 | if (r.ok) { 16 | const data: Data = await r.json(); 17 | 18 | return data.ethereum.usd; 19 | } 20 | 21 | throw new Error(`${r.status} ${r.statusText}`); 22 | } catch (error) { 23 | console.error(error); 24 | 25 | throw error; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/connectors/metamask.ts: -------------------------------------------------------------------------------- 1 | import useWeb3Store from '@/hooks/useWeb3Store'; 2 | import normalizeChainId from '@/utils/normalizeChainId'; 3 | import detectEthereumProvider from '@metamask/detect-provider'; 4 | import type { 5 | IEthereumProvider, 6 | ProviderAccounts, 7 | ProviderChainId, 8 | ProviderRpcError, 9 | } from 'eip1193-provider'; 10 | 11 | const __DEV__ = true; 12 | export class NoEthereumProviderError extends Error { 13 | public constructor() { 14 | super(); 15 | this.name = this.constructor.name; 16 | this.message = 'No Ethereum provider found'; 17 | } 18 | } 19 | 20 | export class UnsupportedChainIdError extends Error { 21 | public constructor( 22 | unsupportedChainId: number, 23 | supportedChainIds?: readonly number[], 24 | ) { 25 | super(); 26 | this.name = this.constructor.name; 27 | this.message = `Unsupported chain id: ${unsupportedChainId}. Supported chain ids are: ${supportedChainIds}.`; 28 | } 29 | } 30 | 31 | export class UserRejectedRequestError extends Error { 32 | public constructor() { 33 | super(); 34 | this.name = this.constructor.name; 35 | this.message = 'The user rejected the request.'; 36 | } 37 | } 38 | 39 | export class UnsupportedProviderError extends Error { 40 | public constructor() { 41 | super(); 42 | this.name = this.constructor.name; 43 | this.message = 'Connect to site / This provider is not supported.'; 44 | } 45 | } 46 | 47 | export default class MetaMaskConnector { 48 | readonly supportedChainIds: number[]; 49 | 50 | constructor({ supportedChainIds }: { supportedChainIds: number[] }) { 51 | this.supportedChainIds = supportedChainIds; 52 | } 53 | 54 | private handleChainChanged = (chainId: ProviderChainId) => { 55 | if (__DEV__) { 56 | console.log('[handleChainChanged]', chainId); 57 | } 58 | 59 | window.location.reload(); 60 | }; 61 | 62 | private handleAccountsChanged = (accounts: ProviderAccounts) => { 63 | if (__DEV__) { 64 | console.log('[handleAccountsChanged]', accounts); 65 | } 66 | 67 | useWeb3Store.setState({ 68 | account: accounts[0], 69 | }); 70 | }; 71 | 72 | private handleDisconnect = (error: ProviderRpcError) => { 73 | if (__DEV__) { 74 | console.log('[handleDisconnect]', error); 75 | } 76 | 77 | this.deactivate(); 78 | }; 79 | 80 | public activate = async () => { 81 | if (__DEV__) { 82 | console.log('[activate]'); 83 | } 84 | 85 | const provider = (await detectEthereumProvider()) as IEthereumProvider; 86 | 87 | if (!provider) { 88 | throw new NoEthereumProviderError(); 89 | } 90 | 91 | let accounts: ProviderAccounts; 92 | 93 | try { 94 | accounts = (await provider.request({ 95 | method: 'eth_requestAccounts', 96 | })) as ProviderAccounts; 97 | } catch (error) { 98 | if ((error as any).code === 4001) { 99 | throw new UserRejectedRequestError(); 100 | } 101 | 102 | console.log( 103 | 'eth_requestAccounts was unsuccessful, falling back to enable', 104 | ); 105 | } 106 | 107 | if (!accounts) { 108 | try { 109 | accounts = await provider.enable(); 110 | } catch (error) { 111 | if ((error as any).code === 4001) { 112 | throw new UserRejectedRequestError(); 113 | } 114 | 115 | console.log('enable was unsuccessful'); 116 | 117 | throw new UnsupportedProviderError(); 118 | } 119 | } 120 | 121 | const account = accounts[0]; 122 | 123 | let _chainId: string; 124 | 125 | try { 126 | _chainId = (await provider.request({ method: 'eth_chainId' })) as string; 127 | } catch { 128 | console.log('eth_chainId was unsuccessful, falling back to net_version'); 129 | } 130 | 131 | if (!_chainId) { 132 | try { 133 | _chainId = (provider as any).send({ method: 'net_version' }) 134 | ?.result as string; 135 | } catch { 136 | console.log('net_version v2 was unsuccessful'); 137 | 138 | throw new UnsupportedProviderError(); 139 | } 140 | } 141 | 142 | const chainId = normalizeChainId(_chainId as string); 143 | 144 | if (!!this.supportedChainIds && !this.supportedChainIds.includes(chainId)) { 145 | throw new UnsupportedChainIdError(chainId, this.supportedChainIds); 146 | } 147 | 148 | const Web3Provider = (await import('@ethersproject/providers')) 149 | .Web3Provider; 150 | 151 | const library = new Web3Provider(provider); 152 | 153 | library.pollingInterval = 12000; 154 | 155 | useWeb3Store.setState({ 156 | connector: this, 157 | chainId: chainId, 158 | account: account, 159 | library: library, 160 | }); 161 | 162 | if (provider.on) { 163 | provider.on('chainChanged', this.handleChainChanged); 164 | provider.on('accountsChanged', this.handleAccountsChanged); 165 | provider.on('disconnect', this.handleDisconnect); 166 | } 167 | }; 168 | 169 | public deactivate = () => { 170 | if (__DEV__) { 171 | console.log('[deactivate]'); 172 | } 173 | 174 | if ((window as any).ethereum && (window as any).ethereum.removeListener) { 175 | (window as any).ethereum.removeListener( 176 | 'chainChanged', 177 | this.handleChainChanged, 178 | ); 179 | (window as any).ethereum.removeListener( 180 | 'accountsChanged', 181 | this.handleAccountsChanged, 182 | ); 183 | (window as any).ethereum.removeListener( 184 | 'disconnect', 185 | this.handleDisconnect, 186 | ); 187 | } 188 | }; 189 | 190 | public isAuthorized = async () => { 191 | if (!window.ethereum) { 192 | return false; 193 | } 194 | 195 | try { 196 | const provider = (await detectEthereumProvider()) as IEthereumProvider; 197 | 198 | return await provider 199 | .request({ 200 | method: 'eth_requestAccounts', 201 | }) 202 | .then((accounts: string[]) => { 203 | if (accounts.length > 0) { 204 | return true; 205 | } else { 206 | return false; 207 | } 208 | }); 209 | } catch { 210 | return false; 211 | } 212 | }; 213 | } 214 | 215 | export const injected = new MetaMaskConnector({ 216 | supportedChainIds: [1, 4], 217 | }); 218 | -------------------------------------------------------------------------------- /lib/connectors/walletconnect.ts: -------------------------------------------------------------------------------- 1 | import { INFURA_ID } from '@/constants/chains'; 2 | 3 | import useWeb3Store from '@/hooks/useWeb3Store'; 4 | import normalizeChainId from '@/utils/normalizeChainId'; 5 | import type WalletConnectProvider from '@walletconnect/ethereum-provider'; 6 | import { UnsupportedChainIdError } from './metamask'; 7 | 8 | const __DEV__ = true; 9 | export default class WalletConnectConnector { 10 | public wc?: WalletConnectProvider; 11 | 12 | readonly supportedChainIds: number[]; 13 | 14 | constructor({ supportedChainIds }: { supportedChainIds: number[] }) { 15 | this.supportedChainIds = supportedChainIds; 16 | } 17 | 18 | private handleChainChanged = (chainId: string | number) => { 19 | if (__DEV__) { 20 | console.log('[handleChainChanged]', chainId); 21 | } 22 | 23 | window.location.reload(); 24 | }; 25 | 26 | private handleAccountsChanged = (accounts: string[]) => { 27 | if (__DEV__) { 28 | console.log('[handleAccountsChanged]', accounts); 29 | } 30 | 31 | useWeb3Store.setState({ 32 | account: accounts[0], 33 | }); 34 | }; 35 | 36 | private handleDisconnect = (code: number, reason: string) => { 37 | if (__DEV__) { 38 | console.log('[handleDisconnect]', code, reason); 39 | } 40 | 41 | this.deactivate(); 42 | }; 43 | 44 | public activate = async () => { 45 | if (__DEV__) { 46 | console.log('[activate]'); 47 | } 48 | 49 | const WalletConnectProvider = ( 50 | await import('@walletconnect/ethereum-provider') 51 | ).default; 52 | 53 | const provider = new WalletConnectProvider({ 54 | infuraId: INFURA_ID, 55 | }); 56 | 57 | this.wc = provider; 58 | 59 | const accounts = await this.wc.enable(); 60 | 61 | const account = accounts[0]; 62 | 63 | const _chainId = this.wc.chainId; 64 | 65 | const chainId = normalizeChainId(_chainId); 66 | 67 | if (!!this.supportedChainIds && !this.supportedChainIds.includes(chainId)) { 68 | throw new UnsupportedChainIdError(chainId, this.supportedChainIds); 69 | } 70 | 71 | const Web3Provider = (await import('@ethersproject/providers')) 72 | .Web3Provider; 73 | 74 | const library = new Web3Provider(this.wc); 75 | 76 | library.pollingInterval = 12000; 77 | 78 | useWeb3Store.setState({ 79 | connector: this, 80 | chainId: chainId, 81 | account: account, 82 | library: library, 83 | }); 84 | 85 | if (this.wc.on) { 86 | this.wc.on('chainChanged', this.handleChainChanged); 87 | this.wc.on('accountsChanged', this.handleAccountsChanged); 88 | this.wc.on('disconnect', this.handleDisconnect); 89 | } 90 | }; 91 | 92 | public deactivate = () => { 93 | if (__DEV__) { 94 | console.log('[deactivate]'); 95 | } 96 | 97 | if (this.wc) { 98 | this.wc.removeListener('disconnect', this.handleDisconnect); 99 | this.wc.removeListener('chainChanged', this.handleChainChanged); 100 | this.wc.removeListener('accountsChanged', this.handleAccountsChanged); 101 | this.wc.disconnect(); 102 | } 103 | }; 104 | } 105 | 106 | export const walletconnect = new WalletConnectConnector({ 107 | supportedChainIds: [1, 4], 108 | }); 109 | -------------------------------------------------------------------------------- /next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/basic-features/typescript for more information. 6 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | module.exports = { 3 | reactStrictMode: true, 4 | productionBrowserSourceMaps: false, 5 | poweredByHeader: false, 6 | optimizeImages: true, 7 | optimizeCss: true, 8 | // experimental: { 9 | // esmExternals: true, 10 | // outputFileTracing: true 11 | // }, 12 | images: { 13 | domains: [ 14 | 'raw.githubusercontent.com', 15 | 'assets.coingecko.com', 16 | 'logos.covalenthq.com', 17 | 'www.covalenthq.com', 18 | 's2.coinmarketcap.com', 19 | ], 20 | }, 21 | webpack: (config, { isServer }) => { 22 | if (!isServer) { 23 | config.resolve.fallback.fs = false 24 | } 25 | return config 26 | }, 27 | } 28 | 29 | // Don't delete this console log, useful to see the config in Vercel deployments 30 | console.log('next.config.js', JSON.stringify(module.exports, null, 2)) 31 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@manifoldfinance/staking", 3 | "version": "0.9.2", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "export NEXT_PUBLIC_COMMIT=$(git rev-parse HEAD) && npx next build", 8 | "fmt": "npx prettier --config .prettierrc.json --write '**/*.ts'", 9 | "start": "next start", 10 | "lint": "next lint --fix", 11 | "test": "npx jest", 12 | "type-check": "npx tsc --pretty --noEmit", 13 | "format": "npx prettier --write \"**/*.tsx\" \"**/*.ts\"", 14 | "analyze": "ANALYZE=true next build", 15 | "compile-contract-types": "npx typechain --target ethers-v5 --out-dir './contracts/types' './contracts/*.json'" 16 | }, 17 | "dependencies": { 18 | "@davatar/react": "^1.8.1", 19 | "@ethersproject/abi": "5.6.3", 20 | "@ethersproject/abstract-provider": "5.7.0", 21 | "@ethersproject/address": "5.6.0", 22 | "@ethersproject/bignumber": "^5", 23 | "@ethersproject/bytes": "5.6.0", 24 | "@ethersproject/constants": "^5.6.0", 25 | "@ethersproject/contracts": "^5.6.0", 26 | "@ethersproject/properties": "5.4.0", 27 | "@ethersproject/providers": "5.7.1", 28 | "@ethersproject/transactions": "5.6.2", 29 | "@ethersproject/units": "5.6.0", 30 | "@ethersproject/web": "5.6.0", 31 | "@headlessui/react": "^1.6.6", 32 | "@metamask/detect-provider": "^1.2.0", 33 | "@metamask/onboarding": "^1.0.1", 34 | "@radix-ui/react-slider": "^0.1.4", 35 | "@sushiswap/core-sdk": "1.0.0-canary.29", 36 | "@sushiswap/sdk": "5.0.0-canary.116", 37 | "@walletconnect/ethereum-provider": "^1.7.8", 38 | "@web3-react/core": "^6.1.9", 39 | "@web3-react/store": "^8.0.13-alpha.0", 40 | "@web3-react/types": "^6.0.7", 41 | "@wwwr/injectedprovider": "^1.0.0", 42 | "classnames": "^2.3.1", 43 | "dayjs": "^1.10.6", 44 | "eth-rpc-errors": "^4.0.3", 45 | "ethers": "^5.6.9", 46 | "fathom-client": "^3.1.0", 47 | "ismobilejs": "^1.1.1", 48 | "kbar": "^0.1.0-beta.36", 49 | "next": "^12.2.2", 50 | "next-seo": "^4.28.1", 51 | "omit-ts": "^1.0.1", 52 | "react": "17.0.2", 53 | "react-dom": "17.0.2", 54 | "react-feather": "^2.0.9", 55 | "react-hot-toast": "^2.1.1", 56 | "react-use": "^17.3.1", 57 | "swr": "^1.3.0", 58 | "zustand": "^3.7.2" 59 | }, 60 | "devDependencies": { 61 | "@babel/core": "7.16.5", 62 | "@babel/preset-env": "^7.16.5", 63 | "@babel/preset-typescript": "^7.16.5", 64 | "@next/bundle-analyzer": "^12.0.7", 65 | "@testing-library/jest-dom": "^5.16.1", 66 | "@testing-library/react": "^12.1.2", 67 | "@typechain/ethers-v5": "^10.1.0", 68 | "@types/classnames": "^2.3.1", 69 | "@types/gtag.js": "^0.0.8", 70 | "@types/jest": "^27.0.3", 71 | "@types/node": "^16", 72 | "@types/react": "^17.0.19", 73 | "@types/react-dom": "^17.0.11", 74 | "autoprefixer": "^10.3.3", 75 | "babel-jest": "^27.4.5", 76 | "csstype": "^2.6.2", 77 | "eip1193-provider": "^1.0.1", 78 | "eslint": "7.32.0", 79 | "eslint-config-next": "11.1.0", 80 | "eslint-config-prettier": "^8.3.0", 81 | "ganache-cli": "^6.12.2", 82 | "identity-obj-proxy": "^3.0.0", 83 | "jest": "27.0.0", 84 | "lint-staged": "^11.1.2", 85 | "postcss": "^8.3.6", 86 | "prettier": "^2.7.1", 87 | "pretty-quick": "^3.1.1", 88 | "tailwindcss": "^2.2.19", 89 | "ts-jest": "27.0.0", 90 | "ts-node": "^10.4.0", 91 | "tslib": "^2.4.0", 92 | "typechain": "^8.1.0", 93 | "typescript": "^4.2.3", 94 | "web3": "^1.6.1" 95 | }, 96 | "repository": { 97 | "type": "git", 98 | "url": "git+https://github.com/manifoldfinance/staking.git" 99 | }, 100 | "keywords": [ 101 | "ethereum", 102 | "staking" 103 | ], 104 | "author": "Manifold Finance, Inc", 105 | "license": "MIT", 106 | "bugs": { 107 | "url": "https://github.com/manifoldfinance/staking/issues" 108 | }, 109 | "homepage": "https://github.com/manifoldfinance/staking#readme", 110 | "browserslist": { 111 | "production": [ 112 | ">0.3%", 113 | "not dead", 114 | "not op_mini all", 115 | "not IE > 0", 116 | "not samsung 4", 117 | "not and_uc 12.12" 118 | ], 119 | "development": [ 120 | "last 1 chrome version", 121 | "last 1 firefox version", 122 | "last 1 edge version", 123 | "last 1 safari version" 124 | ] 125 | }, 126 | "nextBundleAnalysis": { 127 | "budget": 563200, 128 | "budgetPercentIncreaseRed": 25, 129 | "showDetails": true 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /pages/404.tsx: -------------------------------------------------------------------------------- 1 | function CustomErrorPage() { 2 | return ( 3 |
4 |
5 |
6 |

Four O Four

7 | 8 |

9 | This page could not be found 10 |

11 |
12 |
13 |
14 | ); 15 | } 16 | 17 | export default CustomErrorPage; 18 | -------------------------------------------------------------------------------- /pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import '../styles/globals.css'; 2 | 3 | import type { AppProps } from 'next/app'; 4 | import Head from 'next/head'; 5 | import Navigation from '@/components/navigation'; 6 | import { Toaster } from 'react-hot-toast'; 7 | import { useEagerConnect } from '@/hooks/useEagerConnect'; 8 | 9 | function MyApp({ Component, pageProps }: AppProps) { 10 | useEagerConnect(); 11 | 12 | return ( 13 | <> 14 | 15 | Manifold Finance 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 |
24 | 25 | 26 | 27 | ); 28 | } 29 | 30 | export default MyApp; 31 | -------------------------------------------------------------------------------- /pages/_document.tsx: -------------------------------------------------------------------------------- 1 | import Document, { Html, Head, Main, NextScript } from 'next/document'; 2 | 3 | class MyDocument extends Document { 4 | render() { 5 | return ( 6 | 7 | 8 | 13 | 19 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
32 | 33 | 34 | 35 |
36 | 37 | 38 | ); 39 | } 40 | } 41 | 42 | export default MyDocument; 43 | -------------------------------------------------------------------------------- /pages/faqs.tsx: -------------------------------------------------------------------------------- 1 | import qa from '@/data/qa.json'; 2 | 3 | function FAQPage() { 4 | return ( 5 |
6 |
7 |
8 |

FAQs

9 |
10 | 11 |
12 |
    13 | {qa.map((datum, index) => ( 14 |
  • 15 |
    16 |

    17 | {datum.question} 18 |

    19 | 20 |

    {datum.answer}

    21 |
    22 |
  • 23 | ))} 24 |
25 |
26 |
27 |
28 | ); 29 | } 30 | 31 | export default FAQPage; 32 | -------------------------------------------------------------------------------- /pages/index.tsx: -------------------------------------------------------------------------------- 1 | import { useRouter } from 'next/dist/client/router'; 2 | import { useEffect } from 'react'; 3 | 4 | function HomePage() { 5 | const { replace } = useRouter(); 6 | 7 | useEffect(() => { 8 | replace('/stake'); 9 | }, [replace]); 10 | 11 | return null; 12 | } 13 | 14 | export default HomePage; 15 | -------------------------------------------------------------------------------- /pages/mint.tsx: -------------------------------------------------------------------------------- 1 | import ConnectAccount from '@/components/connectAccount'; 2 | import MintView from '@/views/mint'; 3 | import useWeb3Store from '@/hooks/useWeb3Store'; 4 | 5 | function MintPage() { 6 | const account = useWeb3Store((state) => state.account); 7 | 8 | if (account) return ; 9 | 10 | return ; 11 | } 12 | 13 | export default MintPage; 14 | -------------------------------------------------------------------------------- /pages/stake.tsx: -------------------------------------------------------------------------------- 1 | import ConnectAccount from '@/components/connectAccount'; 2 | import useWeb3Store from '@/hooks/useWeb3Store'; 3 | import StakeView from '@/views/stake'; 4 | 5 | function StakePage() { 6 | const account = useWeb3Store((state) => state.account); 7 | 8 | if (account) return ; 9 | 10 | return ; 11 | } 12 | 13 | export default StakePage; 14 | -------------------------------------------------------------------------------- /pages/vote.tsx: -------------------------------------------------------------------------------- 1 | import ConnectAccount from '@/components/connectAccount'; 2 | import useWeb3Store from '@/hooks/useWeb3Store'; 3 | //import VoteView from '@/views/vote'; 4 | 5 | function VotePage() { 6 | const account = useWeb3Store((state) => state.account); 7 | 8 | if (account) return ; 9 | 10 | return ; 11 | } 12 | 13 | export default VotePage; 14 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /public/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manifoldfinance/staking/b6f68dbcad9d4fa8267bffcbfc9dd05c6a67f090/public/android-chrome-192x192.png -------------------------------------------------------------------------------- /public/android-chrome-384x384.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manifoldfinance/staking/b6f68dbcad9d4fa8267bffcbfc9dd05c6a67f090/public/android-chrome-384x384.png -------------------------------------------------------------------------------- /public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manifoldfinance/staking/b6f68dbcad9d4fa8267bffcbfc9dd05c6a67f090/public/apple-touch-icon.png -------------------------------------------------------------------------------- /public/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #000000 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /public/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manifoldfinance/staking/b6f68dbcad9d4fa8267bffcbfc9dd05c6a67f090/public/favicon-16x16.png -------------------------------------------------------------------------------- /public/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manifoldfinance/staking/b6f68dbcad9d4fa8267bffcbfc9dd05c6a67f090/public/favicon-32x32.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manifoldfinance/staking/b6f68dbcad9d4fa8267bffcbfc9dd05c6a67f090/public/favicon.ico -------------------------------------------------------------------------------- /public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manifoldfinance/staking/b6f68dbcad9d4fa8267bffcbfc9dd05c6a67f090/public/logo.png -------------------------------------------------------------------------------- /public/mstile-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manifoldfinance/staking/b6f68dbcad9d4fa8267bffcbfc9dd05c6a67f090/public/mstile-310x310.png -------------------------------------------------------------------------------- /public/safari-pinned-tab.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | Created by potrace 1.14, written by Peter Selinger 2001-2017 9 | 10 | 12 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /public/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Manifold Finance Staking DApp", 3 | "short_name": "Manifold DApp", 4 | "icons": [ 5 | { 6 | "src": "/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "/android-chrome-384x384.png", 12 | "sizes": "384x384", 13 | "type": "image/png" 14 | } 15 | ], 16 | "theme_color": "#ffffff", 17 | "background_color": "#ffffff", 18 | "display": "standalone" 19 | } 20 | -------------------------------------------------------------------------------- /public/tokens/ETH.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manifoldfinance/staking/b6f68dbcad9d4fa8267bffcbfc9dd05c6a67f090/public/tokens/ETH.png -------------------------------------------------------------------------------- /public/tokens/ETH@32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manifoldfinance/staking/b6f68dbcad9d4fa8267bffcbfc9dd05c6a67f090/public/tokens/ETH@32.png -------------------------------------------------------------------------------- /public/tokens/FOLD.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manifoldfinance/staking/b6f68dbcad9d4fa8267bffcbfc9dd05c6a67f090/public/tokens/FOLD.png -------------------------------------------------------------------------------- /public/tokens/FOLD@32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manifoldfinance/staking/b6f68dbcad9d4fa8267bffcbfc9dd05c6a67f090/public/tokens/FOLD@32.png -------------------------------------------------------------------------------- /public/tokens/MANIFOLD.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manifoldfinance/staking/b6f68dbcad9d4fa8267bffcbfc9dd05c6a67f090/public/tokens/MANIFOLD.png -------------------------------------------------------------------------------- /public/tokens/SUSHI.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manifoldfinance/staking/b6f68dbcad9d4fa8267bffcbfc9dd05c6a67f090/public/tokens/SUSHI.png -------------------------------------------------------------------------------- /public/tokens/SUSHI@32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manifoldfinance/staking/b6f68dbcad9d4fa8267bffcbfc9dd05c6a67f090/public/tokens/SUSHI@32.png -------------------------------------------------------------------------------- /public/tokens/USDC.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manifoldfinance/staking/b6f68dbcad9d4fa8267bffcbfc9dd05c6a67f090/public/tokens/USDC.png -------------------------------------------------------------------------------- /public/tokens/USDC@32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manifoldfinance/staking/b6f68dbcad9d4fa8267bffcbfc9dd05c6a67f090/public/tokens/USDC@32.png -------------------------------------------------------------------------------- /public/tokens/USDM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manifoldfinance/staking/b6f68dbcad9d4fa8267bffcbfc9dd05c6a67f090/public/tokens/USDM.png -------------------------------------------------------------------------------- /public/tokens/XFOLD.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manifoldfinance/staking/b6f68dbcad9d4fa8267bffcbfc9dd05c6a67f090/public/tokens/XFOLD.png -------------------------------------------------------------------------------- /public/tokens/XFOLD@32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manifoldfinance/staking/b6f68dbcad9d4fa8267bffcbfc9dd05c6a67f090/public/tokens/XFOLD@32.png -------------------------------------------------------------------------------- /public/tokens/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manifoldfinance/staking/b6f68dbcad9d4fa8267bffcbfc9dd05c6a67f090/public/tokens/logo.png -------------------------------------------------------------------------------- /public/tokens/openmev.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manifoldfinance/staking/b6f68dbcad9d4fa8267bffcbfc9dd05c6a67f090/public/tokens/openmev.png -------------------------------------------------------------------------------- /scripts/atomic.cjs: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const fs = require('fs'); 3 | const fetch = require('node-fetch'); 4 | 5 | const commit = process.env.VERCEL_GIT_COMMIT_SHA; 6 | 7 | async function getCommit() { 8 | if (!commit) return `No COMMIT_SHA environment variable set.`; 9 | try { 10 | const res = await fetch( 11 | `https://api.github.com/repos/manifoldfinance/sushiswap-interface/commits/${commit}`, 12 | ); 13 | const data = await res.json(); 14 | return { 15 | isDeployCommit: commit === 'HEAD' ? 'Unknown' : true, 16 | sha: data.sha, 17 | author: data.commit.author.name, 18 | date: data.commit.author.date, 19 | message: data.commit.message, 20 | link: data.html_url, 21 | }; 22 | } catch (error) { 23 | return `Unable to get git commit info: ${error.message}`; 24 | } 25 | } 26 | 27 | async function go() { 28 | const buildInfo = { 29 | buildTime: Date.now(), 30 | commit: await getCommit(), 31 | }; 32 | 33 | // @note this is different for nextjs, should be in dir: `.next/~` 34 | fs.writeFileSync( 35 | path.join(__dirname, '.next/server/pages/artifact.json'), 36 | JSON.stringify(buildInfo, null, 2), 37 | ); 38 | console.log('build info generated', buildInfo); 39 | } 40 | go(); 41 | -------------------------------------------------------------------------------- /scripts/git-hash.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -Eeo pipefail 3 | # -*- tab-width: 4; encoding: utf-8 -*- 4 | ## v0.0.2 5 | ## CC0-1.0 6 | # Return the short git hash for a source tree 7 | # 8 | # > usage 9 | # $ git-hash.sh [dir] 10 | # 11 | # The Current directory is assumed to identify the source tree unless supplied with argument 12 | ## The source tree is the result of `git archive'. 13 | # `git archive' inserts the abbreviated hash of the archive's commit into this script 14 | ### `.gitattributes' file: 15 | # `.gitattributes` 16 | # git-hash.sh export-subst 17 | ### 18 | # @see {@link https://git-scm.com/docs/gitattributes} 19 | 20 | # shellcheck disable=SC2154 21 | git_hash=$Format:%h$ 22 | 23 | if test "${git_hash}" != $Format:%h$; then 24 | echo "${git_hash}" 25 | exit 0 26 | fi 27 | 28 | ## @function secondary 29 | ## @summary The source tree is a git repository. 30 | 31 | case $# in 32 | 0) ;; 33 | 1) if test ! -d "$1"; then 34 | echo "error" 35 | exit 1 36 | fi 37 | cd "$1" || exit 38 | if test $? -eq 1; then 39 | echo "error" 40 | exit 1 41 | fi;; 42 | *) echo "error" 43 | exit 1;; 44 | esac 45 | 46 | git_hash=$(git show -s --format=%h . 2>/dev/null) 47 | if test $? -eq 0; then 48 | echo "${git_hash}" 49 | exit 0 50 | fi 51 | 52 | ## @return Error 53 | ## @summary application unable to resolve input command, exit 54 | # @note TRAP could be used but this is portable (posix) 55 | echo "ERROR: Command not found" 56 | exit 127 -------------------------------------------------------------------------------- /scripts/version.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | git_hash='$Format:%h$' 3 | 4 | if test "${git_hash}" != '$''Format:%h$'; then 5 | echo "${git_hash}" 6 | exit 0 7 | fi 8 | 9 | case $# in 10 | 0) ;; 11 | 1) if test ! -d "$1"; then 12 | echo "error" 13 | exit 1 14 | fi 15 | cd "$1" 16 | if test $? -eq 1; then 17 | echo "error" 18 | exit 1 19 | fi;; 20 | *) echo "error" 21 | exit 1;; 22 | esac 23 | 24 | git_hash=`git show -s --format=%h . 2>/dev/null` 25 | if test $? -eq 0; then 26 | echo "${git_hash}" 27 | exit 0 28 | fi 29 | 30 | echo "unknown" 31 | exit 0 32 | -------------------------------------------------------------------------------- /styles/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @layer utilities { 6 | .hide-number-input-arrows::-webkit-outer-spin-button, 7 | .hide-number-input-arrows::-webkit-inner-spin-button { 8 | -webkit-appearance: none; 9 | margin: 0; 10 | } 11 | .hide-number-input-arrows[type="number"] { 12 | -moz-appearance: textfield; 13 | } 14 | .touch-action-none { 15 | touch-action: none; 16 | } 17 | } 18 | 19 | .background-radial-gradient { 20 | position: fixed; 21 | top: 0; 22 | left: 0; 23 | right: 0; 24 | pointer-events: none; 25 | width: 200vw; 26 | height: 200vh; 27 | background: radial-gradient( 28 | 50% 50% at 50% 50%, 29 | rgba(99, 102, 241, 6%), 30 | rgba(255, 255, 255, 0) 100% 31 | ); 32 | transform: translate(-50vw, -100vh); 33 | z-index: -1; 34 | } 35 | -------------------------------------------------------------------------------- /svgs/MetaMaskOutline.tsx: -------------------------------------------------------------------------------- 1 | export default function MetaMaskOutline({ 2 | title = 'MetaMask', 3 | titleId = 'metamask', 4 | size = 24, 5 | }) { 6 | return ( 7 | 15 | {title} 16 | 22 | 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /svgs/WalletConnect.tsx: -------------------------------------------------------------------------------- 1 | export default function WalletConnect({ 2 | title = 'WalletConnect', 3 | titleId = 'walletconnect', 4 | size = 24, 5 | }) { 6 | return ( 7 | 15 | {title} 16 | 20 | 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | mode: 'jit', 3 | purge: [ 4 | './components/**/*.{tsx,ts}', 5 | './pages/**/*.{tsx,ts}', 6 | './views/**/*.{tsx,ts}', 7 | './constants/*.ts', 8 | ], 9 | darkMode: 'media', 10 | theme: { 11 | extend: { 12 | colors: { 13 | primary: { 14 | DEFAULT: '#0C0F21', 15 | 400: '#181b2c', 16 | 300: '#303342', 17 | }, 18 | network: { 19 | rinkeby: '#f6c343', 20 | }, 21 | }, 22 | }, 23 | }, 24 | variants: { 25 | extend: {}, 26 | }, 27 | plugins: [], 28 | }; 29 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "strict": false, 12 | "forceConsistentCasingInFileNames": true, 13 | "noEmit": true, 14 | "esModuleInterop": true, 15 | "allowSyntheticDefaultImports": true, 16 | "declaration": false, 17 | "sourceMap": false, 18 | "module": "ESNext", 19 | "moduleResolution": "node", 20 | "strictNullChecks": false, 21 | "resolveJsonModule": true, 22 | "isolatedModules": true, 23 | "jsx": "preserve", 24 | "baseUrl": ".", 25 | "paths": { 26 | "@/*": [ 27 | "./*" 28 | ] 29 | }, 30 | "incremental": true, 31 | "tsBuildInfoFile": "./" 32 | }, 33 | "include": [ 34 | "next-env.d.ts", 35 | "**/*.ts", 36 | "**/*.tsx" 37 | , "jest.config.js", "__tests__/depositStake.js" 38 | ], 39 | "exclude": [ 40 | "**/node_modules", 41 | "**/.*/", 42 | "./contracts/types/DictatorDAOFactory.ts" 43 | ], 44 | "files": ["connectors.ts"] 45 | } 46 | -------------------------------------------------------------------------------- /utils/bn.ts: -------------------------------------------------------------------------------- 1 | import type { BigNumberish } from '@ethersproject/bignumber'; 2 | import { formatFixed } from '@ethersproject/bignumber'; 3 | 4 | /** 5 | * @name btof 6 | * @description Converts a BigNumber to Float based on the provided decimals 7 | */ 8 | export function btof(value?: BigNumberish, decimals = 18) { 9 | return parseFloat(formatFixed(value, decimals)); 10 | } 11 | -------------------------------------------------------------------------------- /utils/calculateLockupMultiplier.ts: -------------------------------------------------------------------------------- 1 | export default function calculateLockupMultiplier(days: string | number) { 2 | return Number((Number(days) / (365 * 2)) * 0.5 + 1).toFixed(2); 3 | } 4 | -------------------------------------------------------------------------------- /utils/escapeRegExp.ts: -------------------------------------------------------------------------------- 1 | export const INPUT_REGEX = RegExp(`^\\d*(?:\\\\[.])?\\d*$`); // match escaped "." characters via in a non-capturing group 2 | 3 | export default function escapeRegExp(string: string): string { 4 | return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string 5 | } 6 | -------------------------------------------------------------------------------- /utils/formatNumber.ts: -------------------------------------------------------------------------------- 1 | import { commify } from '@ethersproject/units'; 2 | 3 | export default function formatNumber(value?: number, decimals = 2) { 4 | if (typeof value === 'number') { 5 | return commify(value.toFixed(decimals)); 6 | } 7 | 8 | return Number(0).toFixed(decimals); 9 | } 10 | -------------------------------------------------------------------------------- /utils/getExplorerLink.ts: -------------------------------------------------------------------------------- 1 | import { SupportedChainId } from '@/constants/chains'; 2 | 3 | const ETHERSCAN_PREFIXES: { [chainId: number]: string } = { 4 | [SupportedChainId.MAINNET]: '', 5 | [SupportedChainId.RINKEBY]: 'rinkeby.', 6 | }; 7 | 8 | export enum ExplorerDataType { 9 | TRANSACTION = 'transaction', 10 | TOKEN = 'token', 11 | ADDRESS = 'address', 12 | BLOCK = 'block', 13 | } 14 | 15 | /** 16 | * Return the explorer link for the given data and data type 17 | * @param chainId the ID of the chain for which to return the data 18 | * @param data the data to return a link for 19 | * @param type the type of the data 20 | */ 21 | export default function getExplorerLink( 22 | chainId: number, 23 | data: string, 24 | type: ExplorerDataType, 25 | ): string { 26 | const prefix = `https://${ETHERSCAN_PREFIXES[chainId] ?? ''}etherscan.io`; 27 | 28 | switch (type) { 29 | case ExplorerDataType.TRANSACTION: 30 | return `${prefix}/tx/${data}`; 31 | 32 | case ExplorerDataType.TOKEN: 33 | return `${prefix}/token/${data}`; 34 | 35 | case ExplorerDataType.BLOCK: 36 | return `${prefix}/block/${data}`; 37 | 38 | case ExplorerDataType.ADDRESS: 39 | return `${prefix}/address/${data}`; 40 | default: 41 | return `${prefix}`; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /utils/getFutureTimestamp.ts: -------------------------------------------------------------------------------- 1 | import dayjs from 'dayjs'; 2 | import utc from 'dayjs/plugin/utc'; 3 | 4 | dayjs.extend(utc); 5 | 6 | export default function getFutureTimestamp(days: number) { 7 | return dayjs().utc(true).add(days, 'days').unix(); 8 | } 9 | -------------------------------------------------------------------------------- /utils/handleError.ts: -------------------------------------------------------------------------------- 1 | import { errorCodes, getMessageFromCode, serializeError } from 'eth-rpc-errors'; 2 | import toast from 'react-hot-toast'; 3 | 4 | export default function handleError(error: any, id: string) { 5 | console.error(error); 6 | 7 | let errorMessage = error?.message ?? 'Something Went Wrong'; 8 | 9 | if ('code' in error) { 10 | const _error = serializeError(error); 11 | 12 | console.error('Serialized Error:', _error); 13 | 14 | if (_error.code === errorCodes.provider.userRejectedRequest) { 15 | toast.dismiss(id); 16 | 17 | return; 18 | } else { 19 | errorMessage = getMessageFromCode(_error.code); 20 | 21 | toast.error(errorMessage, { id }); 22 | } 23 | } else { 24 | toast.error(errorMessage, { id }); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /utils/hexlify.ts: -------------------------------------------------------------------------------- 1 | export default function hexlify(message: string) { 2 | return '0x' + Buffer.from(message, 'utf8').toString('hex'); 3 | } 4 | -------------------------------------------------------------------------------- /utils/isEqual.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Case-insensitive string comparison 3 | */ 4 | export default function isEqual(a: string, b: string) { 5 | return a.localeCompare(b, undefined, { sensitivity: 'accent' }) === 0; 6 | } 7 | -------------------------------------------------------------------------------- /utils/isMobile.ts: -------------------------------------------------------------------------------- 1 | import isMobileJs from 'ismobilejs'; 2 | 3 | const isMobile = isMobileJs().phone; 4 | 5 | export default isMobile; 6 | -------------------------------------------------------------------------------- /utils/normalizeChainId.ts: -------------------------------------------------------------------------------- 1 | export default function normalizeChainId(chainId: string | number): number { 2 | if (typeof chainId === 'string') { 3 | const parsedChainId = Number.parseInt( 4 | chainId, 5 | chainId.trim().substring(0, 2) === '0x' ? 16 : 10, 6 | ); 7 | 8 | if (Number.isNaN(parsedChainId)) 9 | throw new Error(`chainId ${chainId} is not an integer`); 10 | 11 | return parsedChainId; 12 | } else { 13 | if (!Number.isInteger(chainId)) 14 | throw new Error(`chainId ${chainId} is not an integer`); 15 | 16 | return chainId; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /utils/shortenAddress.ts: -------------------------------------------------------------------------------- 1 | export default function shortenAddress(address: string, chars = 4): string { 2 | const parsed = address; 3 | 4 | if (!parsed) { 5 | throw Error(`Invalid 'address' parameter '${address}'.`); 6 | } 7 | return `${parsed.substring(0, chars + 2)}...${parsed.substring(42 - chars)}`; 8 | } 9 | -------------------------------------------------------------------------------- /vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "headers": [ 3 | { 4 | "source": "/sw.js", 5 | "headers": [ 6 | { 7 | "key": "Cache-Control", 8 | "value": "public, max-age=0, must-revalidate" 9 | } 10 | ] 11 | }, 12 | { 13 | "source": "/(.*)", 14 | "headers": [ 15 | { 16 | "key": "X-XSS-Protection", 17 | "value": "1; mode=block" 18 | }, 19 | { 20 | "key": "X-Content-Type-Options", 21 | "value": "nosniff" 22 | } 23 | ] 24 | } 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /views/mint.tsx: -------------------------------------------------------------------------------- 1 | import { tabClassNames, tabPanelClassNames } from '@/components/tabs'; 2 | 3 | import MintDeposit from '@/components/mint/deposit'; 4 | import Panel from '@/components/panel'; 5 | import { Tab } from '@headlessui/react'; 6 | import WithdrawStake from '@/components/stake/withdrawStake'; 7 | 8 | const TAB_KEYS = { 9 | DEPOSIT: 'Deposit', 10 | WITHDRAW: 'Withdraw', 11 | MINTDEPOSIT: 'Staking', 12 | }; 13 | 14 | function MintView() { 15 | return ( 16 |
17 |
18 | 19 | 20 | 21 | {TAB_KEYS.MINTDEPOSIT} 22 | 23 | 24 | 25 | {TAB_KEYS.WITHDRAW} 26 | 27 | 28 | 29 | 30 | 35 | 36 | 37 | 38 | 43 | 44 | 45 |
46 |
47 | ); 48 | } 49 | 50 | export default MintView; 51 | -------------------------------------------------------------------------------- /views/stake.tsx: -------------------------------------------------------------------------------- 1 | import { tabClassNames, tabPanelClassNames } from '@/components/tabs'; 2 | 3 | import DepositStake from '@/components/stake/depositStake'; 4 | import MintDeposit from '@/components/mint/deposit'; 5 | import Panel from '@/components/panel'; 6 | import { Tab } from '@headlessui/react'; 7 | import WithdrawStake from '@/components/stake/withdrawStake'; 8 | 9 | const TAB_KEYS = { 10 | DEPOSIT: 'Deposit', 11 | WITHDRAW: 'Withdraw', 12 | MINT: 'Staking', 13 | }; 14 | 15 | function StakeView() { 16 | return ( 17 | <> 18 |
19 |
20 | 21 | 22 | 23 | {TAB_KEYS.DEPOSIT} 24 | 25 | 26 | 27 | {TAB_KEYS.WITHDRAW} 28 | 29 | 30 | 31 | {TAB_KEYS.MINT} 32 | 33 | 34 | 35 | 36 | 41 | 42 | 43 | 44 | 49 | 50 | 51 | 52 | 57 | 58 | 59 | 60 | 61 |
62 |
63 |
64 |
65 | FOLD/XFOLD Staking is deprecated! 66 |

Dapp has been switched to withdrawal-only mode

67 |

Stay tuned for V2

68 |
69 |
70 | 71 | ); 72 | } 73 | 74 | export default StakeView; 75 | --------------------------------------------------------------------------------