diff --git a/.env b/.env index b177495..17745f4 100644 --- a/.env +++ b/.env @@ -1,26 +1,7 @@ -VITE_BASE_URL= -VITE_TRADE_URL= -VITE_DOCS_URL= -VITE_MINTSCAN_URL= -VITE_ETHERSCAN_URL= - -VITE_ALCHEMY_API_KEY= -VITE_WALLETCONNECT2_PROJECT_ID= -VITE_PK_ENCRYPTION_KEY= - -VITE_STAKING_LEARN_MORE_LINK= -VITE_LAUNCH_BLOG_POST_LINK= - -VITE_NETWORK_ENVIRONMENT= -VITE_NETWORK_INDEXER_REST_ENDPOINT= -VITE_NETWORK_INDEXER_WS_ENDPOINT= -VITE_NEWORK_VALIDATOR_REST_ENDPOINT= -VITE_NETWORK_CHAIN_ID= -VITE_NETWORK_USDC_DENOM= -VITE_NETWORK_USDC_DECIMALS= - -VITE_ETH_CHAIN_ID= -VITE_ETH_DYDX_ADDRESSS= -VITE_BRIDGE_CONTRACT_ADDRESS= -VITE_DYDX_DENOM= -VITE_DYDX_DECIMALS= \ No newline at end of file +VITE_DOCS_URL=https://docs.dorafactory.org/docs +VITE_ETHERSCAN_URL=https://sepolia.etherscan.io +VITE_DORA_EXPLORER_URL=https://vota-testnet-explorer.dorafactory.org/doravotatestnet +VITE_INFURA_API_KEY=bee5140d9d2243d580ef49ade75c2356 +VITE_ETH_CHAIN_ID=11155111 +VITE_ETH_DORA_ADDRESSS=0xe0A1A9e1a0704CE7B6c73988dbb4E2a54B846791 +VITE_BRIDGE_CONTRACT_ADDRESS=0x775cB1bB3101A3881D093A2e1e1D0A7135809EDA \ No newline at end of file diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml new file mode 100644 index 0000000..28dcd17 --- /dev/null +++ b/.github/workflows/cicd.yml @@ -0,0 +1,42 @@ +name: Vota bridge CICD + +on: + push: + branches: [dora-migrate-vota] + +jobs: + build: + runs-on: ubuntu-latest + permissions: + id-token: write + contents: read + steps: + - name: Git clone the repository + uses: actions/checkout@v4 + - name: Build image + env: + BUILD_ARG: ${{ github.ref_name }} + run: | + docker build -t vota-bridge-ui:local -f Dockerfile . + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@master + with: + role-to-assume: ${{ secrets.AWS_ROLE }} + aws-region: ap-southeast-1 + - name: Login to Amazon ECR + id: login-ecr + uses: aws-actions/amazon-ecr-login@v2 + with: + mask-password: 'true' + - name: Tag, push image to Amazon ECR + env: + ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} + ECR_REPOSITORY: vota-bridge-ui + IMAGE_TAG: ${{ github.ref_name }} + run: | + docker tag vota-bridge-ui:local $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG + docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG + - name: Redeploy on aws + run: | + aws eks update-kubeconfig --region ap-southeast-1 --name ${{ secrets.EKS_CLUSTER }} + kubectl rollout restart deploy vota-bridge-ui-dev diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..1334072 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,19 @@ +FROM node:18-alpine AS build + +WORKDIR /app + +COPY . . +RUN npm i -g pnpm +RUN pnpm install +RUN pnpm run build + +FROM nginx:alpine +COPY --from=build /app/dist /usr/share/nginx/html + +# optional nginx.conf +# COPY nginx.conf /etc/nginx/nginx.conf + +EXPOSE 80 + +# 启动 Nginx +CMD ["nginx", "-g", "daemon off;"] diff --git a/index.html b/index.html index 98140cd..f81c5ba 100644 --- a/index.html +++ b/index.html @@ -5,7 +5,7 @@ - dYdX · Migrate + DORA Token Migration diff --git a/package.json b/package.json index de5fa03..9155889 100644 --- a/package.json +++ b/package.json @@ -37,11 +37,13 @@ "@react-stately/table": "^3.9.1", "@reduxjs/toolkit": "^1.9.5", "autoprefixer": "10.4.15", + "bech32": "^1.1.4", "bignumber.js": "^9.1.1", "buffer": "^6.0.3", "crypto-js": "^4.1.1", "eslint": "8.47.0", "eslint-config-next": "13.4.19", + "ethers": "^6.13.1", "lodash": "^4.17.21", "luxon": "^3.4.3", "postcss": "8.4.28", @@ -54,6 +56,8 @@ "react-router-dom": "^6.16.0", "react-stately": "^3.23.0", "reselect": "^4.1.8", + "stream": "^0.0.3", + "stream-browserify": "^3.0.0", "styled-components": "^5.3.11", "typescript": "5.1.6", "vite-plugin-svgr": "^3.2.0", @@ -62,6 +66,7 @@ "devDependencies": { "@react-types/shared": "^3.20.0", "@react-types/table": "^3.8.1", + "@types/bech32": "^1.1.4", "@types/crypto-js": "^4.1.2", "@types/lodash": "^4.14.197", "@types/luxon": "^3.3.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 78190e8..5ff8a77 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,5 +1,9 @@ lockfileVersion: '6.0' +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + dependencies: '@cosmjs/encoding': specifier: ^0.31.1 @@ -70,6 +74,9 @@ dependencies: autoprefixer: specifier: 10.4.15 version: 10.4.15(postcss@8.4.28) + bech32: + specifier: ^1.1.4 + version: 1.1.4 bignumber.js: specifier: ^9.1.1 version: 9.1.1 @@ -85,6 +92,9 @@ dependencies: eslint-config-next: specifier: 13.4.19 version: 13.4.19(eslint@8.47.0)(typescript@5.1.6) + ethers: + specifier: ^6.13.1 + version: 6.13.2 lodash: specifier: ^4.17.21 version: 4.17.21 @@ -121,6 +131,12 @@ dependencies: reselect: specifier: ^4.1.8 version: 4.1.8 + stream: + specifier: ^0.0.3 + version: 0.0.3 + stream-browserify: + specifier: ^3.0.0 + version: 3.0.0 styled-components: specifier: ^5.3.11 version: 5.3.11(@babel/core@7.23.0)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0) @@ -141,6 +157,9 @@ devDependencies: '@react-types/table': specifier: ^3.8.1 version: 3.8.1(react@18.2.0) + '@types/bech32': + specifier: ^1.1.4 + version: 1.1.4 '@types/crypto-js': specifier: ^4.1.2 version: 4.1.2 @@ -202,8 +221,8 @@ packages: resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==} engines: {node: '>=0.10.0'} - /@adraffy/ens-normalize@1.10.0: - resolution: {integrity: sha512-nA9XHtlAkYfJxY7bce8DcN7eKxWWCWkU+1GR9d+U6MbNpfwQp8TI7vqOsBsMcHoT4mBu2kypKoSKnghEzOOq5Q==} + /@adraffy/ens-normalize@1.10.1: + resolution: {integrity: sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==} dev: false /@adraffy/ens-normalize@1.9.4: @@ -718,7 +737,7 @@ packages: bech32: 1.1.4 bignumber.js: 9.1.1 ethereum-cryptography: 2.1.2 - ethers: 6.8.0 + ethers: 6.13.2 long: 4.0.0 protobufjs: 7.2.5 ws: 8.14.2(bufferutil@4.0.7)(utf-8-validate@5.0.10) @@ -4068,6 +4087,13 @@ packages: use-sync-external-store: 1.2.0(react@18.2.0) dev: false + /@types/bech32@1.1.4: + resolution: {integrity: sha512-LWSo7sw6NFc9u7HpMK6aPRa7lNw3NKYAEEQCvdFFLkMCgwzv+0/CySQeqdL9hUIsirThQJ4u6ax/0uKEN63Z2g==} + deprecated: This is a stub types definition. bech32 provides its own type definitions, so you do not need this installed. + dependencies: + bech32: 1.1.4 + dev: true + /@types/connect@3.4.36: resolution: {integrity: sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w==} dependencies: @@ -5048,7 +5074,6 @@ packages: /bech32@1.1.4: resolution: {integrity: sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==} - dev: false /big-integer@1.6.51: resolution: {integrity: sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==} @@ -5241,6 +5266,11 @@ packages: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} dev: false + /component-emitter@2.0.0: + resolution: {integrity: sha512-4m5s3Me2xxlVKG9PkZpQqHQR7bgpnN7joDMJ4yvVkVXngjoITG76IaZmzmywSeRTeTpc6N6r3H3+KyUurV8OYw==} + engines: {node: '>=18'} + dev: false + /concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} @@ -5962,17 +5992,17 @@ packages: '@scure/bip39': 1.2.1 dev: false - /ethers@6.8.0: - resolution: {integrity: sha512-zrFbmQRlraM+cU5mE4CZTLBurZTs2gdp2ld0nG/f3ecBK+x6lZ69KSxBqZ4NjclxwfTxl5LeNufcBbMsTdY53Q==} + /ethers@6.13.2: + resolution: {integrity: sha512-9VkriTTed+/27BGuY1s0hf441kqwHJ1wtN2edksEtiRvXx+soxRX3iSXTfFqq2+YwrOqbDoTHjIhQnjJRlzKmg==} engines: {node: '>=14.0.0'} dependencies: - '@adraffy/ens-normalize': 1.10.0 + '@adraffy/ens-normalize': 1.10.1 '@noble/curves': 1.2.0 '@noble/hashes': 1.3.2 '@types/node': 18.15.13 aes-js: 4.0.0-beta.5 tslib: 2.4.0 - ws: 8.5.0 + ws: 8.17.1 transitivePeerDependencies: - bufferutil - utf-8-validate @@ -7782,6 +7812,12 @@ packages: resolution: {integrity: sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==} dev: false + /stream@0.0.3: + resolution: {integrity: sha512-aMsbn7VKrl4A2T7QAQQbzgN7NVc70vgF5INQrBXqn4dCXN1zy3L9HGgLO5s7PExmdrzTJ8uR/27aviW8or8/+A==} + dependencies: + component-emitter: 2.0.0 + dev: false + /strict-uri-encode@2.0.0: resolution: {integrity: sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==} engines: {node: '>=4'} @@ -8405,12 +8441,12 @@ packages: utf-8-validate: 5.0.10 dev: false - /ws@8.5.0: - resolution: {integrity: sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==} + /ws@8.17.1: + resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 - utf-8-validate: ^5.0.2 + utf-8-validate: '>=5.0.2' peerDependenciesMeta: bufferutil: optional: true diff --git a/public/currencies/dora.png b/public/currencies/dora.png new file mode 100644 index 0000000..e526370 Binary files /dev/null and b/public/currencies/dora.png differ diff --git a/public/favicon.svg b/public/favicon.svg index d6b2f8f..3a4da46 100644 --- a/public/favicon.svg +++ b/public/favicon.svg @@ -1,23 +1,17 @@ - - - - - - - - - - - - - - - - - - - + + + + + + + \ No newline at end of file diff --git a/src/App.tsx b/src/App.tsx index 7a8401d..d9f6df7 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -62,15 +62,13 @@ const Content = () => { items={[ { value: MigrateTabs.Migrate, - label: stringGetter({ key: STRING_KEYS.MIGRATE }), + label: "Migrate", forceMount: true, content: , }, { value: MigrateTabs.PendingMigrations, - label: stringGetter({ - key: STRING_KEYS.PENDING_MIGRATIONS, - }), + label: 'Pending Migrations', forceMount: true, content: , }, diff --git a/src/components/AssetIcon.tsx b/src/components/AssetIcon.tsx index 968e92a..cade89c 100644 --- a/src/components/AssetIcon.tsx +++ b/src/components/AssetIcon.tsx @@ -5,6 +5,7 @@ export type AssetSymbol = keyof typeof assetIcons; const assetIcons = { DYDX: '/currencies/dydx.png', ETH: '/currencies/eth.png', + DORA: '/currencies/dora.png' } as const; const isAssetSymbol = (symbol?: string): symbol is AssetSymbol => diff --git a/src/components/CopyButton.tsx b/src/components/CopyButton.tsx index 1194f79..8f26c4f 100644 --- a/src/components/CopyButton.tsx +++ b/src/components/CopyButton.tsx @@ -54,7 +54,7 @@ export const CopyButton = ({ onClick={onCopy} > - {children ?? stringGetter({ key: copied ? STRING_KEYS.COPIED : STRING_KEYS.COPY })} + {children ?? (copied ? 'Copied' : 'Copy')} ); }; diff --git a/src/components/Icon.tsx b/src/components/Icon.tsx index 8b55fee..34e2cb3 100644 --- a/src/components/Icon.tsx +++ b/src/components/Icon.tsx @@ -43,6 +43,7 @@ export enum IconName { Close = 'Close', Copy = 'Copy', Discord = 'Discord', + Explorer = 'Dora vota Explorer', ExportKeys = 'ExportKeys', File = 'File', HelpCircle = 'HelpCircle', diff --git a/src/components/TimeoutButton.tsx b/src/components/TimeoutButton.tsx index 01879c8..b1db08e 100644 --- a/src/components/TimeoutButton.tsx +++ b/src/components/TimeoutButton.tsx @@ -23,6 +23,14 @@ export const TimeoutButton = ({ const secondsLeft = Math.max(0, (timeoutDeadline - now) / 1000); + const getWaitSecondsMessage = (secondsLeft: number) => { + const seconds = Math.ceil(secondsLeft); + return seconds === 1 + ? `Wait for ${seconds} second` + : `Wait for ${seconds} seconds`; + }; + const waitSecondsMessage = secondsLeft ? getWaitSecondsMessage(secondsLeft) : children; + return ( ); }; diff --git a/src/constants/abi/bridgeContractAbi.json b/src/constants/abi/bridgeContractAbi.json index 7879083..8984f9d 100644 --- a/src/constants/abi/bridgeContractAbi.json +++ b/src/constants/abi/bridgeContractAbi.json @@ -1,44 +1,80 @@ [ { - "anonymous": false, "inputs": [ { - "indexed": true, - "internalType": "uint256", - "name": "id", - "type": "uint256" + "internalType": "contract ERC20", + "name": "_token", + "type": "address" }, { - "indexed": false, + "internalType": "address", + "name": "_admin", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, "internalType": "uint256", - "name": "amount", + "name": "idx", "type": "uint256" }, { - "indexed": false, + "indexed": true, "internalType": "address", - "name": "from", + "name": "eoa", "type": "address" }, { - "indexed": false, - "internalType": "bytes", - "name": "accAddress", - "type": "bytes" + "indexed": true, + "internalType": "address", + "name": "vota", + "type": "address" }, { "indexed": false, - "internalType": "bytes", - "name": "data", - "type": "bytes" + "internalType": "uint256", + "name": "amount", + "type": "uint256" } ], - "name": "Bridge", + "name": "Submit", "type": "event" }, { "inputs": [], - "name": "_nextAvailableBridgeId", + "name": "admin", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "amountThreshold", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "processedRecords", "outputs": [ { "internalType": "uint256", @@ -51,14 +87,112 @@ }, { "inputs": [], - "name": "_token", + "name": "token", + "outputs": [ + { + "internalType": "contract ERC20", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalRecords", "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ { "internalType": "address", + "name": "_admin", + "type": "address" + } + ], + "name": "changeAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_idx", + "type": "uint256" + } + ], + "name": "record", + "outputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "vota", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "txHash", + "type": "bytes32" + } + ], + "internalType": "struct DoraBridge.Record", "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_user", "type": "address" } ], + "name": "recordOf", + "outputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "vota", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "txHash", + "type": "bytes32" + } + ], + "internalType": "struct DoraBridge.Record[]", + "name": "results", + "type": "tuple[]" + } + ], "stateMutability": "view", "type": "function" }, @@ -66,21 +200,57 @@ "inputs": [ { "internalType": "uint256", - "name": "amount", + "name": "_count", + "type": "uint256" + } + ], + "name": "getUnprocessedRecords", + "outputs": [ + { + "internalType": "uint256", + "name": "size", "type": "uint256" }, { - "internalType": "bytes", - "name": "accAddress", - "type": "bytes" + "components": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "vota", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "txHash", + "type": "bytes32" + } + ], + "internalType": "struct DoraBridge.Record[]", + "name": "records", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" }, { - "internalType": "bytes", - "name": "memo", - "type": "bytes" + "internalType": "address", + "name": "_votaAddr", + "type": "address" } ], - "name": "bridge", + "name": "submit", "outputs": [], "stateMutability": "nonpayable", "type": "function" @@ -88,14 +258,19 @@ { "inputs": [ { - "internalType": "address", - "name": "token", - "type": "address" + "internalType": "bytes32", + "name": "_txHash", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_count", + "type": "uint256" } ], - "name": "initialize", + "name": "process", "outputs": [], "stateMutability": "nonpayable", "type": "function" } -] +] \ No newline at end of file diff --git a/src/constants/abi/ethINFContractAbi.json b/src/constants/abi/ethINFContractAbi.json new file mode 100644 index 0000000..11e9e2a --- /dev/null +++ b/src/constants/abi/ethINFContractAbi.json @@ -0,0 +1,235 @@ +[ + { + "inputs": [ + { + "internalType": "uint256", + "name": "initBalance", + "type": "uint256" + } + ], + "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": "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": "decimals", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_owner", + "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": "_value", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_from", + "type": "address" + }, + { + "internalType": "address", + "name": "_to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_value", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_value", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_owner", + "type": "address" + }, + { + "internalType": "address", + "name": "_spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "remaining", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } + ] \ No newline at end of file diff --git a/src/constants/abi/index.ts b/src/constants/abi/index.ts index 13b4b55..783d8b4 100644 --- a/src/constants/abi/index.ts +++ b/src/constants/abi/index.ts @@ -1,5 +1,6 @@ import bridgeContractAbi from './bridgeContractAbi.json'; import ethDYDXContractAbi from './ethDYDXContractAbi.json'; +import ethINFContractAbi from './ethDYDXContractAbi.json'; import wethDYDXContractAbi from './wethDYDXContractAbi.json'; -export { bridgeContractAbi, ethDYDXContractAbi, wethDYDXContractAbi }; +export { bridgeContractAbi, ethDYDXContractAbi, wethDYDXContractAbi, ethINFContractAbi }; diff --git a/src/constants/account.ts b/src/constants/account.ts index ef45ec5..c2ac4bb 100644 --- a/src/constants/account.ts +++ b/src/constants/account.ts @@ -31,12 +31,12 @@ export enum EvmDerivedAccountStatus { Derived, } -import type { DydxAddress, EthereumAddress } from './wallets'; +import type { DoraAddress, EthereumAddress } from './wallets'; export type EvmDerivedAddresses = { version?: string; [EthereumAddress: EthereumAddress]: { encryptedSignature?: string; - dydxAddress?: DydxAddress; + DoraAddress?: DoraAddress; }; }; diff --git a/src/constants/links.ts b/src/constants/links.ts index 041f48e..4bc9ed3 100644 --- a/src/constants/links.ts +++ b/src/constants/links.ts @@ -1,36 +1,24 @@ import { IconName } from '@/components/Icon'; -import { STRING_KEYS } from './localization'; +import { STRING_KEYS, DORA_KEYS } from './localization'; import { AppRoute } from './routes'; export const RELEVANT_LINKS = [ { - value: 'DOCUMENTATION', - iconName: IconName.Terminal, - labelStringKey: STRING_KEYS.DOCUMENTATION, - href: import.meta.env.VITE_DOCS_URL, - }, - { - value: 'MINTSCAN', + value: 'DORA_EXPLORER', iconName: IconName.Mintscan, - labelStringKey: STRING_KEYS.MINTSCAN, - href: import.meta.env.VITE_MINTSCAN_URL, - }, - { - value: 'COMMUNITY', - iconName: IconName.Discord, - labelStringKey: STRING_KEYS.COMMUNITY, - href: 'https://discord.gg/dydx', + labelStringKey: DORA_KEYS.DORA_EXPLORER, + href: import.meta.env.VITE_DORA_EXPLORER_URL, }, { value: 'TERMS_OF_USE', iconName: IconName.File, - labelStringKey: STRING_KEYS.TERMS_OF_USE, + labelStringKey: 'Terms of Use', href: AppRoute.Terms, }, { value: 'PRIVACY_POLICY', iconName: IconName.Privacy, - labelStringKey: STRING_KEYS.PRIVACY_POLICY, + labelStringKey: 'Private Policy', href: AppRoute.Privacy, }, ]; diff --git a/src/constants/localStorage.ts b/src/constants/localStorage.ts index 4f1f581..2626a38 100644 --- a/src/constants/localStorage.ts +++ b/src/constants/localStorage.ts @@ -1,7 +1,7 @@ export enum LocalStorageKey { // Onboarding / Accounts EvmAddress = 'dydx.migrate.EvmAddress', - DydxAddress = 'dydx.migrate.DydxAddress', + DoraAddress = 'dydx.migrate.DoraAddress', OnboardingSelectedWalletType = 'dydx.migrate.OnboardingSelectedWalletType', WalletConnectionType = 'dydx.migrate.WalletConnectionType', OnboardingHasAcknowledgedTerms = 'dydx.migrate.OnboardingHasAcknowledgedTerms', diff --git a/src/constants/localization.ts b/src/constants/localization.ts index 30b9f4d..e366ba5 100644 --- a/src/constants/localization.ts +++ b/src/constants/localization.ts @@ -13,15 +13,53 @@ export { TOOLTIP_STRING_KEYS } from '@dydxprotocol/v4-localization'; export enum SupportedLocales { EN = 'en', - ZH_CN = 'zh-CN', - JA = 'ja', - KO = 'ko', - RU = 'ru', - TR = 'tr', - FR = 'fr', - PT = 'pt', - ES = 'es', - DE = 'de', + // ZH_CN = 'zh-CN', + // JA = 'ja', + // KO = 'ko', + // RU = 'ru', + // TR = 'tr', + // FR = 'fr', + // PT = 'pt', + // ES = 'es', + // DE = 'de', +} + +export const DORA_KEYS = { + "DORA_CHAIN_SETTLEMENT": 'Dora Vota settlement', + "DORA_EXPLORER" : 'Dora Vota Explorer', + "VOTA_CHAIN" : 'Dora Vota', + "ETHEREUM": 'Ethereum' +} + +export const DORA_LONG_SENTENCE = { + "CONFIRM_MIGRATION_DISCLAIMER_1": "I understand it may take 24~48 hours until my tokens are available on the Dora Vota.", + "CONFIRM_MIGRATION_DISCLAIMER_2": "I understand that my ethDORA tokens will be sent to a black hole address for permanent destruction in the Bridge Smart Contract and irrecoverable.", + "PENDING_MIGRATIONS_DESCRIPTION_I": 'This table lists all ongoing DORA token migrations from Ethereum to the Dora Vota. It refreshes periodically or upon a manual page reload.', + "PENDING_MIGRATIONS_DESCRIPTION_II": 'Once a pending migration is listed, the migration will be completed within ~48 hours. Once the migration has been settled, it will be removed from this table during the next automatic update or when the page is manually refreshed.', + "PENDING_MIGRATIONS_DESCRIPTION_III": 'However, you can see all your migration records (including those that are pending or have been settled) in the "Mine" option. Please note that if the migration is pending, the Transaction Hash will display as 0x0000...00000. If your migration is settled, it will display the link of transaction hash of the successful migration on the Dora Vota.', + "MIGRATION_FAQ_DORA_CHAIN_PORTAL": "What is the Dora Vota Migration Portal?", + "MIGRATION_FAQ_DORA_CHAIN_PORTAL_ANSWER": "The Dora Factory community is migrating the Ethereum ERC-20 DORA tokens (ethDORA) to the Dora Vota appchain. This portal assists ethDORA holders in engaging with the Ethereum smart contract to migrate their DORA tokens from Ethereum to the Dora Vota appchain.", + "MIGRATION_FAQ_WHAT_ADDRESS_CAN_INTERACT": "What addresses can interact with the portal to migrate ethDORA to Dora Vota Chain?", + "MIGRATION_FAQ_WHAT_ADDRESS_CAN_INTERACT_ANSWER": "Only Ethereum addresses holding Ethereum ERC20 DORA can successfully interact with the token migration smart contract to proceed with the token migration.", + "MIGRATION_FAQ_GAS_FEES_ANSWER": "Please note that a gas fee will be charged during the token migration process. The user will pay all gas fees incurred.", + "MIGRATION_FAQ_GAS_FEES": "Who will be paying the migration gas fees?", + "MIGRATION_FAQ_HOW_LONG_ANSWER": "The migration will be finished within 48 hours. Note that the migration time is subject to change. As more addresses complete token migration, expected token migration time will prolong.", + "MIGRATION_FAQ_HOW_LONG": "How long should the migration take?", + "MIGRATION_FAQ_CUSTODY_TOKEN": "Will the token migration portal custody my tokens?", + "MIGRATION_FAQ_CUSTODY_TOKEN_ANSWER": "Please note that the token migration portal and the smart contracts are non-custodial. You will interact with the smart contract to receive DORA on the Dora Vota Chain. After migration, your ethDORA tokens will be burnt (sent to a null address).", + "MIGRATION_FAQ_CONTRACR_TOKEN": "What should I do if my ethDORA tokens are in a Contract address/Safe?", + "MIGRATION_FAQ_CONTRACR_TOKEN_ANSWER":"If your ethDORA tokens are in a Contract address, please contact @doravota on Telegram. We can help you manually complete the migration process. If your ethDORA tokens are in an Ethereum Gnosis Safe, please transfer them to an EOA address to engage with the migration portal and proceed with the migration. Please make sure you transfer the tokens securely, and you will be solely responsible for the operation.", + "MIGRATION_FAQ_VCDORA_HOLDER": "What will happen for vcDORA holders?", + "MIGRATION_FAQ_VCDORA_HOLDER_ANSWER": "vcDORA credits on the vcDORA Ethereum smart contract will still be valid until all DORA tokens staked in the vcDORA smart contract exit. Once you can withdraw your ethDORA from the vcDORA smart contract, you can immediately engage with the migration portal. If you still cannot withdraw from the vcDORA smart contract, please don’t worry as the vcDORA contract will remain and be maintained on Eththeum.", + "MIGRATION_FAQ_HOW_TO_TRACK_ANSWER": "Please use https://vota-migration.dorafactory.org/ to track migration status.", + "MIGRATION_FAQ_HOW_TO_TRACK": "How can I track the migration status?", + "MIGRATION_FAQ_WHAT_ADDRESS_ANSWER": "Users who interact with the portal can send dYdX Chain DYDX tokens to any dYdX Chain address. User’s can send tokens directly to their dYdX Chain address that is automatically created from their Ethereum address’s signature.", + "MIGRATION_FAQ_WHAT_ADDRESS": "What address can I send dYdX Chain DYDX to?", + "MIGRATION_FAQ_WHAT_TOKENS_ANSWER": "Holders who successfully engage with the portal will receive wethDYDX on Ethereum and DYDX on dYdX Chain.", + "MIGRATION_FAQ_WHAT_TOKENS": "What tokens will I receive?", + "MIGRATION_FAQ_WRAPPED_TOKENS_ANSWER": "wethDYDX are minted 1:1 to any users who successfully send Ethereum-based DYDX to the smart contract. wethDYDX have the same v3 governance rights as Ethereum-based DYDX, and are transferable. wethDYDX cannot be bridged to v4.", + "MIGRATION_FAQ_WRAPPED_TOKENS": "What are wrapped Ethereum DYDX tokens (“wethDYDX”)?", + "MIGRATION_BLOCKED_MESSAGE_DESTINATION": "Because the destination address appears to be a resident of, or using this user interface from, a jurisdiction that violates our terms of use, or has engaged in activity that violates our terms of use, the destination address has been blocked and this transaction cannot be completed.", } export const EN_LOCALE_DATA = { @@ -49,28 +87,28 @@ export const SUPPORTED_LOCALE_STRING_LABELS: { [key in SupportedLocales]: string; } = { [SupportedLocales.EN]: 'English', - [SupportedLocales.ZH_CN]: '中文', - [SupportedLocales.JA]: '日本語', - [SupportedLocales.KO]: '한국어', - [SupportedLocales.RU]: 'русский', - [SupportedLocales.TR]: 'Türkçe', - [SupportedLocales.FR]: 'Français', - [SupportedLocales.PT]: 'Português', - [SupportedLocales.ES]: 'Español', - [SupportedLocales.DE]: 'Deutsch', + // [SupportedLocales.ZH_CN]: '中文', + // [SupportedLocales.JA]: '日本語', + // [SupportedLocales.KO]: '한국어', + // [SupportedLocales.RU]: 'русский', + // [SupportedLocales.TR]: 'Türkçe', + // [SupportedLocales.FR]: 'Français', + // [SupportedLocales.PT]: 'Português', + // [SupportedLocales.ES]: 'Español', + // [SupportedLocales.DE]: 'Deutsch', }; export const SUPPORTED_LOCALE_BASE_TAGS = { [SupportedLocales.EN]: 'en', - [SupportedLocales.ZH_CN]: 'zh', - [SupportedLocales.JA]: 'ja', - [SupportedLocales.KO]: 'ko', - [SupportedLocales.RU]: 'ru', - [SupportedLocales.TR]: 'tr', - [SupportedLocales.FR]: 'fr', - [SupportedLocales.PT]: 'pt', - [SupportedLocales.ES]: 'es', - [SupportedLocales.DE]: 'de', + // [SupportedLocales.ZH_CN]: 'zh', + // [SupportedLocales.JA]: 'ja', + // [SupportedLocales.KO]: 'ko', + // [SupportedLocales.RU]: 'ru', + // [SupportedLocales.TR]: 'tr', + // [SupportedLocales.FR]: 'fr', + // [SupportedLocales.PT]: 'pt', + // [SupportedLocales.ES]: 'es', + // [SupportedLocales.DE]: 'de', }; export const SUPPORTED_BASE_TAGS_LOCALE_MAPPING = Object.fromEntries( diff --git a/src/constants/migrate.ts b/src/constants/migrate.ts index 6578dea..20dddec 100644 --- a/src/constants/migrate.ts +++ b/src/constants/migrate.ts @@ -1,5 +1,5 @@ import BigNumber from 'bignumber.js'; -import { DydxAddress } from './wallets'; +import { DoraAddress } from './wallets'; export const DYDX_CHAIN_ESTIMATED_BLOCK_TIME_MS = 1_500; export const TOKEN_DECIMAL_SHIFT = 18; @@ -30,9 +30,9 @@ export enum DestinationAddressOptions { export type PendingMigrationData = { id: number; - blockHeight: number; - address: DydxAddress; + address: DoraAddress; amount: BigNumber; + txHash: String }; export enum PendingMigrationFilter { diff --git a/src/constants/wallets.ts b/src/constants/wallets.ts index c120a70..37a18f9 100644 --- a/src/constants/wallets.ts +++ b/src/constants/wallets.ts @@ -92,7 +92,7 @@ export const WALLET_CONNECT_EXPLORER_RECOMMENDED_IDS = Object.values( export const wallets: Record = { [WalletType.OtherWallet]: { type: WalletType.OtherWallet, - stringKey: STRING_KEYS.OTHER_WALLET, + stringKey: "Other", icon: GenericWalletIcon, connectionTypes: [WalletConnectionType.InjectedEip1193, WalletConnectionType.WalletConnect2], matchesInjectedEip1193: (provider) => @@ -105,14 +105,14 @@ export const wallets: Record = { }, [WalletType.CoinbaseWallet]: { type: WalletType.CoinbaseWallet, - stringKey: STRING_KEYS.COINBASE_WALLET, + stringKey: "Coinbase Wallet", icon: CoinbaseIcon, connectionTypes: [WalletConnectionType.CoinbaseWalletSdk, WalletConnectionType.InjectedEip1193], matchesInjectedEip1193: (provider) => provider.isCoinbaseWallet, }, [WalletType.MetaMask]: { type: WalletType.MetaMask, - stringKey: STRING_KEYS.METAMASK, + stringKey: "MetaMask", icon: MetaMaskIcon, connectionTypes: [WalletConnectionType.InjectedEip1193, WalletConnectionType.WalletConnect2], matchesInjectedEip1193: isMetaMask, @@ -120,7 +120,7 @@ export const wallets: Record = { }, [WalletType.WalletConnect2]: { type: WalletType.WalletConnect2, - stringKey: STRING_KEYS.WALLET_CONNECT_2, + stringKey: "WalletConnect", icon: WalletConnectIcon, connectionTypes: [WalletConnectionType.WalletConnect2], }, @@ -165,19 +165,19 @@ export const COSMOS_DERIVATION_PATH = "m/44'/118'/0'/0/0"; * @description typed data to sign for dYdX Chain onboarding */ export const SIGN_TYPED_DATA = { - primaryType: 'dYdX', + primaryType: 'dYdx', domain: { - name: 'dYdX Chain', + name: 'Dora Vota', }, types: { - dYdX: [{ name: 'action', type: 'string' }], + dYdx: [{ name: 'action', type: 'string' }], }, message: { - action: 'dYdX Chain Onboarding', + action: 'DORA Token Migration Onboarding', }, } as const; export type PrivateInformation = ReturnType; export type EthereumAddress = `0x${string}`; -export type DydxAddress = `dydx${string}`; +export type DoraAddress = `dora${string}`; diff --git a/src/hooks/index.ts b/src/hooks/index.ts index fd43ccf..7362ca6 100644 --- a/src/hooks/index.ts +++ b/src/hooks/index.ts @@ -4,7 +4,7 @@ import { useBreakpoints } from './useBreakpoints'; import { useDialogArea } from './useDialogArea'; import { useDydxClient } from './useDydxClient'; import { useInitializePage } from './useInitializePage'; -import { useIsDydxAddressValid } from './useIsDydxAddressValid'; +import { useIsDoraAddressValid } from './useIsDoraAddressValid'; import { useLocaleSeparators } from './useLocaleSeparators'; import { useLocalStorage } from './useLocalStorage'; import { useMatchingEvmNetwork } from './useMatchingEvmNetwork'; @@ -23,7 +23,7 @@ export { useDialogArea, useDydxClient, useInitializePage, - useIsDydxAddressValid, + useIsDoraAddressValid, useLocaleSeparators, useLocalStorage, useMatchingEvmNetwork, diff --git a/src/hooks/migrate/useBridgeTransaction.tsx b/src/hooks/migrate/useBridgeTransaction.tsx index 4383feb..9c419a5 100644 --- a/src/hooks/migrate/useBridgeTransaction.tsx +++ b/src/hooks/migrate/useBridgeTransaction.tsx @@ -3,13 +3,15 @@ import { useQuery } from 'react-query'; import { fromBech32, toHex } from '@cosmjs/encoding'; import { useContractWrite, useWaitForTransaction } from 'wagmi'; import BigNumber from 'bignumber.js'; +import { MustBigNumber } from '@/lib/numbers'; +import { useAccountBalance } from '../useAccountBalance'; import { bridgeContractAbi } from '@/constants/abi'; import { TOKEN_DECIMAL_SHIFT, TransactionStatus } from '@/constants/migrate'; -import { DydxAddress, EthereumAddress } from '@/constants/wallets'; +import { DoraAddress, EthereumAddress } from '@/constants/wallets'; import { useTrackTransactionFinalized } from './useTrackTransactionFinalized'; -import { useIsDydxAddressValid } from '../useIsDydxAddressValid'; +import { useIsDoraAddressValid } from '../useIsDoraAddressValid'; import { useDydxClient } from '../useDydxClient'; export const useBridgeTransaction = ({ @@ -24,13 +26,12 @@ export const useBridgeTransaction = ({ ); const [bridgeTxHash, setBridgeTxHash] = useState(); const [bridgeTxMinedBlockNumber, setBridgeTxMinedBlockNumber] = useState(); + const { ethDORABalance } = useAccountBalance(); const { isTransactionFinalized, numBlocksTillFinalized } = useTrackTransactionFinalized({ bridgeTxMinedBlockNumber, }); - const isDestinationAddressValid = useIsDydxAddressValid(destinationAddress); - useEffect(() => { if (isTransactionFinalized) setTransactionStatus(TransactionStatus.Finalized); }, [isTransactionFinalized]); @@ -57,21 +58,33 @@ export const useBridgeTransaction = ({ setBridgeTxMinedBlockNumber(undefined); }; + // Validations + const isAmountValid = Boolean( + MustBigNumber(ethDORABalance).gt(0) && amountBN?.gt(0) && amountBN?.lte(ethDORABalance ?? 0) + ); + + const isDestinationAddressValid = useIsDoraAddressValid(destinationAddress); + + const canConfirmBridgeContracts = isAmountValid && isDestinationAddressValid; + console.log(`can bridge? ${canConfirmBridgeContracts}`) + const { writeAsync: bridge, isLoading: isBridgePending } = useContractWrite({ address: import.meta.env.VITE_BRIDGE_CONTRACT_ADDRESS, abi: bridgeContractAbi, - functionName: 'bridge', + functionName: 'submit', args: [ amountBN?.shiftedBy(TOKEN_DECIMAL_SHIFT)?.toFixed() ?? '0', - isDestinationAddressValid - ? `0x${toHex(fromBech32(destinationAddress as DydxAddress).data)}` - : '', - '', // memo + isDestinationAddressValid ? `0x${toHex(fromBech32(destinationAddress as DoraAddress).data)}` : `0`, ], chainId: Number(import.meta.env.VITE_ETH_CHAIN_ID), }); const startBridge = async () => { + // must valid destination address and valid amount, can we call bridge + if (!canConfirmBridgeContracts) { + console.log(" The address or account is incorrect, unable to proceed with the transfer. "); + return; + } setTransactionStatus(TransactionStatus.Pending); try { const result = await bridge(); @@ -105,7 +118,7 @@ export const useBridgeTransaction = ({ queryKey: [ 'pollIsCurrentTransactionAcknowledged', { - dydxAddress: destinationAddress, + DoraAddress: destinationAddress, ethBlockHeight: Number(bridgeTxMinedBlockNumber), }, ], diff --git a/src/hooks/migrate/useTokenAllowance.tsx b/src/hooks/migrate/useTokenAllowance.tsx index ed27f31..66d9db0 100644 --- a/src/hooks/migrate/useTokenAllowance.tsx +++ b/src/hooks/migrate/useTokenAllowance.tsx @@ -4,12 +4,15 @@ import BigNumber from 'bignumber.js'; import { useContractWrite, useContractRead, useWaitForTransaction } from 'wagmi'; import { ethDYDXContractAbi } from '@/constants/abi'; +import { ethINFContractAbi } from '@/constants/abi'; import { TOKEN_DECIMAL_SHIFT } from '@/constants/migrate'; import { MustBigNumber } from '@/lib/numbers'; import { useAccounts } from '../useAccounts'; import { useAccountBalance } from '../useAccountBalance'; +import { error } from 'console'; +import { isNull } from 'lodash'; export const useTokenAllowance = ({ amountBN, @@ -21,13 +24,13 @@ export const useTokenAllowance = ({ watch: boolean; }) => { const { evmAddress } = useAccounts(); - const { ethDYDXBalance } = useAccountBalance(); + const { ethDORABalance } = useAccountBalance(); const [needsRefetch, setNeedsRefetch] = useState(false); const { data: needTokenAllowance, refetch } = useContractRead({ - address: import.meta.env.VITE_ETH_DYDX_ADDRESSS, - abi: ethDYDXContractAbi, + address: import.meta.env.VITE_ETH_DORA_ADDRESSS, + abi: ethINFContractAbi, functionName: 'allowance', args: [evmAddress, import.meta.env.VITE_BRIDGE_CONTRACT_ADDRESS], chainId: Number(import.meta.env.VITE_ETH_CHAIN_ID), @@ -37,20 +40,26 @@ export const useTokenAllowance = ({ MustBigNumber(allowance as string).lt(amountBN?.shiftedBy(TOKEN_DECIMAL_SHIFT) ?? 0), }); + console.log(`内部调试needTokenAllowance为${needTokenAllowance}`) + const { data: approveTokenData, writeAsync: approveToken, isLoading: isApproveTokenPending, + isError, + error } = useContractWrite({ - address: import.meta.env.VITE_ETH_DYDX_ADDRESSS, - abi: ethDYDXContractAbi, + address: import.meta.env.VITE_ETH_DORA_ADDRESSS, + abi: ethINFContractAbi, functionName: 'approve', args: [ import.meta.env.VITE_BRIDGE_CONTRACT_ADDRESS, - MustBigNumber(ethDYDXBalance).shiftedBy(TOKEN_DECIMAL_SHIFT).toFixed(), + MustBigNumber(ethDORABalance).shiftedBy(TOKEN_DECIMAL_SHIFT).toFixed(), ], chainId: Number(import.meta.env.VITE_ETH_CHAIN_ID), }); + console.log(`approve有错误吗${isError}`) + console.log(`approve出现的错误为${error}`) const { isLoading: isApproveTokenTxPending, error: approveTokenTxError } = useWaitForTransaction({ hash: approveTokenData?.hash, diff --git a/src/hooks/useAccountBalance.tsx b/src/hooks/useAccountBalance.tsx index 546211a..0cd1ae9 100644 --- a/src/hooks/useAccountBalance.tsx +++ b/src/hooks/useAccountBalance.tsx @@ -1,7 +1,8 @@ import { useContext, createContext } from 'react'; import { useSelector } from 'react-redux'; import { useQuery } from 'react-query'; -import { useBalance } from 'wagmi'; +import { useBalance, useContractRead } from 'wagmi'; +import { ethINFContractAbi } from '@/constants/abi'; import { TOKEN_DECIMAL_SHIFT } from '@/constants/migrate'; @@ -26,29 +27,48 @@ export const useAccountBalance = () => useContext(AccountBalanceContext)!; const ACCOUNT_BALANCE_POLLING_INTERVAL = 60_000; const useAccountBalanceContext = () => { - const { evmAddress, dydxAddress, getAccountBalance } = useAccounts(); + const { evmAddress, DoraAddress, getAccountBalance } = useAccounts(); const canAccountMigrate = useSelector(calculateCanAccountMigrate); - const { data: ethDYDXBalanceData, refetch: refetchEthDYDXBalance } = useBalance({ - enabled: import.meta.env.VITE_ETH_DYDX_ADDRESSS && evmAddress && canAccountMigrate, + console.log(`ETH dydx contract address is ${import.meta.env.VITE_ETH_DORA_ADDRESSS}`) + console.log(`query evmAddress is ${evmAddress}`) + console.log(`是否可以进行账户迁移${canAccountMigrate}`) + console.log(`chain id is ${Number(import.meta.env.VITE_ETH_CHAIN_ID)}`) + + const { data: ethDORABalanceData, refetch: refetchethDORABalance } = useBalance({ + enabled: import.meta.env.VITE_ETH_DORA_ADDRESSS && evmAddress && canAccountMigrate, address: evmAddress, chainId: Number(import.meta.env.VITE_ETH_CHAIN_ID), - token: import.meta.env.VITE_ETH_DYDX_ADDRESSS, + token: import.meta.env.VITE_ETH_DORA_ADDRESSS, + }); + + const { data: balance, isError, isLoading, refetch } = useContractRead({ + address: import.meta.env.VITE_ETH_DORA_ADDRESSS, + abi: ethINFContractAbi, + functionName: 'balanceOf', + args: [evmAddress], + watch: true, }); - const { data: wethDYDXBalanceData, refetch: refetchWethDYDXBalance } = useBalance({ - enabled: import.meta.env.VITE_BRIDGE_CONTRACT_ADDRESS && evmAddress && canAccountMigrate, + console.log(`dydx使用合约查询的余额为${balance}`) + + console.log(`ethDORABalanceData is ${ethDORABalanceData}`) + const result = useBalance({ + enabled: import.meta.env.VITE_ETH_DORA_ADDRESSS && evmAddress && canAccountMigrate, address: evmAddress, - token: import.meta.env.VITE_BRIDGE_CONTRACT_ADDRESS, + chainId: Number(import.meta.env.VITE_ETH_CHAIN_ID), + token: import.meta.env.VITE_ETH_DORA_ADDRESSS, }); + console.log(`result查询结果为${result.toString()}`) + const { data: DYDXBalance, refetch: refetchDYDXBalance } = useQuery({ - enabled: Boolean(import.meta.env.VITE_DYDX_DENOM && dydxAddress !== undefined), - queryKey: ['usePollDYDXBalance', { dydxAddress }], + enabled: Boolean(import.meta.env.VITE_DYDX_DENOM && DoraAddress !== undefined), + queryKey: ['usePollDYDXBalance', { DoraAddress }], queryFn: async () => { - if (!dydxAddress) return; + if (!DoraAddress) return; return await getAccountBalance({ - dydxAddress, + DoraAddress, denom: import.meta.env.VITE_DYDX_DENOM, }); }, @@ -60,21 +80,20 @@ const useAccountBalanceContext = () => { .toString(), }); - const { formatted: ethDYDXBalance } = ethDYDXBalanceData || {}; - const { formatted: wethDYDXBalance } = wethDYDXBalanceData || {}; + const { formatted: ethDORABalance } = ethDORABalanceData || {}; + + console.log(ethDORABalance) const refetchBalances = () => { if (!evmAddress || !canAccountMigrate) return; - refetchEthDYDXBalance(); - refetchWethDYDXBalance(); + refetchethDORABalance(); - if (dydxAddress !== undefined) refetchDYDXBalance(); + if (DoraAddress !== undefined) refetchDYDXBalance(); }; return { - ethDYDXBalance, - wethDYDXBalance, + ethDORABalance, DYDXBalance, refetchBalances, diff --git a/src/hooks/useAccounts.tsx b/src/hooks/useAccounts.tsx index 2dc0f24..4f82859 100644 --- a/src/hooks/useAccounts.tsx +++ b/src/hooks/useAccounts.tsx @@ -9,7 +9,7 @@ import { OnboardingState, type EvmDerivedAddresses } from '@/constants/account'; import { DialogTypes } from '@/constants/dialogs'; import { LocalStorageKey, LOCAL_STORAGE_VERSIONS } from '@/constants/localStorage'; -import { DydxAddress, EthereumAddress, PrivateInformation } from '@/constants/wallets'; +import { DoraAddress, EthereumAddress, PrivateInformation } from '@/constants/wallets'; import { setOnboardingState } from '@/state/account'; import { openDialog } from '@/state/dialogs'; @@ -75,17 +75,17 @@ const useAccountsContext = () => { const saveEvmDerivedAccount = ({ evmAddress, - dydxAddress, + DoraAddress, }: { evmAddress: EthereumAddress; - dydxAddress?: DydxAddress; + DoraAddress?: DoraAddress; }) => { saveEvmDerivedAddresses({ ...evmDerivedAddresses, version: LOCAL_STORAGE_VERSIONS[LocalStorageKey.EvmDerivedAddresses], [evmAddress]: { ...evmDerivedAddresses[evmAddress], - dydxAddress, + DoraAddress, }, }); }; @@ -125,16 +125,16 @@ const useAccountsContext = () => { const { getAccountBalance, getSubaccounts } = useMemo( () => ({ getAccountBalance: async ({ - dydxAddress, + DoraAddress, denom = import.meta.env.VITE_DYDX_DENOM, }: { - dydxAddress: DydxAddress; + DoraAddress: DoraAddress; denom?: string; - }) => await compositeClient?.validatorClient.get.getAccountBalance(dydxAddress, denom), + }) => await compositeClient?.validatorClient.get.getAccountBalance(DoraAddress, denom), - getSubaccounts: async ({ dydxAddress }: { dydxAddress: DydxAddress }) => { + getSubaccounts: async ({ DoraAddress }: { DoraAddress: DoraAddress }) => { try { - const response = await compositeClient?.indexerClient.account.getSubaccounts(dydxAddress); + const response = await compositeClient?.indexerClient.account.getSubaccounts(DoraAddress); return response?.subaccounts; } catch (error) { // 404 is expected if the user has no subaccounts @@ -163,8 +163,8 @@ const useAccountsContext = () => { const dydxAccounts = useMemo(() => localDydxWallet?.accounts, [localDydxWallet]); - const dydxAddress = useMemo( - () => localDydxWallet?.address as DydxAddress | undefined, + const DoraAddress = useMemo( + () => localDydxWallet?.address as DoraAddress | undefined, [localDydxWallet] ); @@ -178,9 +178,9 @@ const useAccountsContext = () => { useEffect(() => { if (evmAddress) { - saveEvmDerivedAccount({ evmAddress, dydxAddress }); + saveEvmDerivedAccount({ evmAddress, DoraAddress }); } - }, [evmAddress, dydxAddress]); + }, [evmAddress, DoraAddress]); useEffect(() => { (async () => { @@ -262,7 +262,7 @@ const useAccountsContext = () => { hdKey, localDydxWallet, dydxAccounts, - dydxAddress, + DoraAddress, // Onboarding state saveHasAcknowledgedTerms, diff --git a/src/hooks/useDydxClient.tsx b/src/hooks/useDydxClient.tsx index 50d773c..a5b23e5 100644 --- a/src/hooks/useDydxClient.tsx +++ b/src/hooks/useDydxClient.tsx @@ -24,7 +24,7 @@ const useDydxClientContext = () => { // ------ Client Initialization ------ // const [compositeClient, setCompositeClient] = useState(); - useEffect(() => { +/* useEffect(() => { (async () => { try { const initializedClient = await CompositeClient.connect( @@ -54,7 +54,7 @@ const useDydxClientContext = () => { console.error(error); } })(); - }, []); + }, []); */ // ------ Wallet Methods ------ // const getWalletFromEvmSignature = async ({ signature }: { signature: string }) => { diff --git a/src/hooks/useIsDoraAddressValid.tsx b/src/hooks/useIsDoraAddressValid.tsx new file mode 100644 index 0000000..ebc340c --- /dev/null +++ b/src/hooks/useIsDoraAddressValid.tsx @@ -0,0 +1,34 @@ +import { useMemo } from 'react'; +import { decode } from 'bech32'; + +import { useAccounts } from './useAccounts'; +import { useRestrictions } from './useRestrictions'; + + +function verifyIsBech32(address: string): Error | undefined { + try { + decode(address); + } catch (error) { + return error; + } + + return undefined; +} + +function isValidAddress(address: string): boolean { + // An address is valid if it starts with `dora` and is Bech32 format. + return address.startsWith('dora') && verifyIsBech32(address) === undefined; +} + +export const useIsDoraAddressValid = (doraAddress?: string) => { + const { DoraAddress: accountDoraAddress } = useAccounts(); + const { isAddressSanctioned } = useRestrictions(); + + return useMemo( + () => + doraAddress !== undefined && + (doraAddress === accountDoraAddress || isValidAddress(doraAddress)) && + !isAddressSanctioned(doraAddress), + [doraAddress, accountDoraAddress, isAddressSanctioned] + ); +}; diff --git a/src/hooks/useIsDydxAddressValid.tsx b/src/hooks/useIsDydxAddressValid.tsx deleted file mode 100644 index 7961351..0000000 --- a/src/hooks/useIsDydxAddressValid.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { useMemo } from 'react'; -import { validation } from '@dydxprotocol/v4-client-js'; - -import { useAccounts } from './useAccounts'; -import { useRestrictions } from './useRestrictions'; - -export const useIsDydxAddressValid = (dydxAddress?: string) => { - const { dydxAddress: accountDydxAddress } = useAccounts(); - const { isAddressSanctioned } = useRestrictions(); - - return useMemo( - () => - dydxAddress !== undefined && - (dydxAddress === accountDydxAddress || validation.isValidAddress(dydxAddress)) && - !isAddressSanctioned(dydxAddress), - [dydxAddress, accountDydxAddress, isAddressSanctioned] - ); -}; diff --git a/src/hooks/useMigrateToken.tsx b/src/hooks/useMigrateToken.tsx index d06ced3..79c20e1 100644 --- a/src/hooks/useMigrateToken.tsx +++ b/src/hooks/useMigrateToken.tsx @@ -2,7 +2,7 @@ import { useContext, createContext, useEffect, useState } from 'react'; import { useSelector } from 'react-redux'; import BigNumber from 'bignumber.js'; -import { STRING_KEYS } from '@/constants/localization'; +import { STRING_KEYS, DORA_LONG_SENTENCE } from '@/constants/localization'; import { MigrateFormSteps, @@ -19,7 +19,7 @@ import { parseWalletError } from '@/lib/wallet'; import { useAccountBalance } from './useAccountBalance'; import { useAccounts } from './useAccounts'; import { useBridgeTransaction } from './migrate/useBridgeTransaction'; -import { useIsDydxAddressValid } from './useIsDydxAddressValid'; +import { useIsDoraAddressValid } from './useIsDoraAddressValid'; import { useMatchingEvmNetwork } from './useMatchingEvmNetwork'; import { usePendingMigrationsData } from './usePendingMigrationsData'; import { useStringGetter } from './useStringGetter'; @@ -40,8 +40,8 @@ export const useMigrateToken = () => useContext(MigrateTokenContext)!; const useMigrateTokenContext = () => { const stringGetter = useStringGetter(); - const { evmAddress, dydxAddress } = useAccounts(); - const { ethDYDXBalance, refetchBalances } = useAccountBalance(); + const { evmAddress, DoraAddress } = useAccounts(); + const { ethDORABalance, refetchBalances } = useAccountBalance(); const { screenAddresses, restrictUser } = useRestrictions(); const { isMatchingNetwork, matchNetwork, isSwitchingNetwork } = useMatchingEvmNetwork({ chainId: Number(import.meta.env.VITE_ETH_CHAIN_ID), @@ -57,26 +57,24 @@ const useMigrateTokenContext = () => { const [errorMsg, setErrorMsg] = useState(); const [amountBN, setAmountBN] = useState(); const [destinationAddress, setDestinationAddress] = useState( - dydxAddress as string | undefined + DoraAddress as string | undefined ); - useEffect(() => { - setDestinationAddress(dydxAddress); - }, [dydxAddress]); - useEffect(() => { setAmountBN(undefined); }, [evmAddress]); // Validations const isAmountValid = Boolean( - MustBigNumber(ethDYDXBalance).gt(0) && amountBN?.gt(0) && amountBN?.lte(ethDYDXBalance ?? 0) + MustBigNumber(ethDORABalance).gt(0) && amountBN?.gt(0.1) && amountBN?.lte(ethDORABalance ?? 0) ); - const isDestinationAddressValid = useIsDydxAddressValid(destinationAddress); + const isDestinationAddressValid = useIsDoraAddressValid(destinationAddress); const canWriteContracts = canAccountMigrate && isAmountValid && isDestinationAddressValid; - + console.log(`在allowance中是否可以写合约?${canWriteContracts}`) + console.log(amountBN) + console.log(MigrateFormSteps.Preview) // Transactions const { needTokenAllowance, approveToken, ...tokenAllowance } = useTokenAllowance({ amountBN, @@ -84,6 +82,8 @@ const useMigrateTokenContext = () => { watch: currentStep === MigrateFormSteps.Preview, }); + console.log(`needTokenAllowance is ${needTokenAllowance}`) + const { clearStatus, startBridge, bridgeTxError, transactionStatus, ...bridgeTransaction } = useBridgeTransaction({ amountBN, @@ -93,10 +93,10 @@ const useMigrateTokenContext = () => { const { setAddressSearchFilter, setFilter, refetchPendingMigrations } = usePendingMigrationsData(); - useEffect(() => { +/* useEffect(() => { // Found current corresponding pending migration if (transactionStatus === TransactionStatus.Acknowledged) refetchPendingMigrations(); - }, [transactionStatus]); + }, [transactionStatus]); */ useEffect(() => { // Reset statuses when editing or starting new migration @@ -110,7 +110,7 @@ const useMigrateTokenContext = () => { const resetForm = (shouldClearInputs?: boolean) => { if (shouldClearInputs) { setAmountBN(undefined); - setDestinationAddress(dydxAddress); + setDestinationAddress(DoraAddress); } setCurrentStep(MigrateFormSteps.Edit); }; @@ -132,18 +132,16 @@ const useMigrateTokenContext = () => { try { const screenResults = await screenAddresses({ - addresses: [evmAddress!, dydxAddress!, destinationAddress!], + addresses: [evmAddress!, DoraAddress!, destinationAddress!], throwError: true, }); - if (screenResults?.[evmAddress as string] || screenResults?.[dydxAddress as string]) { + if (screenResults?.[evmAddress as string] || screenResults?.[DoraAddress as string]) { restrictUser(); return; } else if (screenResults?.[destinationAddress!]) { setErrorMsg( - stringGetter({ - key: STRING_KEYS.MIGRATION_BLOCKED_MESSAGE_DESTINATION, - }) + DORA_LONG_SENTENCE.MIGRATION_BLOCKED_MESSAGE_DESTINATION ); return; } @@ -180,7 +178,7 @@ const useMigrateTokenContext = () => { resetForm(); } else if (transactionStatus === TransactionStatus.Acknowledged) { // Show relevant pending migrations - if (destinationAddress !== dydxAddress) { + if (destinationAddress !== DoraAddress) { setFilter(PendingMigrationFilter.All); setAddressSearchFilter(destinationAddress ?? ''); } else { diff --git a/src/hooks/usePendingMigrationsData.tsx b/src/hooks/usePendingMigrationsData.tsx index 04a3c5b..472a66f 100644 --- a/src/hooks/usePendingMigrationsData.tsx +++ b/src/hooks/usePendingMigrationsData.tsx @@ -1,5 +1,9 @@ -import { useMemo, useState } from 'react'; +import { useEffect, useMemo, useState } from 'react'; import { useQuery } from 'react-query'; +import { useBalance, useContractRead } from 'wagmi'; +import { bridgeContractAbi } from '@/constants/abi'; +import { ethers } from 'ethers'; +import { toBech32, fromHex } from '@cosmjs/encoding'; import { PendingMigrationData, @@ -7,12 +11,13 @@ import { TOKEN_DECIMAL_SHIFT, } from '@/constants/migrate'; -import { DydxAddress } from '@/constants/wallets'; +import { DoraAddress } from '@/constants/wallets'; import { MustBigNumber } from '@/lib/numbers'; import { useAccounts } from './useAccounts'; import { useDydxClient } from './useDydxClient'; +import BigNumber from 'bignumber.js'; const PENDING_MIGRATIONS_POLLING_INTERVAL = 600_000; @@ -21,41 +26,137 @@ export const usePendingMigrationsData = ({ }: { interval?: number; } = {}) => { - const { dydxAddress } = useAccounts(); + const { DoraAddress, evmAddress } = useAccounts(); const { compositeClient } = useDydxClient(); + // const [unprocessedCount, setUnprocessedCount] = useState(); const [filter, setFilter] = useState( - dydxAddress ? PendingMigrationFilter.Mine : PendingMigrationFilter.All + DoraAddress ? PendingMigrationFilter.Mine : PendingMigrationFilter.All ); const [addressSearchFilter, setAddressSearchFilter] = useState(''); - const { data: pendingMigrations, refetch: refetchPendingMigrations } = useQuery({ - enabled: !!compositeClient, - queryKey: ['pollPendingMigrations'], - queryFn: async () => - await compositeClient?.validatorClient.get.getDelayedCompleteBridgeMessages(), - select: (data) => - (data?.messages ?? []).map(({ blockHeight, message }) => ({ - blockHeight, - id: message?.event?.id, - address: message?.event?.address as DydxAddress, - amount: MustBigNumber(message?.event?.coin?.amount).shiftedBy(TOKEN_DECIMAL_SHIFT * -1), - })) as PendingMigrationData[], - refetchInterval: interval, - staleTime: interval, + const [pendingMigrations, setPendingMigrations] = useState([]); + const [refetchPendingMigrations, setRefetchPendingMigrations] = useState(null); + const [filteredPendingMigrations, setFilteredPendingMigrations] = useState< + PendingMigrationData[] + >([]); + + // Get totalRecords count + const { data: totalRecordsData } = useContractRead({ + address: import.meta.env.VITE_BRIDGE_CONTRACT_ADDRESS, + abi: bridgeContractAbi, + functionName: 'totalRecords', + }); + + console.log(`total totalRecordsData 数量is ${totalRecordsData}`); + + // Get processedRecords count + const { data: processedRecordsData } = useContractRead({ + address: import.meta.env.VITE_BRIDGE_CONTRACT_ADDRESS, + abi: bridgeContractAbi, + functionName: 'processedRecords', }); + console.log(`processedRecordsData 数量is ${processedRecordsData}`); + + // Get unprocessedRecords count + const unprocessedCount = Number(totalRecordsData) - Number(processedRecordsData); + + // Get unprocessed Records Info + const { data: unprocessedRecordsData } = useContractRead({ + address: import.meta.env.VITE_BRIDGE_CONTRACT_ADDRESS, + abi: bridgeContractAbi, + functionName: 'getUnprocessedRecords', + args: [unprocessedCount], + enabled: unprocessedCount > 0, + }); + console.log(`unprocessedCount is ${unprocessedCount}`); + + console.log(`未处理的Record data为${unprocessedRecordsData}`); + + useEffect(() => { + if (unprocessedCount > 0) { + const fetchData = async () => { + try { + console.log(`查询未处理的数据。。。。。。。。`); + const data = unprocessedRecordsData; + if (!data) { + console.log('Data is undefined or null'); + setPendingMigrations([]); + return; + } + console.log(`data is ${data}`); + if (Array.isArray(data) && data.length === 2) { + const [size, records] = data; + const mappedData = records.map((record, idx) => ({ + id: idx, + address: toBech32( + 'dora', + fromHex(record.vota.startsWith('0x') ? record.vota.slice(2) : record.vota) + ), + amount: BigNumber(record.amount).shiftedBy(-18).toFixed(), + txHash: ethers.hexlify(record.txHash), + })) as PendingMigrationData[]; + setPendingMigrations(mappedData); + } else { + console.log(`data is not in expected format: ${data}`); + setPendingMigrations([]); + } + } catch (error) { + console.error('Error fetching pending migrations:', error); + setPendingMigrations([]); + } + }; - const filteredPendingMigrations = useMemo( - () => - (pendingMigrations ?? []).filter(({ address }) => - address.includes( - dydxAddress && filter === PendingMigrationFilter.Mine - ? dydxAddress - : addressSearchFilter.trim().toLowerCase() - ) + fetchData(); + + const intervalId = setInterval(fetchData, interval); + return () => clearInterval(intervalId); + } + }, [unprocessedCount, unprocessedRecordsData, interval]); + + // Get an evm address's all records + const { data: evmUserRecords, error } = useContractRead({ + address: import.meta.env.VITE_BRIDGE_CONTRACT_ADDRESS, + abi: bridgeContractAbi, + functionName: 'recordOf', + args: [evmAddress], + enabled: filter === PendingMigrationFilter.Mine && Boolean(evmAddress), + }); + + console.log(evmUserRecords); + + useEffect(() => { + if (error) { + console.error('Error fetching records from bridge contract:', error); + } + + let transformedUserRecords = []; + if (evmUserRecords) { + transformedUserRecords = Object.values(evmUserRecords); + } + + let transformedEVMUserRecords = transformedUserRecords.map((record, idx) => ({ + id: idx, + address: toBech32( + 'dora', + fromHex(record.vota.startsWith('0x') ? record.vota.slice(2) : record.vota) ), - [pendingMigrations, addressSearchFilter, filter, dydxAddress] - ); + amount: BigNumber(record.amount).shiftedBy(-18), + txHash: ethers.hexlify(record.txHash), + })) as PendingMigrationData[]; + + console.log(`transformedEVMUserRecords is ${transformedEVMUserRecords}`); + + if (filter === PendingMigrationFilter.Mine && evmAddress && transformedUserRecords.length) { + setFilteredPendingMigrations(transformedEVMUserRecords); + } else { + const searchFilter = addressSearchFilter.trim().toLowerCase(); + const filtered = (pendingMigrations ?? []).filter(({ address }) => + address.includes(searchFilter) + ); + setFilteredPendingMigrations(filtered); + } + }, [evmUserRecords, error, addressSearchFilter, filter, evmAddress]); const { data: latestBlockHeight } = useQuery({ enabled: !!compositeClient && Boolean(pendingMigrations?.length), diff --git a/src/hooks/useRestrictions.tsx b/src/hooks/useRestrictions.tsx index e0a767e..365c9a0 100644 --- a/src/hooks/useRestrictions.tsx +++ b/src/hooks/useRestrictions.tsx @@ -12,7 +12,7 @@ import { useAccounts } from './useAccounts'; const useRestrictionContext = () => { const { compositeClient } = useDydxClient(); - const { evmAddress, dydxAddress, disconnect } = useAccounts(); + const { evmAddress, DoraAddress, disconnect } = useAccounts(); const dispatch = useDispatch(); const [sanctionedAddresses, setSanctionedAddresses] = useState>(new Set()); @@ -92,17 +92,17 @@ const useRestrictionContext = () => { }, [screenAddresses, evmAddress]); useEffect(() => { - if (dydxAddress) screenAddresses({ addresses: [dydxAddress] }); - }, [screenAddresses, dydxAddress]); + if (DoraAddress) screenAddresses({ addresses: [DoraAddress] }); + }, [screenAddresses, DoraAddress]); useEffect(() => { if ( (evmAddress && isAddressSanctioned(evmAddress)) || - (dydxAddress && isAddressSanctioned(dydxAddress)) + (DoraAddress && isAddressSanctioned(DoraAddress)) ) { restrictUser(); } - }, [evmAddress, dydxAddress, isAddressSanctioned, restrictUser]); + }, [evmAddress, DoraAddress, isAddressSanctioned, restrictUser]); return { screenAddresses, diff --git a/src/icons/logo-short.tsx b/src/icons/logo-short.tsx index d4a4902..b43ae96 100644 --- a/src/icons/logo-short.tsx +++ b/src/icons/logo-short.tsx @@ -1,45 +1,178 @@ const LogoShortIcon: React.FC<{ id?: string }> = ({ id }) => ( + // - - - + + + + + + + + + + + - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + + + + + + // width="135" + // height="145" + // viewBox="0 0 135 145" + // fill="none" + // xmlns="http://www.w3.org/2000/svg" + // > + // + // + // + // + // + // + // + // + // + // + // + // + // + // ); export default LogoShortIcon; diff --git a/src/icons/migrate.svg b/src/icons/migrate.svg index c77b877..65541ac 100644 --- a/src/icons/migrate.svg +++ b/src/icons/migrate.svg @@ -1,9 +1,61 @@ - - + + + + + + + + + + + + + + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/lib/timeUtils.ts b/src/lib/timeUtils.ts index 5a33aac..8ebb3c4 100644 --- a/src/lib/timeUtils.ts +++ b/src/lib/timeUtils.ts @@ -36,19 +36,19 @@ export const getStringsForTimeInterval = (timeInterval: Duration) => { if (months > 0) { timeString = Math.round(months).toString(); - unitStringKey = STRING_KEYS.MONTHS_ABBREVIATED; + unitStringKey = "M"; } else if (weeks > 0) { timeString = Math.round(weeks + days / 7).toString(); - unitStringKey = STRING_KEYS.WEEKS_ABBREVIATED; + unitStringKey = "w"; } else if (days > 0) { timeString = Math.round(days + hours / 24).toString(); - unitStringKey = STRING_KEYS.DAYS_ABBREVIATED; + unitStringKey = "d"; } else if (hours > 0) { timeString = Math.round(hours + minutes / 60).toString(); - unitStringKey = STRING_KEYS.HOURS_ABBREVIATED; + unitStringKey = "h"; } else { timeString = Math.ceil(minutes).toString(); - unitStringKey = STRING_KEYS.MINUTES_ABBREVIATED; + unitStringKey = "m"; } return { diff --git a/src/lib/wagmi.ts b/src/lib/wagmi.ts index ed7e675..2b0eb82 100644 --- a/src/lib/wagmi.ts +++ b/src/lib/wagmi.ts @@ -3,6 +3,7 @@ import { createConfig, configureChains, mainnet, sepolia, Chain } from 'wagmi'; import { alchemyProvider } from 'wagmi/providers/alchemy'; import { jsonRpcProvider } from 'wagmi/providers/jsonRpc'; import { publicProvider } from 'wagmi/providers/public'; +import { infuraProvider } from 'wagmi/providers/infura'; import { CoinbaseWalletConnector } from 'wagmi/connectors/coinbaseWallet'; import { InjectedConnector } from 'wagmi/connectors/injected'; @@ -18,15 +19,16 @@ export const WAGMI_SUPPORTED_CHAINS: Chain[] = [mainnet, sepolia]; const { chains, publicClient, webSocketPublicClient } = configureChains( WAGMI_SUPPORTED_CHAINS, [ - import.meta.env.VITE_ALCHEMY_API_KEY && - alchemyProvider({ apiKey: import.meta.env.VITE_ALCHEMY_API_KEY }), - jsonRpcProvider({ - rpc: (chain) => ({ http: chain.rpcUrls.default.http[0] }), - }), + // use infura api key + infuraProvider({apiKey: import.meta.env.VITE_INFURA_API_KEY}), publicProvider(), ].filter(isTruthy) ); +console.log(chains) +console.log(publicClient) +console.log(webSocketPublicClient) + const injectedConnectorOptions = { chains, options: { diff --git a/src/lib/wallet/index.ts b/src/lib/wallet/index.ts index 80ee1c4..f592414 100644 --- a/src/lib/wallet/index.ts +++ b/src/lib/wallet/index.ts @@ -11,7 +11,7 @@ import { import { detectInjectedEip1193Providers } from './providers'; // Formatting -export const truncateAddress = (address?: string, prefix: string = 'dydx') => { +export const truncateAddress = (address?: string, prefix: string = 'dora') => { if (!address) return ''; const hash = address.replace(prefix, ''); const firstHalf = hash.slice(0, 4); @@ -19,6 +19,14 @@ export const truncateAddress = (address?: string, prefix: string = 'dydx') => { return `${prefix}${firstHalf}...${secondHalf}`; }; +export const truncateTransactionHash = (address?: string, prefix: string = '') => { + if (!address) return ''; + const hash = address.replace(prefix, ''); + const firstHalf = hash.slice(0, 8); + const secondHalf = hash.slice(-8); + return `${prefix}${firstHalf}...${secondHalf}`; +}; + // Wallet connections export const getWalletConnection = ({ walletType, @@ -114,12 +122,7 @@ export const parseWalletError = ({ break; } default: { - message = stringGetter({ - key: STRING_KEYS.SOMETHING_WENT_WRONG_WITH_MESSAGE, - params: { - ERROR_MESSAGE: error.message || stringGetter({ key: STRING_KEYS.UNKNOWN_ERROR }), - }, - }); + message = `Something went wrong: ${error.message || 'Unknown error'}`; } } diff --git a/src/views/Header.tsx b/src/views/Header.tsx index 93e721f..eb40a63 100644 --- a/src/views/Header.tsx +++ b/src/views/Header.tsx @@ -36,24 +36,19 @@ export const Header = () => { { group: 'navigation', items: [ - import.meta.env.VITE_TRADE_URL && { - value: 'TRADE', - label: stringGetter({ key: STRING_KEYS.TRADE }), - href: import.meta.env.VITE_TRADE_URL, - }, { value: 'MIGRATE', - label: stringGetter({ key: STRING_KEYS.MIGRATE }), + label: 'Migrate', active: true, href: '/', }, { value: 'MORE', - label: stringGetter({ key: STRING_KEYS.MORE }), + label: 'More', subitems: RELEVANT_LINKS.map((linkItem) => ({ value: linkItem.value, slotBefore: linkItem.iconName ? : undefined, - label: stringGetter({ key: linkItem.labelStringKey }), + label: linkItem.labelStringKey, href: linkItem.href, })), }, diff --git a/src/views/HelpPanel.tsx b/src/views/HelpPanel.tsx index 8071431..4cc74ae 100644 --- a/src/views/HelpPanel.tsx +++ b/src/views/HelpPanel.tsx @@ -1,6 +1,6 @@ import styled, { AnyStyledComponent } from 'styled-components'; -import { STRING_KEYS } from '@/constants/localization'; +import { STRING_KEYS, DORA_LONG_SENTENCE } from '@/constants/localization'; import { AppRoute } from '@/constants/routes'; import breakpoints from '@/styles/breakpoints'; @@ -19,9 +19,9 @@ export const HelpPanel = () => { -

{stringGetter({ key: STRING_KEYS.MIGRATION_HELP })}

- - {stringGetter({ key: STRING_KEYS.LEARN_MORE })} +

Migration help

+ + Learn More } @@ -29,70 +29,46 @@ export const HelpPanel = () => { - {stringGetter({ key: STRING_KEYS.HERE })} - - ), - }, - }), + header: DORA_LONG_SENTENCE.MIGRATION_FAQ_DORA_CHAIN_PORTAL, + content: DORA_LONG_SENTENCE.MIGRATION_FAQ_DORA_CHAIN_PORTAL_ANSWER, }, { - header: stringGetter({ - key: STRING_KEYS.MIGRATION_FAQ_WHAT_TOKENS, - }), - content: stringGetter({ - key: STRING_KEYS.MIGRATION_FAQ_WHAT_TOKENS_ANSWER, - }), + header: DORA_LONG_SENTENCE.MIGRATION_FAQ_WHAT_ADDRESS_CAN_INTERACT, + content: DORA_LONG_SENTENCE.MIGRATION_FAQ_WHAT_ADDRESS_CAN_INTERACT_ANSWER, }, { - header: stringGetter({ key: STRING_KEYS.MIGRATION_FAQ_HOW_LONG }), - content: stringGetter({ - key: STRING_KEYS.MIGRATION_FAQ_HOW_LONG_ANSWER, - }), + header: DORA_LONG_SENTENCE.MIGRATION_FAQ_CUSTODY_TOKEN, + content: DORA_LONG_SENTENCE.MIGRATION_FAQ_CUSTODY_TOKEN_ANSWER, }, { - header: stringGetter({ - key: STRING_KEYS.MIGRATION_FAQ_HOW_TO_TRACK, - }), - content: stringGetter({ - key: STRING_KEYS.MIGRATION_FAQ_HOW_TO_TRACK_ANSWER, - }), + header: DORA_LONG_SENTENCE.MIGRATION_FAQ_HOW_LONG, + content: DORA_LONG_SENTENCE.MIGRATION_FAQ_HOW_LONG_ANSWER, }, { - header: stringGetter({ key: STRING_KEYS.MIGRATION_FAQ_GAS_FEES }), - content: stringGetter({ - key: STRING_KEYS.MIGRATION_FAQ_GAS_FEES_ANSWER, - }), + header: DORA_LONG_SENTENCE.MIGRATION_FAQ_CONTRACR_TOKEN, + content: DORA_LONG_SENTENCE.MIGRATION_FAQ_CONTRACR_TOKEN_ANSWER, }, { - header: stringGetter({ - key: STRING_KEYS.MIGRATION_FAQ_WHAT_ADDRESS, - }), - content: stringGetter({ - key: STRING_KEYS.MIGRATION_FAQ_WHAT_ADDRESS_ANSWER, - }), + header: DORA_LONG_SENTENCE.MIGRATION_FAQ_VCDORA_HOLDER, + content: DORA_LONG_SENTENCE.MIGRATION_FAQ_VCDORA_HOLDER_ANSWER, }, { - header: stringGetter({ - key: STRING_KEYS.MIGRATION_FAQ_WRAPPED_TOKENS, - }), - content: stringGetter({ - key: STRING_KEYS.MIGRATION_FAQ_WRAPPED_TOKENS_ANSWER, - }), + header: DORA_LONG_SENTENCE.MIGRATION_FAQ_GAS_FEES, + content: DORA_LONG_SENTENCE.MIGRATION_FAQ_GAS_FEES_ANSWER, + }, + { + header: DORA_LONG_SENTENCE.MIGRATION_FAQ_HOW_TO_TRACK, + content: DORA_LONG_SENTENCE.MIGRATION_FAQ_HOW_TO_TRACK_ANSWER, }, ]} /> - {stringGetter({ key: STRING_KEYS.TERMS_OF_USE })} + Terms of Use + + + Privacy policy ); @@ -102,7 +78,7 @@ const Styled: Record = {}; Styled.Container = styled.div` text-align: center; - + @media ${breakpoints.notTablet} { width: max-content; } diff --git a/src/views/MigrateForm/ConfirmedStep/FinalizingCountdownTimer.tsx b/src/views/MigrateForm/ConfirmedStep/FinalizingCountdownTimer.tsx index e49159b..b956609 100644 --- a/src/views/MigrateForm/ConfirmedStep/FinalizingCountdownTimer.tsx +++ b/src/views/MigrateForm/ConfirmedStep/FinalizingCountdownTimer.tsx @@ -40,7 +40,7 @@ export const FinalizingCountdownTimer = ({ return ( - {stringGetter({ key: STRING_KEYS.FINALIZING })} + Finalizing {secondsRemaining > 0 ? ` ${minutes}:${seconds}` : '...'} ); diff --git a/src/views/MigrateForm/ConfirmedStep/MigrateFormConfirmedStep.tsx b/src/views/MigrateForm/ConfirmedStep/MigrateFormConfirmedStep.tsx index 0132484..1e2abbd 100644 --- a/src/views/MigrateForm/ConfirmedStep/MigrateFormConfirmedStep.tsx +++ b/src/views/MigrateForm/ConfirmedStep/MigrateFormConfirmedStep.tsx @@ -3,7 +3,7 @@ import BigNumber from 'bignumber.js'; import { ButtonAction, ButtonType } from '@/constants/buttons'; import { TransactionStatus } from '@/constants/migrate'; -import { STRING_KEYS } from '@/constants/localization'; +import { STRING_KEYS, DORA_KEYS } from '@/constants/localization'; import { layoutMixins } from '@/styles/layoutMixins'; @@ -39,18 +39,12 @@ export const MigrateFormConfirmedStep = () => { const { evmAddress } = useAccounts(); - const getAssetOnChainLabel = ({ asset, chain }: { asset: string; chain: string }) => ( - - {asset} {stringGetter({ key: STRING_KEYS.ON_CHAIN, params: { CHAIN: chain } })} - - ); - return ( <> } detailItems={[ { @@ -60,11 +54,11 @@ export const MigrateFormConfirmedStep = () => { {transactionStatus > TransactionStatus.NotStarted && transactionStatus < TransactionStatus.Finalized && !bridgeTxError && } - {stringGetter({ key: STRING_KEYS.TRANSACTION })} + Transaction {transactionStatus >= TransactionStatus.Finalized ? ( - {stringGetter({ key: STRING_KEYS.FINALIZED })} + Finalized ) : bridgeTxError ? ( - {stringGetter({ key: STRING_KEYS.FAILED })} + Failed ) : ( transactionStatus === TransactionStatus.Unfinalized && ( @@ -74,40 +68,22 @@ export const MigrateFormConfirmedStep = () => { ), value: ( - {stringGetter({ key: STRING_KEYS.BLOCK_EXPLORER })} + Block explorer ), }, { key: 'address', - label: stringGetter({ key: STRING_KEYS.ADDRESS }), + label: "Address", value: ( {truncateAddress(evmAddress, '0x')} ), }, - { - key: 'wethDYDX', - label: getAssetOnChainLabel({ - asset: 'wethDYDX', - chain: 'Ethereum', - }), - value: ( - - ), - }, { key: 'ethDYDX', - label: getAssetOnChainLabel({ - asset: 'ethDYDX', - chain: 'Ethereum', - }), + label: 'ethDORA on Ethereum', value: ( { /> } + header={DORA_KEYS.DORA_CHAIN_SETTLEMENT} + headerIcon={} detailItems={[ { key: 'transaction', @@ -132,27 +108,21 @@ export const MigrateFormConfirmedStep = () => { {transactionStatus === TransactionStatus.Finalized && ( )} - {stringGetter({ key: STRING_KEYS.TRANSACTION })} + Transaction - {stringGetter({ - key: - transactionStatus < TransactionStatus.Acknowledged - ? STRING_KEYS.NOT_STARTED - : STRING_KEYS.STARTED, - })} + {transactionStatus < TransactionStatus.Acknowledged + ? "Not Started" + : "Started"} ), - value: stringGetter({ - key: STRING_KEYS.X_HOURS_LOWERCASED, - params: { X: '~40' }, - }), + value: "~40 hours", }, { key: 'address', - label: stringGetter({ key: STRING_KEYS.ADDRESS }), + label: "Address", value: ( {truncateAddress(destinationAddress)} @@ -161,10 +131,7 @@ export const MigrateFormConfirmedStep = () => { }, { key: 'DYDX', - label: getAssetOnChainLabel({ - asset: 'DYDX', - chain: 'dYdX Chain', - }), + label: 'DORA on Dora Vota', value: ( { {bridgeTxError ? ( - {stringGetter({ key: STRING_KEYS.RETRY_MIGRATION })} + Retry migration ) : transactionStatus < TransactionStatus.Acknowledged ? ( {transactionStatus < TransactionStatus.Finalized ? ( - stringGetter({ key: STRING_KEYS.PROGRESS_TRACKING_WARNING }) + "Leave this open to track your progress while the transaction is being finalized." ) : ( )} @@ -194,10 +161,10 @@ export const MigrateFormConfirmedStep = () => { ) : ( resetForm(true)}> - {stringGetter({ key: STRING_KEYS.NEW_MIGRATION })} + New migration - {stringGetter({ key: STRING_KEYS.CHECK_STATUS })} + Check status )} diff --git a/src/views/MigrateForm/EditingStep/MigrateFormEditingStep.tsx b/src/views/MigrateForm/EditingStep/MigrateFormEditingStep.tsx index bdcf98e..3e030e4 100644 --- a/src/views/MigrateForm/EditingStep/MigrateFormEditingStep.tsx +++ b/src/views/MigrateForm/EditingStep/MigrateFormEditingStep.tsx @@ -38,8 +38,8 @@ import { PreviewMigrateButtonAndReceipt } from './PreviewMigrateButtonAndReceipt export const MigrateFormEditingStep = () => { const stringGetter = useStringGetter(); - const { dydxAddress: accountDydxAddress } = useAccounts(); - const { ethDYDXBalance } = useAccountBalance(); + const { DoraAddress: accountDoraAddress } = useAccounts(); + const { ethDORABalance } = useAccountBalance(); const { isAddressSanctioned } = useRestrictions(); const { @@ -55,14 +55,14 @@ export const MigrateFormEditingStep = () => { DestinationAddressOptions.Account ); - const ethDYDXBalanceBN = MustBigNumber(ethDYDXBalance); - const newEthDYDXBalanceBN = ethDYDXBalanceBN.minus(amountBN ?? 0); + const ethDORABalanceBN = MustBigNumber(ethDORABalance); + const newethDORABalanceBN = ethDORABalanceBN.minus(amountBN ?? 0); const onOptionChange = (option: string) => { if (option === DestinationAddressOptions.Other) { setDestinationAddress(''); - } else if (accountDydxAddress) { - setDestinationAddress(accountDydxAddress); + } else if (accountDoraAddress) { + setDestinationAddress(accountDoraAddress); } setDestinationAddressOption(option as DestinationAddressOptions); }; @@ -81,23 +81,18 @@ export const MigrateFormEditingStep = () => { key: 'amount', label: ( - {stringGetter({ - key: STRING_KEYS.AVAILABLE_ON_CHAIN, - params: { - CHAIN: 'Ethereum', - }, - })} - ethDYDX + Available on Ethereum + ethDORA ), value: ( ), @@ -130,7 +125,7 @@ export const MigrateFormEditingStep = () => { setAmountBN(MustBigNumber(floatValue))} value={amountBN?.toFixed( @@ -138,113 +133,52 @@ export const MigrateFormEditingStep = () => { BigNumber.ROUND_DOWN )} slotRight={renderFormInputButton({ - label: stringGetter({ key: STRING_KEYS.MAX }), + label: 'Max', isInputEmpty: !amountBN, onClear: () => setAmountBN(undefined), - onClick: () => ethDYDXBalance && setAmountBN(ethDYDXBalanceBN), + onClick: () => ethDORABalance && setAmountBN(ethDORABalanceBN), })} + validationConfig={ + amountBN && !amountBN.gt(0.1) && + { + attached: true, + type: AlertType.Error, + message: 'Please enter a quantity greater than 0.1', + } + } /> - - {stringGetter({ - key: STRING_KEYS.GENERATED_ADDRESS_VIA_ADDRESS, - params: { - ADDRESS_OR_WALLET_SIGNATURE: ( - - {accountDydxAddress - ? truncateAddress(accountDydxAddress) - : stringGetter({ key: STRING_KEYS.WALLET_SIGNATURE })} - - ), - }, - })} - - ), - slotContent: accountDydxAddress && ( - - {stringGetter({ key: STRING_KEYS.DYDX_CHAIN_ADDRESS })} - - - } - type={InputType.Text} - value={truncateAddress(accountDydxAddress)} - validationConfig={{ - attached: true, - type: AlertType.Info, - message: stringGetter({ - key: STRING_KEYS.GENERATED_ADDRESS_INFO, - params: { - TRADE_URL: import.meta.env.VITE_TRADE_URL || 'the trading app', - }, - }), - }} - disabled - /> - ), - }, - { - value: DestinationAddressOptions.Other, - label: ( - - {stringGetter({ - key: STRING_KEYS.SEND_TO_ANOTHER_ADDRESS, - params: { - ADDRESS: ( - {stringGetter({ key: STRING_KEYS.DYDX_CHAIN_ADDRESS })} - ), - }, - })} - - ), - slotContent: ( - - setDestinationAddress(e.target?.value)} - label={ - - {stringGetter({ key: STRING_KEYS.DYDX_CHAIN_ADDRESS })} - {isDestinationAddressValid && } - - } - type={InputType.Text} - value={destinationAddress} - placeholder={stringGetter({ key: STRING_KEYS.ENTER_ADDRESS })} - slotRight={renderFormInputButton({ - label: stringGetter({ key: STRING_KEYS.PASTE }), - isInputEmpty: !destinationAddress, - onClear: () => setDestinationAddress(''), - onClick: onPasteAddress, - })} - validationConfig={ - destinationAddress && - !isDestinationAddressValid && { - attached: true, - type: AlertType.Error, - message: stringGetter({ - key: isAddressSanctioned(destinationAddress) - ? STRING_KEYS.MIGRATION_BLOCKED_MESSAGE_DESTINATION - : STRING_KEYS.INVALID_ADDRESS_BODY, - }), - } - } - /> - - ), - }, - ]} - value={destinationAddressOption} - onValueChange={onOptionChange} - /> - + Send to your Dora Vota Address + + setDestinationAddress(e.target?.value)} + label={ + + Dora Vota Address + {isDestinationAddressValid && } + + } + type={InputType.Text} + value={destinationAddress} + placeholder={'Enter Dora Vota address'} + slotRight={renderFormInputButton({ + label: "Paste", + isInputEmpty: !destinationAddress, + onClear: () => setDestinationAddress(''), + onClick: onPasteAddress, + })} + validationConfig={ + destinationAddress && + !isDestinationAddressValid && { + attached: true, + type: AlertType.Error, + message: 'Please enter a valid DORA Vota address.', + } + } + /> + diff --git a/src/views/MigrateForm/EditingStep/PreviewMigrateButtonAndReceipt.tsx b/src/views/MigrateForm/EditingStep/PreviewMigrateButtonAndReceipt.tsx index 837a80b..8bf275d 100644 --- a/src/views/MigrateForm/EditingStep/PreviewMigrateButtonAndReceipt.tsx +++ b/src/views/MigrateForm/EditingStep/PreviewMigrateButtonAndReceipt.tsx @@ -29,68 +29,36 @@ type ElementProps = { export const PreviewMigrateButtonAndReceipt = ({ isDisabled, isLoading }: ElementProps) => { const stringGetter = useStringGetter(); - const { dydxAddress, evmAddress } = useAccounts(); + const { DoraAddress, evmAddress } = useAccounts(); const { amountBN, destinationAddress } = useMigrateToken(); - const { ethDYDXBalance, DYDXBalance, wethDYDXBalance } = useAccountBalance(); + const { ethDORABalance, DYDXBalance } = useAccountBalance(); const canAccountMigrate = useSelector(calculateCanAccountMigrate); const getLabel = ({ chain, asset }: { chain: string; asset: string }) => ( - {stringGetter({ - key: STRING_KEYS.BALANCE_ON_CHAIN, - params: { CHAIN: chain }, - })} + Balance on Ethereum {asset} ); const migrateDetailItems = [ { - key: 'ethDYDXBalance', - label: getLabel({ chain: 'Ethereum', asset: 'ethDYDX' }), + key: 'ethDORABalance', + label: getLabel({ chain: 'Ethereum', asset: 'ethDORA' }), value: ( ), }, - import.meta.env.VITE_BRIDGE_CONTRACT_ADDRESS && { - key: 'wethDYDXBalance', - label: getLabel({ chain: 'Ethereum', asset: 'wethDYDX' }), - value: ( - - ), - }, - dydxAddress && - dydxAddress === destinationAddress && { - key: 'DYDXBalance', - label: getLabel({ chain: 'dYdX Chain', asset: 'DYDX' }), - value: ( - - ), - }, ].filter(isTruthy); return ( @@ -103,7 +71,7 @@ export const PreviewMigrateButtonAndReceipt = ({ isDisabled, isLoading }: Elemen type={ButtonType.Submit} state={{ isLoading, isDisabled }} > - {stringGetter({ key: STRING_KEYS.PREVIEW_MIGRATION })} + Preview Migration )} diff --git a/src/views/MigrateForm/PreviewStep/MigrateFormPreviewStep.tsx b/src/views/MigrateForm/PreviewStep/MigrateFormPreviewStep.tsx index a9ad1bb..e47a924 100644 --- a/src/views/MigrateForm/PreviewStep/MigrateFormPreviewStep.tsx +++ b/src/views/MigrateForm/PreviewStep/MigrateFormPreviewStep.tsx @@ -4,7 +4,7 @@ import styled, { type AnyStyledComponent } from 'styled-components'; import { AlertType } from '@/constants/alerts'; import { ButtonAction, ButtonType } from '@/constants/buttons'; -import { STRING_KEYS } from '@/constants/localization'; +import { STRING_KEYS, DORA_LONG_SENTENCE } from '@/constants/localization'; import { layoutMixins } from '@/styles/layoutMixins'; @@ -27,6 +27,7 @@ export const MigrateFormPreviewStep = () => { errorMsg, needTokenAllowance, isAmountValid, + isDestinationAddressValid, isApproveTokenLoading, approveTokenTxError, isBridgePending, @@ -40,43 +41,34 @@ export const MigrateFormPreviewStep = () => { } detailItems={[ { key: 'eth_settlement', - label: stringGetter({ key: STRING_KEYS.ETHEREUM_SETTLEMENT }), + label: 'Ethereum settlement', value: ( - {stringGetter({ - key: STRING_KEYS.X_SECONDS_LOWERCASED, - params: { X: '0-12' }, - })} + 0-12 seconds ), }, { key: 'eth_finalization', - label: stringGetter({ key: STRING_KEYS.ETHEREUM_FINALIZATION }), + label: 'Ethereum finalization', value: ( - {stringGetter({ - key: STRING_KEYS.X_MINUTES_LOWERCASED, - params: { X: '~20' }, - })} + ~20 minutes ), }, { key: 'dydx_settlement', - label: stringGetter({ key: STRING_KEYS.DYDX_CHAIN_SETTLEMENT }), - tooltip: 'dydx-chain-settlement', + label: 'Dora Vota settlement', + // tooltip: 'dydx-chain-settlement', value: ( - {stringGetter({ - key: STRING_KEYS.X_HOURS_LOWERCASED, - params: { X: '~40' }, - })} + ~40 hours ), }, @@ -95,25 +87,19 @@ export const MigrateFormPreviewStep = () => { checked={hasAcknowledgedDuration} onCheckedChange={setHasAcknowledgedDuration} id="acknowledge-duration" - label={stringGetter({ - key: STRING_KEYS.CONFIRM_MIGRATION_DISCLAIMER_1, - })} + label={DORA_LONG_SENTENCE.CONFIRM_MIGRATION_DISCLAIMER_1} /> )} - resetForm(false)}> - {stringGetter({ key: STRING_KEYS.EDIT })} - + resetForm(false)}>Edit { isDisabled: (!needTokenAllowance && !(hasAcknowledgedDuration && hasAcknowledgedLocked)) || isBridgePending || - !isAmountValid, + !isAmountValid || + !isDestinationAddressValid, }} > - {stringGetter({ - key: needTokenAllowance ? STRING_KEYS.APPROVE_ALLOWANCE : STRING_KEYS.CONFIRM_MIGRATION, - })} + {needTokenAllowance ? 'Approve Allowance' : 'Confirm Migration'} diff --git a/src/views/MigrateForm/TokensBeforeAfterDiagram.tsx b/src/views/MigrateForm/TokensBeforeAfterDiagram.tsx index f9e2791..037a77a 100644 --- a/src/views/MigrateForm/TokensBeforeAfterDiagram.tsx +++ b/src/views/MigrateForm/TokensBeforeAfterDiagram.tsx @@ -19,12 +19,12 @@ export const TokensBeforeAfterDiagram = () => { const detailItems = [ { key: 'before', - label: stringGetter({ key: STRING_KEYS.BEFORE }), + label: "Before", value: ( - + { type={OutputType.Asset} fractionDigits={TOKEN_DECIMALS} /> - ethDYDX + ethDORA - {stringGetter({ - key: STRING_KEYS.ON_CHAIN, - params: { - CHAIN: 'Ethereum', - }, - })} + on Ethereum @@ -47,35 +42,30 @@ export const TokensBeforeAfterDiagram = () => { }, { key: 'after', - label: stringGetter({ key: STRING_KEYS.AFTER }), + label: "After", value: ( - + - wethDYDX + ethDORA - {stringGetter({ - key: STRING_KEYS.ON_CHAIN, - params: { - CHAIN: 'Ethereum', - }, - })} + on Ethereum - - + + { fractionDigits={TOKEN_DECIMALS} showSign={ShowSign.Both} /> - DYDX + DORA - {stringGetter({ - key: STRING_KEYS.ON_CHAIN, - params: { - CHAIN: 'dYdX Chain', - }, - })} + on Dora Vota diff --git a/src/views/MigratePanel.tsx b/src/views/MigratePanel.tsx index c26aa07..4987e79 100644 --- a/src/views/MigratePanel.tsx +++ b/src/views/MigratePanel.tsx @@ -5,7 +5,7 @@ import breakpoints from '@/styles/breakpoints'; import { formMixins } from '@/styles/formMixins'; import { layoutMixins } from '@/styles/layoutMixins'; -import { STRING_KEYS } from '@/constants/localization'; +import { STRING_KEYS, DORA_KEYS } from '@/constants/localization'; import { MigrateFormSteps, TransactionStatus } from '@/constants/migrate'; import { useStringGetter, useMigrateToken } from '@/hooks'; @@ -24,27 +24,30 @@ export const MigratePanel = () => { const { currentStep, onFormSubmit, transactionStatus, bridgeTxError } = useMigrateToken(); + const getTransactionStatusMessage = () => { + if (transactionStatus >= TransactionStatus.Finalized) { + return "Sending Successful"; + } + if (bridgeTxError) { + return "Migration Failed"; + } + return "Sending In Progress"; + }; + const { slotIcon, title, subtitle, content } = { [MigrateFormSteps.Edit]: { slotIcon: , - title: stringGetter({ key: STRING_KEYS.MIGRATE }), - subtitle: stringGetter({ - key: STRING_KEYS.FROM_TO, - params: { - FROM: Ethereum, - TO: dYdX Chain, - }, - }), + title: 'Migrate', + subtitle: `From ${DORA_KEYS.ETHEREUM} to ${DORA_KEYS.VOTA_CHAIN}`, content: , }, [MigrateFormSteps.Preview]: { - title: stringGetter({ key: STRING_KEYS.CONFIRM_MIGRATION }), - subtitle: stringGetter({ - key: STRING_KEYS.TO, - params: { - TO: dYdX Chain, - }, - }), + title: "Confirm migration", + subtitle: ( + <> + To Dora Vota + + ), content: , }, [MigrateFormSteps.Confirmed]: { @@ -56,14 +59,7 @@ export const MigratePanel = () => { ) : ( ), - title: stringGetter({ - key: - transactionStatus >= TransactionStatus.Finalized - ? STRING_KEYS.SENDING_SUCCESSFUL - : bridgeTxError - ? STRING_KEYS.MIGRATION_FAILED - : STRING_KEYS.SENDING_IN_PROGRESS, - }), + title: getTransactionStatusMessage(), content: , }, }[currentStep]; diff --git a/src/views/PendingMigrationsPage.tsx b/src/views/PendingMigrationsPage.tsx index ec13def..dbb046a 100644 --- a/src/views/PendingMigrationsPage.tsx +++ b/src/views/PendingMigrationsPage.tsx @@ -2,7 +2,7 @@ import { useState } from 'react'; import styled, { AnyStyledComponent } from 'styled-components'; import { CaretDownIcon } from '@radix-ui/react-icons'; -import { STRING_KEYS } from '@/constants/localization'; +import { STRING_KEYS, DORA_LONG_SENTENCE } from '@/constants/localization'; import { layoutMixins } from '@/styles/layoutMixins'; import breakpoints from '@/styles/breakpoints'; @@ -21,20 +21,15 @@ export const PendingMigrationsPage = () => {

- {stringGetter({ key: STRING_KEYS.PENDING_MIGRATIONS_DESCRIPTION_I })} - {!showFullDescription && '..'} + {DORA_LONG_SENTENCE.PENDING_MIGRATIONS_DESCRIPTION_I}

{showFullDescription && ( <>

- {stringGetter({ - key: STRING_KEYS.PENDING_MIGRATIONS_DESCRIPTION_II, - })} + {DORA_LONG_SENTENCE.PENDING_MIGRATIONS_DESCRIPTION_II}

- {stringGetter({ - key: STRING_KEYS.PENDING_MIGRATIONS_DESCRIPTION_III, - })} + {DORA_LONG_SENTENCE.PENDING_MIGRATIONS_DESCRIPTION_III}

)} @@ -42,9 +37,7 @@ export const PendingMigrationsPage = () => { onPressedChange={setShowFullDescription} slotRight={} > - {stringGetter({ - key: showFullDescription ? STRING_KEYS.VIEW_LESS : STRING_KEYS.VIEW_MORE, - })} + {showFullDescription ? "View Less" : "View More"}
diff --git a/src/views/PendingMigrationsTable.tsx b/src/views/PendingMigrationsTable.tsx index 5156057..122abaa 100644 --- a/src/views/PendingMigrationsTable.tsx +++ b/src/views/PendingMigrationsTable.tsx @@ -1,4 +1,5 @@ import styled, { type AnyStyledComponent } from 'styled-components'; +import { Link } from '@/components/Link'; import { STRING_KEYS, StringGetterFunction } from '@/constants/localization'; @@ -24,15 +25,17 @@ import { type ColumnDef, Table, TableCell } from '@/components/Table'; import { VerticalSeparator } from '@/components/Separator'; import { WithLabel } from '@/components/WithLabel'; -import { truncateAddress } from '@/lib/wallet'; +import { truncateAddress, truncateTransactionHash } from '@/lib/wallet'; import { formatRelativeTimeFromMs } from '@/lib/dateTime'; export enum PendingMigrationsTableColumnKey { Address = 'Address', Amount = 'Amount', - BlockHeight = 'BlockHeight', + // BlockHeight = 'BlockHeight', + TransactionHash = 'TransactionHash', } +const defaultTransactionHash = "0x0000000000000000000000000000000000000000000000000000000000000000" const getPendingMigrationsTableColumnDef = ({ key, latestBlockHeight, @@ -42,12 +45,12 @@ const getPendingMigrationsTableColumnDef = ({ latestBlockHeight?: number; stringGetter: StringGetterFunction; }): ColumnDef => - ( - ({ + (( + { [PendingMigrationsTableColumnKey.Address]: { columnKey: PendingMigrationsTableColumnKey.Address, getCellValue: (row) => row.address, - label: stringGetter({ key: STRING_KEYS.DYDX_CHAIN_ADDRESS }), + label: 'Dora Vota Address', renderCell: ({ address }) => ( {truncateAddress(address)} @@ -57,40 +60,32 @@ const getPendingMigrationsTableColumnDef = ({ [PendingMigrationsTableColumnKey.Amount]: { columnKey: PendingMigrationsTableColumnKey.Amount, getCellValue: (row) => row.amount.toNumber(), - label: stringGetter({ key: STRING_KEYS.AMOUNT }), + label: 'DORA Amount', renderCell: ({ amount }) => ( - + ), }, - [PendingMigrationsTableColumnKey.BlockHeight]: { - columnKey: PendingMigrationsTableColumnKey.BlockHeight, - getCellValue: (row) => row.blockHeight, - label: stringGetter({ key: STRING_KEYS.ESTIMATED_TIME_LEFT }), - renderCell: ({ blockHeight }) => ( + [PendingMigrationsTableColumnKey.TransactionHash]: { + columnKey: PendingMigrationsTableColumnKey.TransactionHash, + getCellValue: (row) => row.txHash, + label: 'Transaction Hash', + renderCell: ({ txHash }) => ( - {latestBlockHeight && blockHeight > latestBlockHeight && ( - + {txHash === defaultTransactionHash ? ( + "0x000000.....000000" + ) : ( + + {truncateTransactionHash((txHash.startsWith('0x') ? txHash.slice(2) : txHash).valueOf())} + )} - - {stringGetter({ key: STRING_KEYS.AVAILABLE_BLOCK })} - - ), }, - }) as Record> - )[key]; + } as Record> + )[key]); export const PendingMigrationsTable = ({ columnKeys = Object.values(PendingMigrationsTableColumnKey), @@ -100,7 +95,7 @@ export const PendingMigrationsTable = ({ className?: string; }) => { const stringGetter = useStringGetter(); - const { dydxAddress } = useAccounts(); + const { DoraAddress } = useAccounts(); const { isNotMobile } = useBreakpoints(); const { @@ -120,13 +115,7 @@ export const PendingMigrationsTable = ({ {showTitle && ( -

{stringGetter({ key: STRING_KEYS.PENDING_MIGRATIONS })}

- {latestBlockHeight && ( - - {stringGetter({ key: STRING_KEYS.LATEST_BLOCK_HEIGHT })}:{' '} - - - )} +

Pending Migrations

)} @@ -134,9 +123,7 @@ export const PendingMigrationsTable = ({ } inputID="search"> ) => { @@ -151,18 +138,18 @@ export const PendingMigrationsTable = ({ )} )} - {dydxAddress && ( + {DoraAddress && ( <> {showAddressSearchInput && } - {stringGetter({ - key: - filter === PendingMigrationFilter.Mine - ? STRING_KEYS.EMPTY_PENDING_MIGRATIONS_MINE - : addressSearchFilter !== '' - ? STRING_KEYS.EMPTY_PENDING_MIGRATIONS_SEARCH - : STRING_KEYS.EMPTY_PENDING_MIGRATIONS_ALL, - params: { - SEARCH_STRING: ( - "{addressSearchFilter}" - ), - }, - })} + {filter === PendingMigrationFilter.Mine ? ( + 'There are no pending migrations with your Dora Vota address currently.' + ) : addressSearchFilter !== '' ? ( + <> + No pending migrations found for + "{addressSearchFilter}" + + ) : ( + 'There are no pending migrations currently.' + )} } /> diff --git a/src/views/PrivacyPolicyPage.tsx b/src/views/PrivacyPolicyPage.tsx index 483aa3e..0ac62b4 100644 --- a/src/views/PrivacyPolicyPage.tsx +++ b/src/views/PrivacyPolicyPage.tsx @@ -4,7 +4,400 @@ export const PrivacyPolicyPage = () => (

Privacy Policy

+ Last updated: Aug 1, 2024
+ +
+

+ This Privacy Policy (this “Policy”) describes + how Matsushiba Foundation and its affiliated companies (collectively, the{' '} + “Company,” “we,” “us,” or “our”) may collect, + process, use, store, and disclose personal information, and your rights and choices + regarding this information when you use and access the specific website for the Token + Migration service at http://vota-migration.dorafactory.org (the{' '} + “Service”). +

+

+ + When you access and use the Service, you agree to be subject to and bound by this Policy. + If you are unwilling or unable to agree with this Policy, you must not use the Service. + +

+

How This Policy Applies

+

+ This Policy applies strictly to information collected through the Service. It does not apply + to any other websites under the same domain as the Service, or third-party applications, + services, or websites, even if you access them through the Service. This Policy does not + apply to information provided to or collected by any third party. When this Policy does not + apply, you should read the applicable privacy policy and related terms posted on the + website, application, or other service you are using before providing any information. +

+

+ note that we may change this Policy from time to time. It is your responsibility to check + this Policy each time you access the Service so that you are aware of such changes, as they + are binding on you. +

+

What Information We Collect

+

+ When you access and use the Service, we or our service providers may collect the following + personal information about you. Please note that the examples below are just personal + information we may have concerning you, but not necessarily information we have received + about you. +

+

+ + Personal information you provide to us + +

    +
  • + Transaction Information: + When you transact or interact with a blockchain, including the migration smart contract, + your transaction, and certain data may be recorded on the blockchain and become publicly + available for others to see, including us. This information generally cannot be deleted + or restricted given the nature of blockchain technology, and it may affect your privacy + rights, particularly the right to delete and restrict. When you interact with the Token + Migration, certain interactions will cause data to be recorded on a blockchain, such as + your public wallet or network address, which we may collect to provide our services to + you. By transacting on a blockchain, you acknowledge that sharing your public wallet or + network address is your decision. If you do not want your privacy rights to be affected, + you should not interact with the blockchain or share your wallet or network address with + us or anyone else, as the blockchain infrastructure may impact those rights. +
  • +
  • + Communication Information: If you reach out + to us for support, you may provide us with your network address, transaction records, + name, physical address, phone number, email, or any other information that you share + with our support team via email, phone, or other communication channels, as well as + information you provide when responding to our surveys or questionnaires. +
  • +
+ + + Information we automatically collect + +

+

+ + We may collect information via cookies and other tracking technologies including: + +

    +
  • + Third-Party Services: We may collect + information about you from third-party services, such as analytics partners, and public + databases (e.g., public blockchain ledger), and we might combine this information with + the data collected from the Service for our own purposes or to provide the Service. +
  • +
  • + Affiliates: We may receive personal + information about you from other sources, such as our affiliated companies. +
  • +
  • + From Others: We may receive information + about you from other users, individuals, or legal authorities due to complaints, + reports, or official requests. +
  • +
+

+

How We Use It

+

Most commonly, we use your personal information in the following circumstances:

+
    +
  • + Service Provision: To provide you with the + Service you've requested. +
  • +
  • + Research and Development: To improve the + Service. We may use your information for development and analysis purposes, such as + improving existing services, creating new ones, and enhancing your overall user + experience. +
  • +
  • + Communication: To send you administrative + communications to keep you informed of our Service, including notifications about changes + to our Service. With your consent, we may also send marketing and promotional materials, + along with updates on our Service. +
  • +
  • + Support: To respond to your inquiries and + provide the support needed to address any issues you might encounter. +
  • +
  • + Security and Legal Compliance: + To enforce our Terms of Use and other policies, to respond to legal requests, to prevent + abuse, fraud, and other malicious activities, and to investigate illegal activities and + enhance security. +
  • +
  • + Anonymization and Aggregation: We may + aggregate or anonymize your personal information (so that it can no longer be associated + with you) for research, statistical, or other purposes. +
  • +
+

How We Share Your Information

+
    +
  • + Our Service Providers: Our services are + supported by third-party vendors, who may, in the course of performing such services for + us (such as hosting, developing, or updating our Service), have access to your personal + information. Service providers may help us administer the Service, provide technical and + customer support, send marketing and promotions to you, communicate with you about our + services, and assist with other legitimate purposes as permitted by law. We contractually + require such service providers to maintain the confidentiality of your information and to + take appropriate measures to keep it secure. +
  • +
  • + Legal Reasons: We may disclose your personal + information in response to subpoenas, warrants, court orders, or other legal processes, or + to comply with relevant laws or regulatory investigations when we believe in good faith + that applicable law requires it. We may also disclose your personal information when we + are investigating potential fraud or when we believe disclosure is necessary or + appropriate to protect the rights, property, or safety of ours, our customers, or others. +
  • +
  • + Affiliates: We may share your personal + information with our parents, subsidiaries, affiliates, or divisions, and their respective + officers, directors, employees, accountants, attorneys, and agents, who may together with + us provide the Service or support us as our service providers. +
  • +
  • + Corporate Change: We may sell, transfer, or + otherwise share some or all of our assets in connection with a merger, reorganization, or + sale of assets or in the event of bankruptcy without your permission. In such an event, + your personal information may be among the assets transferred in such an event. +
  • +
  • + With Your Consent: We may share your + information for other purposes pursuant to your consent or at your direction. +
  • +
+

International Transfers

+

+ We may store, process, and transfer your personal information on servers outside of the EU, + EEA or your home country, which these third countries may not have the same adequate level + of data protection as in your home country. When we transfer your personal information + internationally, we will take appropriate steps to ensure that the information is + transferred and processed in accordance with applicable laws and regulations. By using the + Service, you agree to that transfer. +

+

Data Retention

+

+ We will retain your personal information for as long as needed to fulfill the purposes for + which it was collected and to provide the Service to you. We also will retain and use your + personal information as long as necessary to comply with our business requirements and legal + obligations, to resolve disputes, to protect our assets, and to enforce our Terms of Use, as + permitted by law. +

+

Your Rights and Choices

+

+ You may have certain privacy rights under the law of your home country or the state you + reside, including, but not limited to:{' '} +

+

+ The Right of Access{' '} + - You have the right to receive a copy of your personal information or know what personal + information we have collected about you.{' '} +

+

+ + The Right to Data Portability + {' '} + - You have the right to transfer your personal information to another organization.{' '} +

+

+ + The Right to Correct + {' '} + - You have the right to correct or update your personal information. +

+

+ The Right to Delete{' '} + - You have the right to delete your personal information. +

+

+ + The Right to Restrict + {' '} + - You have the right to restrict certain processing of your information. +

+

+ The Right to Object{' '} + - You have the right to object to certain processing of your information. +

+

+ + The Right to Non-Discrimination + {' '} + - You have the right not to be discriminated against for exercising your privacy rights.{' '} +

+

+ The Right to Appeal{' '} + - If we reject your request, you have the right to appeal.{' '} +

+

+ + The Right to Opt-out Sale or Sharing + {' '} + - You have the right to opt-out of selling or sharing your personal information for + cross-contextual behavioral advertising.{' '} +

+

+ In order for us to review and respond to your requests and determine whether certain privacy + laws apply to us and your personal information, we will require you to verify your identity. + We will review and respond to your requests in accordance with the applicable laws. You may + also have the right to designate an agent to exercise your privacy rights on your behalf.{' '} +

+

+ As we have mentioned above, due to the nature of blockchain technology, data recorded on the + blockchain generally cannot be deleted. This may limit your ability to exercise certain + rights, such as the right to delete, object, or restrict the processing of your personal + information.{' '} +

+

+ To submit a request or inquire about your privacy rights, please contact us using the + contact information provided at the end of this Policy. +

+

Notice for European Residents

+

+ To the extent that applicable laws, such as those in the EEA, the United Kingdom, or + Switzerland, require us to have a legal basis for processing your personal information, we + will do so on the following lawful basis: +

+
    +
  • + We have a necessary and legitimate interest in providing the Service you have requested, + operating our business, or complying with your instructions or other contractual + obligations between you and us (e.g., to operate, provide, maintain, and improve the + Service, to communicate with you, and conduct research and development). +
  • +
  • + To comply with our legal obligations as well as to keep records of our compliance + processes. +
  • +
  • + We have your express consent to process your personal information in a particular manner. +
  • +
+

+ You also may have the right to lodge complaints before the competent data protection + authority (“DPA”), usually the DPA of your home country. Contact details are available via + the links below: +

+

+ For EEA: https://edpb.europa.eu/about-edpb/board/members_en +
+ For the UK: ​https://ico.org.uk/global/contact-us/ +
+ For Switzerland: https://www.edoeb.admin.ch/edoeb/en/home/the-fdpic/contact.html +

+ +

Security

+

+ The security of your personal information is important to us, but remember that no method of + transmission over the Internet, or method of electronic storage is 100% secure. While we + strive to use appropriate technical and organizational measures to protect your personal + information, we cannot guarantee its absolute security. +

+ +

Age Limitations

+

+ All users who are minors in the jurisdiction in which they reside (generally under the age + of 18) must have the permission of, and be directly supervised by, their parent or guardian + to use the Service. If you are a minor, you must have your parent or guardian read and agree + to this Policy prior to you using the Service. Persons under the age of 13 (or under the age + of 16 for residents of the United Kingdom and the European Union) are not permitted to use + or access the Service or provide any information about yourself to us for the Service. +

+ +

Cookies and Other Tracking Technologies

+

+ We may use the cookies below to help us provide the Service and obtain certain information + by automated means. A cookie is a small file of letters and numbers that is stored on your + browser or mobile device when you visit a website. Cookies contain information that is + transferred to your device’s local storage. +

+
    +
  • + Strictly necessary cookies: These cookies are + required for the operation of the Service on the website. For example, they allow us to + enable security, prevent fraud, and debug the website.{' '} +
  • +
  • + Analytical or performance cookies:These + cookies allow us to recognize and count the number of visits and to see how users move + around a website when they are using it. This helps to improve the way a website works, + for example, by ensuring that users are finding what they are looking for easily. +
  • +
  • + Functionality cookies: These cookies are used + to remember you when you return to a website. This enables the website to remember your + preferences. For example, your language preference. +
  • + Targeting cookies: +
  • + These cookies record your visit to our website, the pages you have visited, and the links + you have followed. We will use this information to make our website and the advertising + displayed on it more relevant to your interests. We may also share this information with + third parties for this purpose. +
  • +
+

+ The length of time a cookie remains on your browsing device depends on whether it is a + "persistent" or "session" cookie. Session cookies will only stay on your device until you + close your browser. Persistent cookies are set to automatically expire after a defined + duration. You have the right to decide whether to accept or reject cookies (except strictly + necessary cookies). The cookies we use with the Service are either set by us (first-party + cookies) or by a third party at our request (third-party cookies). We do not control these + third parties' cookies or how they may be used. +

+

+ You can prevent certain web-based cookies from being downloaded on your device by adjusting + the settings in your web browser. Most web browsers will provide information on how to stop + accepting new cookies, how to receive notifications when you get a new cookie, and how to + disable existing cookies. However, please note that without cookies, you may not be able to + fully utilize all the features of our online services. +

+

+ We may use Google Analytics on the Service. If you would like to opt out of Google Analytics + please opt-out at https://tools.google.com/dlpage/gaoptout/. +

+ +

Third-Party Websites

+

+ Our Service may contain links to other websites that we do not operate. If You click on a + third-party link, you will be directed to that third-party’s site. We strongly advise you to + review the privacy policy of every site you visit. We have no control over and assume no + responsibility for the content, privacy policies, or practices of any third-party sites or + services. +

+ +

Changes to This Policy

+

+ We may update our Policy from time to time. We will notify you of any changes by posting the + new Policy on this page. We may let you know via email or a prominent notice on our website, + prior to the change becoming effective, and update the “Last updated” date at the top of + this Policy. You are advised to review this Policy periodically for any changes. Changes to + this Policy are effective when they are posted on this page. Your continued use of the + Service after We make changes to the Policy is deemed to be acceptance of those changes. +

+ +

Do Not Track

+

+ Do Not Track (“DNT”) is a privacy preference that users can set in certain web browsers and + devices. DNT is a way for users to inform websites and services that they do not want + certain information about their webpage visits collected over time and across websites or + online services. Please note that we do not respond to or honor DNT signals or similar + mechanisms transmitted by web browsers. +

+ +

Contact Us

+

+ If you have any questions about this website, or this Privacy Policy, or if you would like + to inquire about your privacy rights, please contact us at:{' '} + + support@dorafactory.org + +

+
); diff --git a/src/views/TermsOfUsePage.tsx b/src/views/TermsOfUsePage.tsx index 37363ff..507f104 100644 --- a/src/views/TermsOfUsePage.tsx +++ b/src/views/TermsOfUsePage.tsx @@ -4,7 +4,1372 @@ export const TermsOfUsePage = () => (

Terms of Use

+ Last updated: Aug 1, 2024
+
+

+ This Terms of Use Agreement (“Agreement”) constitutes a legally binding agreement made + between you, whether personally or on behalf of an entity (“user” or “you” or “developer”) + and the Dora Grant DAO Foundation, the Matsushiba Foundation, both of which are Cayman + Islands foundations (collectively, the “Company” or “we” or “us” or “our”), concerning your + access to and use of the website at{' '} + https://dorafactory.org, any services linked to this + Agreement, and any other media form, media channel, mobile website or mobile application + related or connected thereto (collectively, the “Website”). +

+

+ The Website, Dora Vota's block explorer, MACI/aMACI explorers, and the Token Migration + interface are provided by Matsushiba Foundation (“MF”); the Public Good Staking portal + described below is provided by Dora Grant DAO Foundation (“DGD”). MF is not responsible for + Public Good Staking and DGD is not responsible for other aspects of the Website and + functionalities. +

+

+ + Note: This Agreement has an arbitration provision, which provision is contained below + under the heading “Disputes and Choice of Law”. To the maximum extent permitted by law, + you agree that disputes between you and company will be resolved by binding, individual + arbitration, and you waive your right to a trial by jury or to participate as a plaintiff + or class member in any purported class action or other representative proceeding. + +

+ +

+ The Website provides access to the following functions: +

    +
  • + Staking: Subject to these Staking Terms of + Use, we operate and monitor validator nodes and software to perform non-custodial + validation-as-a-service or otherwise participate in staking protocols in connection with + Digital Assets solely for supported blockchains (the “staking functions”). The staking + functions are strictly offered on a non-custodial basis. Additionally, you authorize + Dora Factory to exercise your voting rights associated with Digital Assets that you + delegate to our validator addresses; provided, however, that (i) voting rights are only + applicable for certain supported blockchains, and (ii) we may exercise voting rights + solely at our own discretion, and we are under no obligation to exercise voting rights + on your behalf. Notwithstanding the foregoing, you also retain a right to exercise such + voting rights associated with any such Digital Assets and, subject to the protocols of + the applicable supported blockchain, may exercise those rights before we do or may + change any vote that we cast after we have done so. +
  • +
  • + Public Good Staking: This is an + infrastructure by which institutions and individuals can support ecosystems through the + staking of tokens. Some ecosystems are yet to be generally available for Public Good + Staking. This part of the website includes a published dashboard for all the + nodes/validators available for Public Good Staking, as well as a list of all governance + proposals and voting results Dora Factory has made on behalf of the nodes/validators. +
  • +
  • + Vota Explorer: This is a website that + displays voting rounds and related information on Dora Vota special-purpose blockchain. + Information displayed on this website includes but is not limited to voting round + information, voting transactions, voting logics (expressed in circuits), zero-knowledge + proof information. The website also provides an interface for users to deploy voting + rounds and an interface for users to vote in a voting round. +
  • +
  • + QRNG API: An experimental API service to + generate random numbers from cloud-based quantum computers. The API is only available + for a limited number of requests. +
  • +
  • + Token Migration: An interface that provides + access to a protocol migrating DORA tokens from Ethereum network to Dora Vota’s network. +
  • +
+

+

+ All of the foregoing shall be referred to collectively as the “Functions”. Supplemental + terms and conditions or documents that may be posted on the Website from time to time, are + hereby expressly incorporated into this Agreement by reference. +

+

+ Company makes no representation that the Website or Company Functions are appropriate or + available outside Cayman Islands. The Website and Company Services are not intended for + distribution to or use by any person or entity in any jurisdiction or country where such + distribution or use would be contrary to law or regulation or which would subject Company to + any registration requirement within such jurisdiction or country and some of the Company + Services may not be available in certain jurisdictions (“Excluded Jurisdictions”) or are + barred from using the Website and Company Services by export controls, sanctions or other + laws (“Excluded Individuals”). Accordingly, those persons who choose to access the Website + or Company Services from other locations do so on their own initiative and are solely + responsible for compliance with local laws, if and to the extent local laws are applicable. +

+

Eligibility

+

+ You cannot register for or use the Website or Company Services if you do not have legal + capacity to enter contracts and are resident or accessing the Website or Company Services + from any Excluded Jurisdiction or are an Excluded Individual. +

+

+ All users who are minors in the jurisdiction in which they reside (generally under the age + of 18) must have the permission of, and be directly supervised by, their parent or guardian + to use the Website. If you are a minor, you must have your parent or guardian read and agree + to this Agreement prior to you using the Website. Persons under the age of 13 (or under the + age of 16 for residents of the United Kingdom and the European Union) or in an Excluded + Jurisdiction are not permitted to register for the Website or use the Company Services. +

+

+ + You accept and agree to be bound by this agreement by acknowledging such acceptance during + the registration process (if applicable) or by accessing and using the Website and Company + Services. If you do not agree to abide by this Agreement, or to modifications that Company + may make to this agreement in the future, do not use or access or continue to use or + access the Company Services or the Website. + +

+ +

Digital Assets

+

+ The Company Services may allow developers and users to donate and store locally on their own + devices, tokens, cryptocurrencies and other crypto or blockchain-based digital assets + (collectively, “Digital Assets”). Digital Assets are not intended to be securities or for + investment purposes. +

+ +

Accounts and Wallets

+

+ You do not need an account to use the Website and Company Services; however, you do need a + wallet hosted by one of the accepted wallet providers—ERC 20, Cosmos or Aptos (“Wallet”) to + engage in Staking, Public Good Staking, or Token Migration. +

+

+ If you create an account, you agree to provide complete and accurate information for that + account and keep that account updated. You are responsible for the safety and security of + any credentials required to access your account or Wallet. You are responsible for all + transactions on the Website or Company Services using your Wallet or account, whether + authorized or not. +

+ +

Staking

+

+ You agree and understand that this Staking Agreement is subject to the terms and conditions + set forth in your User Agreement(s), which also govern this Staking Agreement. In case of + conflict, the User Agreement(s) shall control. You further agree and understand that the + defined terms used in this Staking Agreement, if defined in your User Agreement(s), shall + have the meanings set forth in your User Agreement(s). Your use of our Staking service must + comply with your User Agreement(s). +

+

+ You agree and understand that by accessing or using Staking following any change to this + Staking Agreement, your access or use of Staking shall constitute your agreement to the + amended Staking Agreement, and you agree to be legally bound by its terms and conditions as + amended. You should, therefore, read this Staking Agreement from time to time. If you do not + agree to be bound by this Staking Agreement, you should not access or use Staking. +

+ +

Permission

+

+ Subject to the terms and conditions set forth in this Staking Agreement, we hereby grant to + you a non-assignable, non-exclusive, worldwide, and royalty-free limited license to use our + Staking service. You may not use the Staking service if (a) you are not at least 18 years + old and do not have the legal capacity to enter into this Staking Agreement, (b) you are a + person barred from using Staking under the applicable laws of the United States or other + countries, including the country in which you are resident or from which you use Staking, + and (c) you do not agree to be legally bound by the terms and conditions of this Staking + Agreement in their entirety. +

+ +

Availability

+

+ When you hold certain supported Digital Assets in your wallet you may be given the option to + “stake” these assets in a proof of stake network via staking services provided by Dora + Factory. Dora Factory Staking Services are not currently available to customers in sanction + countries and certain other jurisdictions. +

+ +

The Service; Rewards; Services Fee; Limitations

+

+ If you instruct Dora Factory to stake your assets, Dora Factory or a Staking Services + Provider will facilitate the staking of those assets on your behalf by acting as a + transaction validator on the applicable network for the supported Digital Asset you stake. +

+

+ If Dora Factory or the Staking Services Provider successfully validates a block of + transactions in that supported Digital Asset, you will earn a reward granted by that + supported Digital Asset’s network. The timing and amount of your reward will be determined + by the protocols of the applicable network. Dora Factory does not determine the timing or + amount of your reward. There may be a delay between when you choose to stake your supported + Digital Asset and when you start owing rewards because each network protocol’s activation + queue determines when rewards start accruing. +

+

+ Dora Factory will distribute any earned rewards to you after receipt by Dora Factory, minus + a Staking Services Fee of 5%–100% of the rewards determined by the protocol. Some of the + Staking Services Fee is used to pay ‘gas’ fees, third party fees, and infrastructure costs + associated with staking and the remainder is retained by Dora Factory. +

+

+ Some Digital Asset networks subject staked assets to “slashing” if the transaction validator + representing those assets incorrectly validates a transaction. Dora Factory and/or the + Staking Services Provider will use commercially reasonable efforts to prevent any staked + assets from slashing; however, in the event they are, unless otherwise provided in this + Staking Agreement or your User Agreement, Dora Factory will promptly replace your assets at + no additional cost. For the avoidance of doubt, Dora Factory will not replace assets subject + to slashing as a result of network, network provider, or operator errors, or any other basis + covered by this Staking Agreement or in the Disclaimer of Liability or Force Majeure + sections of your User Agreement. +

+

+ Some Digital Asset Networks may impose a waiting period while staking, during which your + staked assets will be restricted from transfer or sale (“Unbonding Period”). If you instruct + Dora Factory to unstake your assets, you will not have access to your assets until the + expiration of any applicable Unbonding Period. Dora Factory may also impose an additional + waiting period in order to facilitate transfer of your assets to your Dora Factory Account + after the expiration of any applicable Unbonding Period. If rewards are collected on your + staked assets during an Unbonding Period, Dora Factory will credit these rewards to your + Dora Factory Account. +

+ +

No Guarantee of Rewards

+

+ The staking rewards and reward rates vary by the networks. This rate is an estimate and may + change over time. Dora Factory DOES NOT GUARANTEE THAT YOU WILL RECEIVE ANY STAKING REWARDS, + INCLUDING THE STAKING REWARDS RATES. +

+ +

Governance and Voting

+

+ For certain Digital Assets, the underlying protocols offer stakers the ability to vote on + matters related to the governance of protocol-level issues. Dora Factory may or may not + support voting for such assets, and may cease supporting voting at any time in its + discretion. In certain cases, Dora Factory may vote on your behalf where Dora Factory or the + applicable protocol does not support delegated voting. In such instances, Dora Factory will + vote in favor of the decision that it believes supports the most use cases for Dora Factory + users. +

+ +

Suspension or Termination

+

+ You agree and understand that we may, in our sole discretion, change, suspend, discontinue, + or terminate any aspect of our Staking service, or its availability to you, at any time and + without notice. +

+ +

Warranties and Limitation of Liability

+

+ You represent and warrant that you have the requisite authority to enter into this Staking + Agreement according to its terms, and its performance does not violate any laws, + regulations, or agreements applicable to you. +

+

+ + The Staking service is provided on an “as is” basis. You and your affiliates disclaim any + and all express or implied warranties, including, any warranties of merchantability, + fitness for a particular purpose or use, or of non-infringement or any other violation of + any third party intellectual property rights. We and our affiliates do not guarantee or + make any warranty concerning the accuracy or reliability of the staking service, and + disclaim any and all liability (whether in tort or contract or otherwise) for any direct, + indirect, incidental, special, or consequential loss or damage arising from any delay or + loss of access to the staking service, or otherwise in connection with the Staking + service, staking rewards, or staked digital assets. + +

+

+ + To the maximum extent permitted under applicable law, and notwithstanding any of the + foregoing, we will have no liability under this staking agreement. For the avoidance of + doubt, the warranties and limitations of liability set forth in this section are in + addition to, and not in place of, those set forth in the user agreement. + +

+ +

Public Good Staking

+

+ Public Good Staking allows users to stake certain tokens to Dora Factory’s node validators. +

+

+ Public Good Staking is conducted through staking tokens using a Wallet to interact with + related smart contracts or allocate tokens through smart contracts. Each ecosystem will have + minimum and maximum periods to the staking of tokens. Each ecosystem will also have + requirements when users unstake tokens from our validators. +

+

+ Dora Factory might from time to time, in its sole direction, decide to implement incentive + programs for users to participate in Public Good Staking. Such incentive programs will be + determined solely by Dora Factory, and be published on Dora Factory’s social media accounts + or websites. Users might also receive governance credit for use in connection with voting + relating to that ecosystem. +

+ +

+ + You accept and agree to be bound by this Agreement by acknowledging such acceptance during + the registration process (if applicable) or by accessing and using the Website and Company + Services. If you do not agree to abide by this Agreement, or to modifications that Company + may make to this Agreement in the future, do not use or access or continue to use or + access the Company Services or the Website. + +

+ +

Token Migration

+

+ The Token Migration interface allows you to access a protocol that migrates your + Ethereum-based ERC-20 DORA tokens to the Dora Vota network (i.e., a CosmosSDK appchain). You + may be required to pay a “gas” fee associated with interacting with the Ethereum blockchain + network, and you are solely responsible for these fees and costs. Once you initiate a + migration, it cannot be modified, reversed, or otherwise altered, and the originating + Ethereum-based ERC-20 DORA tokens will be unrecoverable. While migration will typically be + completed within 48 hours, delays may occur due to network congestion. +

+ +

Your Responsibility in Token Migration

+

+ You are solely responsible for ensuring that you own and have access to the network + addresses used in the Token Migration interface. We cannot and are unable to verify the + ownership of your network addresses. You are also solely responsible for ensuring that your + Ethereum Wallet is properly connected and that the Dora Vota network address you entered is + correct and complete. Any errors in your network addresses, specifically your Dora Vota + network address, whether due to human error, technical failure, or other causes, may result + in irreversible loss of your Digital Assets. We are not liable for any damages you may + suffer as a result of any incorrect or incomplete network address, or your loss of access to + your Wallet or network address, including permanent loss of Digital Assets, failed + transactions, or the inability to access your Digital Assets. You own and control the + Digital Assets in your Wallet and network address, and are solely responsible for their + security. We do not have control or custody over the content and Digital Assets in your + Wallet or network address. +

+

Smart Contract Risks

+

+ By using the Token Migration interface, you agree and understand that your use is at your + own risk. You are interacting directly with smart contracts on the networks, which involves + inherent risks, including, but not limited to, bugs, exploits, technical issues, errors, and + other potential vulnerabilities, and we are not liable for any such risks. We do not execute + any transactions on your behalf. Any transactions initiated by you on the smart contracts + cannot be modified or reversed. +

+

Availability

+

+ We reserve the right to modify, suspend, or terminate the Token Migration interface at any + time without notice to you. We do not guarantee and warrant that the Token Migration + interface will always be available to you. The Token Migration interface is not currently + available to customers in sanction countries and certain other jurisdictions, specifically + customers residing in Excluded Jurisdiction or who are Excluded Individuals. +

+

Disclaimers

+

+ + The Token Migration interface is a non-exclusive means to facilitate access to the + relevant protocol and/or smart contracts, and is provided on an “as is” basis. We and our + affiliates disclaim any and all express or implied warranties, including, any warranties + of merchantability, fitness for a particular purpose or use, or of non-infringement or any + other violation of any third party intellectual property rights. We and our affiliates do + not guarantee or make any warranty concerning the accuracy or reliability of the Token + Migration interface, and disclaim any and all liability (whether in tort or contract or + otherwise) for any direct, indirect, incidental, special, or consequential loss or damage + arising from any delay or loss of access to the Token Migration interface, including the + bridging smart contract or otherwise in connection with the Token Migration interface. + +

+ +

User Representations

+

Regarding Your Registration

+

+ By using the Company Services, you represent and warrant that: +

    +
  • all registration information you submit is truthful and accurate;
  • +
  • you will maintain the accuracy of such information;
  • +
  • + you will keep your password confidential and will be responsible for all use of your + password and account; +
  • +
  • + you are not a minor in the jurisdiction in which you reside, or if a minor, you have + received parental permission to use this Website; and +
  • +
  • + your use of the Company Services does not violate any applicable law or regulation. +
  • +
+

+

+ You also agree to: (a) provide true, accurate, current and complete information about + yourself as prompted by the Website’s registration form and (b) maintain and promptly update + registration data to keep it true, accurate, current and complete. If you provide any + information that is untrue, inaccurate, not current or incomplete, or Company has reasonable + grounds to suspect that such information is untrue, inaccurate, not current or incomplete, + Company has the right to suspend or terminate your account and refuse any and all current or + future use of the Website (or any portion thereof). +

+

+ We reserve the right to remove or reclaim or change a user name you select if we determine + appropriate in our discretion, such as when the user name is obscene or otherwise + objectionable or when a trademark owner complains about a username that does not closely + relate to a user's actual name. +

+ +

Regarding Content You Provide

+

+ The Website may invite you to chat or participate in blogs, message boards, online forums + and other functionality and may provide you with the opportunity to create, submit, post, + display, transmit, perform, publish, distribute or broadcast content and materials to + Company and/or to or via the Website, including, without limitation, text, writings, video, + audio, photographs, graphics, comments, suggestions, subjects or personally identifiable + information or other material (collectively “Contributions”). Any Contributions you transmit + to Company will be treated as non-confidential and non-proprietary. When you create or make + available a Contribution, you thereby represent and warrant that: +

+
    +
  • + the creation, distribution, transmission, public display and performance, accessing, + downloading and copying of your Contribution does not and will not infringe the + proprietary rights, including but not limited to the copyright, patent, trademark, trade + secret or moral rights of any third party; +
  • +
  • + you are the creator and owner of or have the necessary licenses, rights, consents, + releases and permissions to use and to authorize Company and the Website users to use your + Contributions as necessary to exercise the licenses granted by you under this Agreement; +
  • +
  • + you have the written consent, release, and/or permission of each and every identifiable + individual person in the Contribution to use the name or likeness of each and every such + identifiable individual person to enable inclusion and use of the Contribution in the + manner contemplated by this Website; +
  • +
  • + your Contribution is not obscene, lewd, lascivious, filthy, violent, harassing or + otherwise objectionable (as determined by Company), libelous or slanderous, does not + ridicule, mock, disparage, intimidate or abuse anyone, does not advocate the violent + overthrow of any government, does not incite, encourage or threaten physical harm against + another, does not violate any applicable law, regulation, or rule, and does not violate + the privacy or publicity rights of any third party; +
  • +
  • + your Contribution does not contain material that solicits personal information from anyone + under 18 or exploit people under the age of 18 in a sexual or violent manner, and does not + violate any law concerning child pornography or otherwise intended to protect the health + or well-being of minors; +
  • +
  • + your Contribution does not include any offensive comments that are connected to race, + national origin, gender, sexual preference or physical handicap; and +
  • +
  • + your Contribution does not otherwise violate, or link to material that violates, any + provision of this Agreement or any applicable law or regulation. +
  • +
+ +

Contribution License

+

+ By posting Contributions to any part of the Website or Company Services, or making them + accessible to the Website or Company Services by linking your account to any of your social + network accounts, you automatically grant, and you represent and warrant that you have the + right to grant, to Company an unrestricted, unconditional, unlimited, irrevocable, + perpetual, non-exclusive, transferable, royalty-free, fully-paid, worldwide right and + license to host, use, copy, reproduce, disclose, sell, resell, publish, broadcast, retitle, + archive, store, cache, publicly perform, publicly display, reformat, translate, transmit, + excerpt (in whole or in part) and distribute such Contributions (including, without + limitation, your image and voice) for any purpose, commercial, advertising, or otherwise, to + prepare derivative works of, or incorporate into other works, such Contributions, and to + grant and authorize sublicenses of the foregoing. The use and distribution may occur in any + media formats and through any media channels. Such use and distribution license will apply + to any form, media, or technology now known or hereafter developed, and includes our use of + your name, company name, and franchise name, as applicable, and any of the trademarks, + service marks, trade names and logos, personal and commercial images you provide. Company + does not assert any ownership over your Contributions; rather, as between us and you, + subject to the rights granted to us in this Agreement, you retain full ownership of all of + your Contributions and any intellectual property rights or other proprietary rights + associated with your Contributions. +

+

+ Company has the right, in our sole and absolute discretion, to (a) edit, redact or otherwise + change any Contributions, (b) re-categorize any Contributions to place them in more + appropriate locations or (c) pre-screen or delete any Contributions that are determined to + be inappropriate or otherwise in violation of this Agreement. +

+

+ By uploading your Contributions to the Website, you hereby authorize Company to grant to + each end user a personal, limited, non-transferable, perpetual, non-exclusive, royalty-free, + fully-paid license to access, download, print and otherwise use your Contributions for their + internal purposes and not for distribution, transfer, sale or commercial exploitation of any + kind. +

+ +

Guidelines for Reviews

+

+ Company may accept, reject or remove reviews in its sole discretion. Company has absolutely + no obligation to screen reviews or to delete reviews, even if anyone considers reviews + objectionable or inaccurate. Those persons posting reviews should comply with the following + criteria: (a) reviewers should have firsthand experience with the person/entity being + reviewed; (b) reviews should not contain: offensive language, profanity, or abusive, racist, + or hate language; discriminatory references based on religion, race, gender, national + origin, age, marital status, sexual orientation or disability; or references to illegal + activity; (c) reviewers should not be affiliated with competitors if posting negative + reviews; (d) reviewers should not make any conclusions as to the legality of conduct; and + (e) reviewers may not post any false statements or organize a campaign encouraging others to + post reviews, whether positive or negative. Reviews are not endorsed by Company, and do not + represent the views of Company or of any affiliate or partner of Company. Company does not + assume liability for any review or for any claims, liabilities or losses resulting from any + review. By posting a review, the reviewer hereby grants to Company a perpetual, + non-exclusive, worldwide, royalty-free, fully-paid, assignable and sublicensable license to + Company to reproduce, modify, translate, transmit by any means, display, perform and/or + distribute all content relating to reviews. +

+ +

Mobile Application License

+

Use License

+

+ If you are accessing the Company Services via a mobile application, then Company grants you + a revocable, nonexclusive, non-transferable, limited right to install and use the + application on wireless handsets owned and controlled by you, and to access and use the + application on such devices strictly in accordance with the terms and conditions of this + license. You shall use the application strictly in accordance with the terms of this license + and shall not: +

+
    +
  • + decompile, reverse engineer, disassemble, attempt to derive the source code of, or decrypt + the application; +
  • +
  • + make any modification, adaptation, improvement, enhancement, translation or derivative + work from the application; +
  • +
  • + violate any applicable laws, rules or regulations in connection with your access or use of + the application; +
  • +
  • + remove, alter or obscure any proprietary notice (including any notice of copyright or + trademark) of Company or its affiliates, partners, suppliers or the licensors of the + application; +
  • +
  • + use the application for any revenue generating endeavor, commercial enterprise, or other + purpose for which it is not designed or intended; +
  • +
  • + make the application available over a network or other environment permitting access or + use by multiple devices or users at the same time; +
  • +
  • + use the application for creating a product, service or software that is, directly or + indirectly, competitive with or in any way a substitute for the application; +
  • +
  • + use the application to send automated queries to any website or to send any unsolicited + commercial email; or +
  • +
  • + use any proprietary information or interfaces of Company or other intellectual property of + Company in the design, development, manufacture, licensing or distribution of any + applications, accessories or devices for use with the application. +
  • +
+ +

Terms Applicable to Apple and Android Devices

+

+ The following terms apply when you use a mobile application obtained from either the Apple + Store or Google Play to access the Company Services. You acknowledge that this Agreement is + concluded between you and Company only, and not with Apple Inc. or Google, Inc. (each an + “App Distributor”), and Company, not an App Distributor, is solely responsible for the + Company application and the content thereof. +

+
    +
  • + Scope of license: The license granted to you for the Company application is limited to a + non-transferable license to use the Company application on a device that utilizes the + Apple iOS or Android operating system, as applicable, and in accordance with the usage + rules set forth in the applicable App Distributor terms of service. +
  • +
  • + Maintenance and support: Company is solely responsible for providing any maintenance and + support services with respect to the Company application, as specified in this Agreement, + or as required under applicable law. You acknowledge that each App Distributor has no + obligation whatsoever to furnish any maintenance and support services with respect to the + Company application. +
  • +
  • + Warranty: Company is solely responsible for any product warranties, whether express or + implied by law, to the extent not effectively disclaimed. In the event of any failure of + the Company application to conform to any applicable warranty, you may notify an App + Distributor, and the App Distributor, in accordance with its terms and policies, may + refund the purchase price, if any, paid for the Company application, and to the maximum + extent permitted by applicable law, an App Distributor will have no other warranty + obligation whatsoever with respect to the Company application, and any other claims, + losses, liabilities, damages, costs or expenses attributable to any failure to conform to + any warranty will be Company’s sole responsibility. +
  • +
  • + Product claims: You acknowledge that Company, not an App Distributor, is responsible for + addressing any claims of yours or any third party relating to the Company application or + your possession and/or use of the Company application, including, but not limited to: (a) + product liability claims; (b) any claim that the Company application fails to conform to + any applicable legal or regulatory requirement; and (c) claims arising under consumer + protection or similar legislation. +
  • +
  • + Intellectual property rights: You acknowledge that, in the event of any third party claim + that the Company application or your possession and use of the Company application + infringes a third party’s intellectual property rights, the App Distributor will not be + responsible for the investigation, defense, settlement and discharge of any such + intellectual property infringement claim. +
  • +
  • + Legal compliance: You represent and warrant that (d) you are not located in a country that + is subject to a U.S. government embargo, or that has been designated by the U.S. + government as a “terrorist supporting” country; and (e) you are not listed on any U.S. + government list of prohibited or restricted parties. +
  • +
  • + Third party terms of agreement: You must comply with applicable third party terms of + agreement when using the Company application, e.g., if you have a VoIP application, then + you must not be in violation of their wireless data service agreement when using the + Company application. +
  • +
  • + Third party beneficiary: Company and you acknowledge and agree that the App Distributors, + and their subsidiaries, are third party beneficiaries of this Agreement, and that, upon + your acceptance of the terms and conditions of this Agreement, each App Distributor will + have the right (and will be deemed to have accepted the right) to enforce this Agreement + against you as a third party beneficiary thereof. +
  • +
+ +

Social Media

+

+ As part of the functionality of the Website, you may link your account with online accounts + you may have with third party service providers (each such account, a “Third Party Account”) + by either: (a) providing your Third Party Account login information through the Website; or + (b) allowing Company to access your Third Party Account, as is permitted under the + applicable terms and conditions that govern your use of each Third Party Account. You + represent that you are entitled to disclose your Third Party Account login information to + Company and/or grant Company access to your Third Party Account (including, but not limited + to, for use for the purposes described herein), without breach by you of any of the terms + and conditions that govern your use of the applicable Third Party Account and without + obligating Company to pay any fees or making Company subject to any usage limitations + imposed by such third party service providers. By granting Company access to any Third Party + Accounts, you understand that (a) Company may access, make available and store (if + applicable) any content that you have provided to and stored in your Third Party Account + (the “Social Network Content”) so that it is available on and through the Website via your + account, including without limitation any friend lists, and (b) Company may submit and + receive additional information to your Third Party Account to the extent you are notified + when you link your account with the Third Party Account. Depending on the Third Party + Accounts you choose and subject to the privacy settings that you have set in such Third + Party Accounts, personally identifiable information that you post to your Third Party + Accounts may be available on and through your account on the Website.{' '} + + Please note that if a Third Party Account or associated service becomes unavailable or + Company’s access to such Third Party Account is terminated by the third party service + provider + + , then Social Network Content may no longer be available on and through the Website. You + will have the ability to disable the connection between your account on the Website and your + Third Party Accounts at any time. Please note that your relationship with the third party + service providers associated with your third party accounts is governed solely by your + agreement(s) with such third party service providers. Company makes no effort to review any + Social Network Content for any purpose, including but not limited to, for accuracy, legality + or non-infringement, and Company is not responsible for any Social Network Content. You + acknowledge and agree that Company may access your email address book associated with a + Third Party Account and your contacts list stored on your mobile device or tablet computer + solely for the purposes of identifying and informing you of those contacts who have also + registered to use the Website. At your request made via email to our email address listed + below, or through your account settings (if applicable), Company will deactivate the + connection between the Website and your Third Party Account and delete any information + stored on Company’s servers that was obtained through such Third Party Account, except the + username and profile picture that become associated with your account. +

+ +

Submissions

+

+ You acknowledge and agree that any questions, comments, suggestions, ideas, feedback or + other information about the Website or the Company Services (“Submissions”) provided by you + to Company are non-confidential and Company (as well as any designee of Company) shall be + entitled to the unrestricted use and dissemination of these Submissions for any purpose, + commercial or otherwise, without acknowledgment or compensation to you. +

+ +

Prohibited Activities

+

+ You may not access or use the Website or Company Services for any other purpose other than + that for which Company makes it available. The Website and Company Services may not be used + in connection with any commercial endeavors except those that are specifically endorsed or + approved by Company. Prohibited activity includes, but is not limited to: +

+
    +
  • criminal or tortious activity;
  • +
  • + engage in the sale of any security or investment vehicle for which a license or permit is + required; +
  • +
  • + systematic retrieval of data or other content from the Website to create or compile, + directly or indirectly, a collection, compilation, database or directory without written + permission from Company; +
  • +
  • + making any unauthorized use of the Company Services, including collecting usernames and/or + email addresses of users by electronic or other means for the purpose of sending + unsolicited email, or creating user accounts by automated means or under false pretenses; +
  • +
  • + tricking, defrauding or misleading Company and other users, especially in any attempt to + learn sensitive account information such as passwords; +
  • +
  • + engaging in any automated use of the system, such as using any data mining, robots or + similar data gathering and extraction tools; +
  • +
  • + interfering with, disrupting, or creating an undue burden on the Website or the networks + or services connected to the Website; +
  • +
  • + attempting to impersonate another user or person or using the username of another user; +
  • +
  • selling or otherwise transferring your profile;
  • +
  • + using any information obtained from the Website in order to harass, abuse, or harm another + person; +
  • +
  • + using the Company Services as part of any effort to compete with Company or to provide + services as a service bureau; +
  • +
  • + deciphering, decompiling, disassembling or reverse engineering any of the software + comprising or in any way making up a part of the Website; +
  • +
  • + attempting to bypass any measures of the Website designed to prevent or restrict access to + the Website, or any portion of the Website; +
  • +
  • + harassing, annoying, intimidating or threatening any Company employees or agents engaged + in providing any portion of the Company Services to you; +
  • +
  • deleting the copyright or other proprietary rights notice from any Website content;
  • +
  • + except as may be the result of standard search engine or Internet browser usage, using or + launching, developing or distributing any automated system, including, without limitation, + any spider, robot (or “bot”), cheat utility, scraper or offline reader that accesses the + Website, or using or launching any unauthorized script or other software; or +
  • +
  • + using the Website or Company Services in a manner inconsistent with any and all applicable + laws and regulations. +
  • +
+ +

No Professional Advice or Fiduciary Duties

+

+ All information provided in connection with your access and use of the Website and Company + Services should not and may not be construed as professional advice. You should not take, + and should refrain from taking, any action based on any information contained on the Website + or in the Company Services, or any other information we make available at any time, + including, without limitation, blog posts, articles, links to third-party content, discord + or telegram content, news feeds, tutorials, tweets and videos. Before you make any + financial, legal or other decisions involving the Company Services or use thereof, you + should seek independent professional advice from an individual who is licensed and qualified + in the area for which such advice would be appropriate. This Agreement not intended to, and + do not, create or impose any fiduciary duties on us. You further agree that the only duties + and obligations that we have are expressly set out in this Agreement (including in the + Privacy Policy). +

+ +

Acknowledgement of Certain Risks; Other Disclaimers; Release of Claims

+

+ By accessing and using the Website and Company Services, you represent that you understand + the inherent risks associated with using cryptographic and blockchain-based systems, and + that you have a working knowledge of the usage and intricacies of Digital Assets. You + further understand that the markets for these Digital Assets are highly volatile due to + factors including (but not limited to) adoption, speculation, technology, security, and + regulation. You acknowledge and accept that the cost and speed of transacting with + cryptographic and blockchain-based systems are variable and may increase dramatically at any + time. You further acknowledge and accept the risk that your Digital Assets, or any Digital + Assets you acquire may lose some or all of their value and you may suffer loss due to the + fluctuation of prices of tokens and/or significant price slippage and cost. You understand + that anyone can create a token, including fake versions of existing tokens and tokens that + falsely claim to represent projects, and acknowledge and accept the risk that you may + mistakenly trade those or other tokens. You further acknowledge that we are not responsible + for any of these variables or risks and that we cannot be held liable for any resulting + losses that you experience while accessing or using the Website or Company Services. +

+ +

+ The Company Services and your Digital Assets could be impacted by one or more regulatory + inquiries or regulatory actions, which could impede or limit the ability of to continue to + make its proprietary software, and thus, could impede or limit your ability to continue to + use the Company Services. +

+

+ You understand and acknowledge that cryptography is a progressing field with advances in + code cracking and other technical advancements, such as the development of quantum + computers, which may present risks to Digital Assets and the Company Services, and could + result in the theft or loss of your Digital Assets. You acknowledge that the Company + Services may be subject to flaws and that you are solely responsible for evaluating any code + provided by the Website or Company Services. +

+

+ Although we intend to provide accurate and timely information on the Website and during your + use of the Company Services, that intention does not reflect a binding commitment, and the + Website and other information available when using the Company Services may not be accurate, + complete, error-free or current. To continue to provide you with as complete and accurate + information as possible, information may be changed or updated from time to time without + notice, including, without limitation, information regarding our policies. Accordingly, you + should verify all information before relying on it in any manner, and all decisions based on + such information contained on the Website or made available through the Services are your + sole and absolute responsibility. No representation of any kind or nature is made as to the + accuracy, completeness or appropriateness for any particular purpose of any pricing or other + information distributed via the Website or Services. Any reference to a type of Digital + Asset on the Website or otherwise during the use of the Company Services does not indicate + our approval or disapproval of the technology on which the Digital Asset relies, and should + not be used as a substitute for your understanding of the risks specific to each type of + Digital Asset. +

+

+ Use of the Company Services may carry financial risk. Digital Assets are, by their nature, + highly experimental, risky, and volatile. You acknowledge and agree that you will access and + use the Website and the Services at your own risk. By using the Company Services, you + represent and warrant that you have been, are, and will be solely responsible for making + your independent appraisal and investigations into the risks of a given transaction and the + underlying Digital Assets. You represent that you have sufficient knowledge, market + sophistication, professional advice, and experience to make your evaluation of the merits + and risks of any transaction conducted in connection with the Company Services or any + Digital Asset. You accept all consequences of using the Services, including the risk that + you may lose access to your Digital Assets indefinitely. All transaction decisions are made + solely by you. Notwithstanding anything in this Agreement, we accept no responsibility + whatsoever for, and will in no circumstances be liable to you in connection with, your use + of the Company Services for performing Digital Asset transactions. +

+ +

+ We are a developer of software. We do not operate a Digital Asset exchange platform or offer + trade execution or clearing services and, therefore, has no oversight, involvement, or + control concerning your transactions using the Services. All transactions between users of + our software are executed peer-to-peer directly between the users wallets through smart + contracts. You are responsible for complying with all laws that may be applicable to or + govern your use of the Services, including, but not limited to, the Commodity Exchange Act + and the regulations promulgated thereunder by the U.S. Commodity Futures Trading Commission + (“CFTC”), the federal securities laws and the regulations promulgated thereunder by the U.S. + Securities and Exchange Commission (“SEC”) and all foreign applicable laws. +

+

+ You understand that the Company is not registered or licensed by the CFTC, SEC, or any + financial regulatory authority. No financial regulatory authority has reviewed or approved + the use of the Website or company Services. The Website and the Company-developed software + do not constitute advice or a recommendation concerning any commodity, security, or other + Digital Asset or instrument. Company is not acting as an investment adviser or commodity + trading adviser to any person or entity. +

+ +

Intellectual Property Rights

+

+ The content on the Website (“Company Content”) and the trademarks, service marks, + algorithms, codes, programs, subjects, and logos contained therein (“Marks”) are owned by or + licensed to Company and are subject to copyright and other intellectual property rights + under Cayman Islands and foreign laws and international conventions. Company Content, + includes, without limitation, all source code, databases, functionality, software, website + designs, audio, video, text, photographs and graphics. All Company graphics, logos, designs, + page headers, button icons, scripts and service names are registered trademarks, common law + trademarks or trade dress of Company in Cayman Islands and/or other countries. Company’s + trademarks and trade dress may not be used, including as part of trademarks and/or as part + of domain names, in connection with any product or service in any manner that is likely to + cause confusion and may not be copied, imitated, or used, in whole or in part, without the + prior written permission of the Company. +

+

+ Company Content on the Website is provided to you “AS IS” for your information and personal + use only and may not be used, copied, reproduced, aggregated, distributed, transmitted, + broadcast, displayed, sold, licensed, or otherwise exploited for any other purposes + whatsoever without the prior written consent of the respective owners. Provided that you are + eligible to use the Website, you are granted a limited license to access and use the Website + and the Company Content and to download or print a copy of any portion of the Company + Content to which you have properly gained access solely for your personal, non-commercial + use. Company reserves all rights not expressly granted to you in and to the Website and + Company Content and Marks. +

+

Intellectual Property Claims

+

+ We respect the intellectual property rights of others and require that users of our App and + Services do the same. In accordance with the Digital Millennium Copyright Act of 1998, Title + 17 of the United States Code, Section 512 (“DMCA”), we will respond promptly to claims of + copyright infringement that are reported to the agent that we have designated to receive + notifications of claims infringement (its “Designated Agent”). Our Designated Agent is: Dora + Factory Support ( + + support@dorafactory.org + + ) +

+

To be sure the matter is handled immediately, your written notice must:

+
    +
  • Contain your physical or electronic signature;
  • +
  • + Identify the copyrighted work or other intellectual property alleged to have been + infringed; +
  • +
  • + Identify the allegedly infringing material in a sufficiently precise manner to allow us to + locate that material; +
  • +
  • + Contain adequate information by which we can contact you (including postal address, + telephone number, and email address); +
  • +
  • + Contain a statement that you have a good faith belief that use of the copyrighted material + or other intellectual property is not authorized by the owner, the owner’s agent or the + law; +
  • +
  • Contain a statement that the information in the written notice is accurate; and
  • +
  • + Contain a statement, under penalty of perjury, that you are authorized to act on behalf of + the copyright or other intellectual property right owner. +
  • +
  • + Unless the notice pertains to copyright or other intellectual property infringement, the + Agent will be unable to address the listed concern. +
  • +
+ +

Submitting a DMCA Counter-Notification

+

+ We will notify you that we have removed or disabled access to copyright-protected material + that you provided, if such removal is pursuant to a validly received DMCA take-down notice. + In response, you may provide our Agent with a written counter-notification that includes the + following information: +

+
    +
  • Your physical or electronic signature;
  • +
  • + Identification of the material that has been removed or to which access has been disabled, + and the location at which the material appeared before it was removed or access to it was + disabled; +
  • +
  • + A statement from you under the penalty of perjury, that you have a good faith belief that + the material was removed or disabled as a result of a mistake or misidentification of the + material to be removed or disabled; and +
  • +
  • + Your name, physical address and telephone number, and a statement that you consent to the + jurisdiction of a court for the judicial district in which your physical address is + located, or if your physical address is outside of the United States, for any judicial + district in which we may be located, and that you will accept service of process from the + person who provided notification of allegedly infringing material or an agent of such + person. +
  • +
+

+ We reserve the right, in our sole discretion, to terminate the account or access of any user + of the Website and Company Services who is the subject of repeated DMCA or other + infringement notifications. +

+ +

Third Party Websites and Content

+

+ The Website contains (or you may be sent through the Website or the Company Services) links + to other websites (“Third Party Websites”) as well as articles, photographs, text, graphics, + pictures, designs, music, sound, video, information, applications, software and other + content or items belonging to or originating from third parties (the “Third Party Content”). + Such Third Party Websites and Third Party Content are not investigated, monitored or checked + for accuracy, appropriateness, or completeness by us, and we are not responsible for any + Third Party Websites accessed through the Website or any Third Party Content posted on, + available through or installed from the Website, including the content, accuracy, + offensiveness, opinions, reliability, privacy practices or other policies of or contained in + the Third Party Websites or the Third Party Content. Inclusion of, linking to or permitting + the use or installation of any Third Party Website or any Third Party Content does not imply + approval or endorsement thereof by us. If you decide to leave the Website and access the + Third Party Websites or to use or install any Third Party Content, you do so at your own + risk and you should be aware that our terms and policies no longer govern. You should review + the applicable terms and policies, including privacy and data gathering practices, of any + website to which you navigate from the Website or relating to any applications you use or + install from the Website. Any purchases you make through Third Party Websites will be + through other websites and from other companies, and Company takes no responsibility + whatsoever in relation to such purchases which are exclusively between you and the + applicable third party. +

+ +

Management and Company Services

+

+ Company reserves the right but does not have the obligation to: +

    +
  • monitor the Website and Company Services for violations of this Agreement;
  • +
  • + take appropriate legal action against anyone who, in Company’s sole discretion, violates + this Agreement, including without limitation, reporting such user to law enforcement + authorities; +
  • +
  • + in Company’s sole discretion and without limitation, refuse, restrict access to or + availability of, or disable (to the extent technologically feasible) any user’s + contribution or any portion thereof that may violate this Agreement or any Company + policy; +
  • +
  • + in Company’s sole discretion and without limitation, notice or liability to remove from + the Website or otherwise disable all files and content that are excessive in size or are + in any way burdensome to Company’s systems; +
  • +
  • + otherwise manage the Website and Company Services in a manner designed to protect the + rights and property of Company and others and to facilitate the proper functioning of + the Website. +
  • +
+

+ +

Privacy Policy

+

+ We care about the privacy of our users. Please review the Company Privacy Policy. By using + the Website or Company Services, you are consenting to have your personal data transferred + to and processed in Cayman Islands, which may have less protections than your jurisdiction + of residence.. By accessing or using the Website or the Company Services, you are consenting + to the terms of our Privacy Policy. +

+ +

Term and Termination

+

+ This Agreement shall remain in full force and effect while you use the Website or are + otherwise a user or member of the Website, as applicable. You may terminate your use or + participation at any time, for any reason, by following the instructions for terminating + user accounts in your account settings, if available, or by contacting us using the contact + information below. +

+ +

+ + Without limiting any other provision of this Agreement, Company reserves the right to, in + Company’s sole discretion and without notice or liability, deny access to and use of the + Website and the Company Services, to any person for any reason or for no reason at all, + including without limitation for breach of any representation, warranty or covenant + contained in this Agreement, or of any applicable law or regulation, and company may + terminate your use or participation in the Website and the Company Services, delete your + profile and any content or information that you have posted at any time, without warning, + in Company’s sole discretion. + +

+ +

+ In order to protect the integrity of the Website and Company Services, Company reserves the + right at any time in its sole discretion to block certain IP addresses from accessing the + Website and Company Services. +

+

+ Any provisions of this Agreement that, in order to fulfill the purposes of such provisions, + need to survive the termination or expiration of this Agreement, shall be deemed to survive + for as long as necessary to fulfill such purposes. +

+

+ + You understand that certain laws allow you to cancel this Agreement, without any penalty + or obligation, at any time prior to midnight of Company’s third business day following the + date of this Agreement, excluding Sundays and holidays. To cancel, email the company using + the contact information listing below in this Agreement or by accessing your account + settings. This section applies only to individuals residing in districts with such laws. + +

+ +

+ If Company terminates or suspends your account for any reason, you are prohibited from + registering and creating a new account under your name, a fake or borrowed name, or the name + of any third party, even if you may be acting on behalf of the third party. In addition to + terminating or suspending your account, Company reserves the right to take appropriate legal + action, including without limitation pursuing civil, criminal, and injunctive redress. +

+ +

Modifications

+

To Agreement

+

+ We may modify this Agreement from time to time. Any and all changes to this Agreement will + be posted on the Website and revisions will be indicated by date. You agree to be bound to + any changes to this Agreement when you use the website and all other Services after any such + modification becomes effective. Company may also, in its sole discretion, choose to alert + all users with whom it maintains email information of such modifications by means of an + email to their most recently provided email address. It is therefore important that you + regularly review this Agreement and keep your contact information current in your account + settings to ensure you are informed of changes. You agree that you will periodically check + the Website for updates to this Agreement. Modifications to this Agreement shall be + effective after posting. +

+

To Services

+

+ Company reserves the right at any time to modify or discontinue, temporarily or permanently, + the Company Services (or any part thereof) with or without notice. You agree that Company + shall not be liable to you or to any third party for any modification, suspension or + discontinuance of the Company Services. +

+ +

Disputes and Choice of Law

+

Between Users

+

+ If there is a dispute between users of the Website, or between users and any third party, + you understand and agree that Company is under no obligation to become involved. We have no + responsibility for such disputes. In the event that you have a dispute with one or more + other users, you hereby release Company, its officers, employees, agents and successors in + rights from claims, demands and damages (actual and consequential) of every kind or nature, + known or unknown, suspected and unsuspected, disclosed and undisclosed, arising out of or in + any way related to such disputes and/or the Company Services. +

+

With Company

+

+ All questions of law, rights, and remedies regarding any act, event or occurrence undertaken + pursuant or relating to this Website or the Company Services shall be governed and construed + by the laws of Cayman Islands, without regarding to choice of law principles. Any dispute + arising out of or in connection with the Website or Company Services or this Agreement , + including any question regarding its existence, validity or termination (a “Dispute”) shall + be subject to resolution under this provision. +

+

+ The Company wants to address your concerns without the need for a formal legal dispute. + Before filing a claim against us you agree to try to resolve the Dispute informally by + contacting us at{' '} + + support@dorafactory.org + {' '} + to notify us of the actual or potential Dispute. Similarly, we will undertake reasonable + efforts to contact you to notify you of any actual or potential dispute to resolve any claim + we may possess informally before taking any formal action. The party that provides the + notice of the actual or potential Dispute (the “Notifying Party”) will include in that + notice (a “Notice of Dispute”) the name of User, the Notifying Party’s contact information + for any communications relating to such Dispute (including for the Notifying Party’s legal + counsel if it is represented by counsel in connection with such Dispute), and sufficient + details regarding such Dispute to enable the other party (the “Notified Party”) to + understand the basis of and evaluate the concerns raised. If the Notified Party responds + within ten (10) business days after receiving the Notice of Dispute that it is ready and + willing to engage in good faith discussions in an effort to resolve the Dispute informally, + then each party shall promptly participate in such discussions in good faith. +

+ +

+ If, notwithstanding the Notifying Party’s compliance with all of its obligations under the + preceding paragraph, a Dispute is not resolved within 30 days after the Notice of Dispute is + sent (or if the Notified Party fails to respond to the Notice of Dispute within ten (10) + business days), the Notifying Party may initiate an arbitration proceeding as described + below. If either party purports to initiate arbitration without first providing a Notice of + Dispute and otherwise complying with all of its obligations under the preceding paragraph, + then, notwithstanding any other provision of this Agreement, the arbitrator(s) will promptly + dismiss the claim with prejudice and will award the other party all of its costs and + expenses (including, without limitation, reasonable attorneys’ fees) incurred in connection + with such Dispute. +

+ +

+ Any Dispute that cannot be resolved per the above procedure shall be referred to and finally + resolved by arbitration administered by the Cayman Islands International Arbitration Centre + (“SIAC”) in accordance with the Arbitration Rules of the Cayman Islands International + Arbitration Centre (“SIAC Rules”) for the time being in force, which rules are deemed to be + incorporated by reference in this clause. Subject, however, to the right of Company, at the + Company’s sole discretion, to bring an action to seek injunctive relief to enforce this + Agreement or to stop or prevent an infringement of proprietary or other third party rights + (or any similar cause of action) in any applicable court in any jurisdiction exists with + regard to a user. The seat of the arbitration shall be Cayman Islands. The Tribunal shall + consist of three (3) arbitrator(s). The language of the arbitration shall be English. +

+

+ You and the Company agree that the arbitration of any Dispute shall proceed on an individual + basis, and neither you nor Company may bring a claim as a part of a class, group, + collective, coordinated, consolidated or mass arbitration (each, a “Collective + Arbitration”). Without limiting the generality of the foregoing, a claim to resolve any + Dispute against Company will be deemed a Collective Arbitration if (a) two (2) or more + similar claims for arbitration are filed concurrently by or on behalf of one or more + claimants; and (b) counsel for the claimants are the same, share fees or coordinate across + the arbitrations. “Concurrently” for purposes of this provision means that both arbitrations + are pending (filed but not yet resolved) at the same time. +

+ +

+ + To the maximum extent permitted by applicable law, neither you nor Company shall be + entitled to consolidate, join or coordinate disputes by or against other individuals or + entities, or arbitrate or litigate any dispute in a representative capacity, including as + a representative member of a class or in a private attorney general capacity. In + connection with any dispute (as defined above), any and all such rights are hereby + expressly and unconditionally waived. + {' '} + Without limiting the foregoing, any challenge to the validity of this paragraph shall be + determined exclusively by the arbitrator. +

+ +

+ In no event shall any claim, action or proceeding by you related in any way to the Website + and/or the Company Services (including your visit to or use of the Website and/or the + Company Services) be instituted more than two (2) years after the cause of action arose. You + will be liable for any attorneys’ fees and costs if we have to take any legal action to + enforce this Agreement. +

+ +

Corrections

+

+ Occasionally there may be information on the Website that contains typographical errors, + inaccuracies or omissions that may relate to service descriptions, pricing, availability, + and various other information. Company reserves the right to correct any errors, + inaccuracies or omissions and to change or update the information at any time, without prior + notice. +

+ +

Disclaimers

+

+ Company cannot control the nature of all of the content available on the Website or Company + Services. By operating the Website or Company Services, Company does not represent or imply + that Company endorses any blogs, contributions or other content available on or linked to by + the Website or Company Services, including without limitation content hosted on third party + websites or provided by third party applications, or that Company believes contributions, + blogs or other content to be accurate, useful or non-harmful. We do not control and are not + responsible for unlawful or otherwise objectionable content you may encounter on the Website + or in connection with any contributions. The Company is not responsible for the conduct, + whether online or offline, of any user of the Website or Company Services. +

+ +

+ + You agree that your use of the Website and Company Services will be at your sole risk. To + the fullest extent permitted by law, Company, its officers, directors, employees, and + agents disclaim all warranties, express or implied, in connection with the Website and the + Company Services and your use thereof, including, without limitation, the implied + warranties of merchantability, fitness for a particular purpose and non-infringement. + Company makes no warranties or representations about the accuracy or completeness of the + Website’s content or the content of any websites linked to this Website and assumes no + liability or responsibility for any (a) errors, mistakes, or inaccuracies of content and + materials, (b) personal injury or property damage, of any nature whatsoever, resulting + from your access to and use of our website, (c) any unauthorized access to or use of our + secure servers and/or any and all personal information and/or financial information stored + therein, (d) any interruption or cessation of transmission to or from the Website or + Company services, (e) any bugs, viruses, Trojan horses, or the like which may be + transmitted to or through the Website by any third party, and/or (f) any errors or + omissions in any content and materials or for any loss or damage of any kind incurred as a + result of the use of any content posted, transmitted, or otherwise made available via the + Website or Company Services. Company does not warrant, endorse, guarantee, or assume + responsibility for any product or service advertised or offered by a third party through + the website or any hyperlinked website or featured in any banner or other advertising, and + Company will not be a party to or in any way be responsible for monitoring any transaction + between you and third-party providers of products or services. As with the purchase of a + product or service through any medium or in any environment, you should use your best + judgment and exercise caution where appropriate. + +

+ +

Limitations of Liability

+

+ + In no event shall Company or its directors, employees, or agents be liable to you or any + third party for any direct, indirect, consequential, exemplary, incidental, special or + punitive damages, including lost profit, lost revenue, loss of data or other damages + arising from your use of the Website or Company Services, even if Company has been advised + of the possibility of such damages. Notwithstanding anything to the contrary contained + herein, Company’s liability to you for any cause whatsoever and regardless of the form of + the action, will at all times be limited to the amount paid, if any, by you to Company for + the Company Services during the period of services available prior to any cause of action + arising. + +

+

+ + Certain laws do not allow limitations on implied warranties or the exclusion or limitation + of certain damages. If these laws apply to you, some or all of the above disclaimers or + limitations may not apply to you, and you may have additional rights. + +

+

+ + Certain laws do not allow limitations on implied warranties or the exclusion or limitation + of certain damages. If these laws apply to you, some or all of the above disclaimers or + limitations may not apply to you, and you may have additional rights. + +

+ +

+ + To the maximum extent permitted by law, you further expressly waive and release the + Company Parties from any and all liability, claims, causes of action, or damages arising + from or in any way relating to your use of the Website and Company Services and your + interaction therewith. If you are a California resident, you waive California Civil Code + Section 1542, which says: “A general release does not extend to claims which the creditor + does not know or suspect to exist in his favor at the time of executing the release, + which, if known by him must have materially affected his settlement with the debtor.” + +

+ +

Indemnity

+

+ You agree to defend, indemnify and hold Company, its subsidiaries, and affiliates, and their + respective officers, agents, partners and employees (“Company Parties”), harmless from and + against, any loss, damage, liability, claim, or demand, including reasonable attorneys’ fees + and expenses, made by any third party due to or arising out of your contributed content, use + of the Company Services, and/or arising from a breach of this Agreement and/or any breach of + your representations and warranties set forth above. Notwithstanding the foregoing, Company + reserves the right, at your expense, to assume the exclusive defense and control of any + matter for which you are required to indemnify the Company Parties, and you agree to + cooperate, at your expense, with Company’s defense of such claims. Company will use + reasonable efforts to notify you of any such claim, action, or proceeding which is subject + to this indemnification upon becoming aware of it. +

+ +

Notices

+

+ Except as explicitly stated otherwise, any notices given to Company shall be given by email + to the address listed in the contact information below. Any notices given to you shall be + given to the email address you provided during the registration process, or such other + address as each party may specify. Notice shall be deemed to be given twenty-four (24) hours + after the email is sent, unless the sending party is notified that the email address is + invalid. We may also choose to send notices by regular mail. +

+ +

User Data

+

+ Our Website will maintain certain data that you transfer to the Website for the purpose of + the performance of the Company Services, as well as data relating to your use of the Company + Services. Although we perform regular routine backups of data, you are primarily responsible + for all data that you have transferred or that relates to any activity you have undertaken + using the Company Services. You agree that Company shall have no liability to you for any + loss or corruption of any such data, and you hereby waive any right of action against + Company arising from any such loss or corruption of such data. +

+ +

Electronic Contracting

+

+ Your use of the Company Services includes the ability to enter into agreements and/or to + make transactions electronically.{' '} + + You acknowledge that your electronic submissions constitute your Agreement and intent to + be bound by and to pay for such agreements and transactions. Your Agreement and intent to + be bound by electronic submissions applies to all records relating to all transactions you + enter into relating to the Company Services, including notices of cancellation, policies, + contracts, and applications.{' '} + + In order to access and retain your electronic records, you may be required to have certain + hardware and software, which are your sole responsibility. +

+ +

Miscellaneous

+

+ This Agreement constitutes the entire agreement between you and Company regarding the use of + the Company Services. The failure of Company to exercise or enforce any right or provision + of this Agreement shall not operate as a waiver of such right or provision. The section + titles in this Agreement are for convenience only and have no legal or contractual effect. + This Agreement operates to the fullest extent permissible by law. This Agreement and your + account may not be assigned by you without our express written consent. Company may assign + any or all of its rights and obligations to others at any time. Company shall not be + responsible or liable for any loss, damage, delay or failure to act caused by any cause + beyond Company’s reasonable control. If any provision or part of a provision of this + Agreement is unlawful, void or unenforceable, that provision or part of the provision is + deemed severable from this Agreement and does not affect the validity and enforceability of + any remaining provisions. There is no joint venture, partnership, employment or agency + relationship created between you and Company as a result of this Agreement or use of the + Website and Company Services. Upon Company’s request, you will furnish Company any + documentation, substantiation or releases necessary to verify your compliance with this + Agreement. You agree that this Agreement will not be construed against the Company by virtue + of having drafted them. You hereby waive any and all defenses you may have based on the + electronic form of this Agreement and the lack of signing by the parties hereto to execute + this Agreement. +

+ +

Contact Us

+

+ In order to resolve a complaint regarding the Company Services or to receive further + information regarding use of the Company Services, please contact Company as set forth + below: +

+

+ Foundation Name: Matsushiba Foundation
+ Foundation Name: Dora Grant DAO Foundation Email:
+ support@dorafactory.org +

+
); @@ -30,6 +1395,21 @@ Styled.Article = styled.article` } } + h2 { + font: var(--font-large-book); + color: var(--color-text-2); + padding: 0.5rem 0; + } + + h3, + strong { + font-weight: 900; + } + + strong { + color: var(--color-text-2); + } + p { padding: 0.5rem 0; } diff --git a/src/views/dialogs/AcknowledgeTermsDialog.tsx b/src/views/dialogs/AcknowledgeTermsDialog.tsx index 6c07ae9..2753646 100644 --- a/src/views/dialogs/AcknowledgeTermsDialog.tsx +++ b/src/views/dialogs/AcknowledgeTermsDialog.tsx @@ -34,13 +34,70 @@ export const AcknowledgeTermsDialog = ({ setIsOpen }: ElementProps) => { }; return ( - + +
+

Notice: Please Read Carefully Before Using the Token Migration Interface

+

+

  • + + Caution. + {' '} + + You must make sure you own or have access to the Dora Vota network address you + entered.{' '} + + + Double-check + {' '} + {''} + + that your Dora Vota network address is accurate and complete, before initiating the + migration. We cannot verify the ownership of the Dora Vota address you entered, + other than yourself. This is your responsibility. + +
  • +
  • + + Once initiated, your Ethereum DORA tokens will be sent to a burn address and cannot + be reversed. And you will receive the DORA Tokens at the Dora Vota network address + you entered. Any error you make is irreversible, and we are not liable for any of + your errors. + +
  • +
  • + Allow at least{' '} + + 48 hours + {' '} + for the migration to complete. +
  • +
  • + We do not have control or custody over your digital assets or wallets. You are + interacting directly with the blockchain smart contracts. +
  • +
  • + You are strictly prohibited from using the Token Migration Interface if you are under + any sanctions imposed by any sanctions authority, including but not limited to the + United States Department of the Treasury's Office of Foreign Assets Control, the + United Nations Security Council, the European Union, or any other government authority + (“Sanctions”), or if you are a resident of a country or territory that is the subject + of Sanctions, or if your use of the interface would violate, or cause us to violate + any laws or regulations. Specifically, residents in the United States and the People's + Republic of China are prohibited from using the interface (collectively,{' '} + "Excluded Individual" + ). +
  • +
  • + By using the Token Migration Interface, you agree to our{' '} + + Terms of Use and Privacy Policy + + , and you represent to us that you are not an Excluded Individual. +
  • +

    +
    + { onClick={onContinue} state={{ isDisabled: !hasAcknowledged }} > - {stringGetter({ key: STRING_KEYS.CONTINUE })} + Continue
    diff --git a/src/views/dialogs/DisconnectDialog.tsx b/src/views/dialogs/DisconnectDialog.tsx index acf0279..5008c6e 100644 --- a/src/views/dialogs/DisconnectDialog.tsx +++ b/src/views/dialogs/DisconnectDialog.tsx @@ -29,17 +29,17 @@ export const DisconnectDialog = ({ setIsOpen }: ElementProps) => { }; return ( - + -

    {stringGetter({ key: STRING_KEYS.DISCONNECT_CONFIRMATION })}

    +

    Are you sure you want to disconnect your account?

    - +
    diff --git a/src/views/dialogs/MnemonicExportDialog.tsx b/src/views/dialogs/MnemonicExportDialog.tsx index e6d2efc..a4e966a 100644 --- a/src/views/dialogs/MnemonicExportDialog.tsx +++ b/src/views/dialogs/MnemonicExportDialog.tsx @@ -109,7 +109,7 @@ export const MnemonicExportDialog = ({ setIsOpen }: DialogProps) => { ))} - {stringGetter({ key: STRING_KEYS.CLICK_TO_SHOW })} + Click to show } > @@ -124,9 +124,7 @@ export const MnemonicExportDialog = ({ setIsOpen }: DialogProps) => { isOpen setIsOpen={setIsOpen} title={title} - description={stringGetter({ - key: STRING_KEYS.REVEAL_SECRET_PHRASE_DESCRIPTION, - })} + description="Your secret phrase is a set of 24 words used to backup and access your account." > {content} diff --git a/src/views/dialogs/MoreLinksDialog.tsx b/src/views/dialogs/MoreLinksDialog.tsx index e8145e3..c736f48 100644 --- a/src/views/dialogs/MoreLinksDialog.tsx +++ b/src/views/dialogs/MoreLinksDialog.tsx @@ -36,7 +36,7 @@ export const MoreLinksDialog = ({ setIsOpen }: DialogProps) => { ]; return ( - + ); diff --git a/src/views/dialogs/OnboardingDialog.tsx b/src/views/dialogs/OnboardingDialog.tsx index c35af54..c9da049 100644 --- a/src/views/dialogs/OnboardingDialog.tsx +++ b/src/views/dialogs/OnboardingDialog.tsx @@ -42,8 +42,8 @@ export const OnboardingDialog = ({ setIsOpen }: DialogProps) => { {...(currentOnboardingStep && { [OnboardingSteps.ChooseWallet]: { - title: stringGetter({ key: STRING_KEYS.CONNECT_YOUR_WALLET }), - description: stringGetter({ key: STRING_KEYS.CONNECT_YOUR_WALLET_SUBTITLE }), + title: "Connect your wallet", + description: "Select your wallet from these supported options.", children: ( @@ -61,10 +61,8 @@ export const OnboardingDialog = ({ setIsOpen }: DialogProps) => { ), [EvmDerivedAccountStatus.Derived]: , }[derivationStatus], - title: stringGetter({ key: STRING_KEYS.SIGN_MESSAGE }), - description: stringGetter({ - key: STRING_KEYS.SIGNATURE_CREATES_COSMOS_WALLET, - }), + title: "Sign message", + description: "Signatures are used to verify your ownership and to confirm wallet compatibility", children: ( diff --git a/src/views/dialogs/OnboardingDialog/ChooseWallet.tsx b/src/views/dialogs/OnboardingDialog/ChooseWallet.tsx index 98ada81..1767186 100644 --- a/src/views/dialogs/OnboardingDialog/ChooseWallet.tsx +++ b/src/views/dialogs/OnboardingDialog/ChooseWallet.tsx @@ -28,14 +28,7 @@ export const ChooseWallet = () => { {

    - {stringGetter({ - key: STRING_KEYS.COULD_NOT_CONNECT, - params: { - WALLET: stringGetter({ - key: wallets[selectedWalletType].stringKey, - }), - }, - })} + Couldn't connect to {wallets[selectedWalletType].stringKey},

    } {selectedWalletError} @@ -51,14 +44,14 @@ export const ChooseWallet = () => { slotLeft={} size={ButtonSize.Small} > -
    {stringGetter({ key: wallets[walletType].stringKey })}
    +
    {wallets[walletType].stringKey}
    ))} - {stringGetter({ key: STRING_KEYS.ABOUT_WALLETS })} + About wallets diff --git a/src/views/dialogs/OnboardingDialog/GenerateKeys.tsx b/src/views/dialogs/OnboardingDialog/GenerateKeys.tsx index eef2aff..3a75d7b 100644 --- a/src/views/dialogs/OnboardingDialog/GenerateKeys.tsx +++ b/src/views/dialogs/OnboardingDialog/GenerateKeys.tsx @@ -8,7 +8,7 @@ import { AlertType } from '@/constants/alerts'; import { ButtonAction } from '@/constants/buttons'; import { STRING_KEYS } from '@/constants/localization'; -import { DydxAddress, SIGN_TYPED_DATA } from '@/constants/wallets'; +import { DoraAddress, SIGN_TYPED_DATA } from '@/constants/wallets'; import { useAccounts, useDydxClient, useStringGetter, useMatchingEvmNetwork } from '@/hooks'; @@ -92,37 +92,6 @@ export const GenerateKeys = ({ setStatus(EvmDerivedAccountStatus.Deriving); const signature = await signTypedDataAsync(); - const { wallet: dydxWallet } = await getWalletFromEvmSignature({ - signature, - }); - - // 2. Ensure signature is deterministic - // Check if subaccounts exist - const dydxAddress = dydxWallet.address as DydxAddress; - - try { - const subaccounts = await getSubaccounts({ dydxAddress }); - - if (!subaccounts.length) { - setStatus(EvmDerivedAccountStatus.EnsuringDeterminism); - const additionalSignature = await signTypedDataAsync(); - if (signature !== additionalSignature) { - setStatus(EvmDerivedAccountStatus.NotDerived); - setError( - stringGetter({ - key: STRING_KEYS.INDETERMINISTIC_SIGNING, - }) as string - ); - return; - } - } - } catch (error) { - setStatus(EvmDerivedAccountStatus.NotDerived); - const { message } = parseWalletError({ error, stringGetter }); - if (message) setError(message); - return; - } - await setWalletFromEvmSignature(signature); // 3: Remember me (encrypt and store signature) @@ -132,7 +101,7 @@ export const GenerateKeys = ({ saveEvmSignature(encryptedSignature); } - // 4. Done + // 2. Done setStatus(EvmDerivedAccountStatus.Derived); } catch (error) { setStatus(EvmDerivedAccountStatus.NotDerived); @@ -140,7 +109,7 @@ export const GenerateKeys = ({ error, stringGetter, }); - + console.log(`find error, error is ${error}`); if (message) setError(message); } }; @@ -150,20 +119,9 @@ export const GenerateKeys = ({ {[ { - status: EvmDerivedAccountStatus.Deriving, - title: stringGetter({ key: STRING_KEYS.GENERATE_DYDX_WALLET }), - description: stringGetter({ - key: STRING_KEYS.VERIFY_WALLET_OWNERSHIP, - }), - }, - status === EvmDerivedAccountStatus.EnsuringDeterminism && { status: EvmDerivedAccountStatus.EnsuringDeterminism, - title: stringGetter({ - key: STRING_KEYS.VERIFY_WALLET_COMPATIBILITY, - }), - description: stringGetter({ - key: STRING_KEYS.ENSURES_WALLET_SUPPORT, - }), + title: 'Verify wallet compatibility', + description: 'Ensures your wallet is supported.', }, ] .filter(isTruthy) @@ -188,7 +146,7 @@ export const GenerateKeys = ({ {staticEncryptionKey && ( - {stringGetter({ key: STRING_KEYS.REMEMBER_ME })} + Remember me - {stringGetter({ - key: STRING_KEYS.FREE_SIGNING, - params: { - FREE: ( - - {stringGetter({ - key: STRING_KEYS.FREE_TRADING_TITLE_ASTERISK_FREE, - })} - - ), - }, - })} + Signing is {''} + free + {''} and will not send a transaction. } @@ -225,7 +174,7 @@ export const GenerateKeys = ({ onClick={() => switchNetwork().then(deriveKeys).then(onKeysDerived)} state={{ isLoading: isSwitchingNetwork }} > - {stringGetter({ key: STRING_KEYS.SWITCH_NETWORK })} + Switch network ) : ( )} - - {stringGetter({ key: STRING_KEYS.CHECK_WALLET_FOR_REQUEST })} - + Check your wallet for a request! ); diff --git a/src/views/dialogs/OnboardingTriggerButton.tsx b/src/views/dialogs/OnboardingTriggerButton.tsx index 7a107ef..0d14801 100644 --- a/src/views/dialogs/OnboardingTriggerButton.tsx +++ b/src/views/dialogs/OnboardingTriggerButton.tsx @@ -30,8 +30,8 @@ export const OnboardingTriggerButton = ({ size = ButtonSize.Small }: StyleProps) > { { - [OnboardingState.Disconnected]: stringGetter({ key: STRING_KEYS.CONNECT_WALLET }), - [OnboardingState.WalletConnected]: stringGetter({ key: STRING_KEYS.RECOVER_KEYS }), + [OnboardingState.Disconnected]: 'Connect Wallet', + [OnboardingState.WalletConnected]: 'Reconnect Wallet' }[onboardingState as string] } diff --git a/src/views/dialogs/RestrictedGeoDialog.tsx b/src/views/dialogs/RestrictedGeoDialog.tsx index e3ae7be..328f3e2 100644 --- a/src/views/dialogs/RestrictedGeoDialog.tsx +++ b/src/views/dialogs/RestrictedGeoDialog.tsx @@ -1,17 +1,14 @@ -import styled, { AnyStyledComponent } from "styled-components"; +import styled, { AnyStyledComponent } from 'styled-components'; -import { PreventCloseDialogProps } from "@/constants/dialogs"; -import { STRING_KEYS } from "@/constants/localization"; -import { useStringGetter } from "@/hooks"; -import { layoutMixins } from "@/styles/layoutMixins"; +import { PreventCloseDialogProps } from '@/constants/dialogs'; +import { STRING_KEYS } from '@/constants/localization'; +import { useStringGetter } from '@/hooks'; +import { layoutMixins } from '@/styles/layoutMixins'; -import { Dialog } from "@/components/Dialog"; -import { Icon, IconName } from "@/components/Icon"; +import { Dialog } from '@/components/Dialog'; +import { Icon, IconName } from '@/components/Icon'; -export const RestrictedGeoDialog = ({ - preventClose, - setIsOpen, -}: PreventCloseDialogProps) => { +export const RestrictedGeoDialog = ({ preventClose, setIsOpen }: PreventCloseDialogProps) => { const stringGetter = useStringGetter(); return ( @@ -19,11 +16,13 @@ export const RestrictedGeoDialog = ({ isOpen preventClose={preventClose} setIsOpen={setIsOpen} - title={stringGetter({ key: STRING_KEYS.REGION_NOT_PERMITTED_TITLE })} + title="Restricted" slotIcon={} > - {stringGetter({ key: STRING_KEYS.MIGRATION_BLOCKED_MESSAGE })} + Because you appear to be a resident of, or using this user interface from, a jurisdiction + that violates our terms of use, or have engaged in activity that violates our terms of use, + you have been blocked and this transaction cannot be completed.
    ); diff --git a/src/views/dialogs/WalletRestrictedDialog.tsx b/src/views/dialogs/WalletRestrictedDialog.tsx index 4a9fe5e..217c8d6 100644 --- a/src/views/dialogs/WalletRestrictedDialog.tsx +++ b/src/views/dialogs/WalletRestrictedDialog.tsx @@ -19,11 +19,11 @@ export const WalletRestrictedDialog = ({ isOpen preventClose={preventClose} setIsOpen={setIsOpen} - title={stringGetter({ key: STRING_KEYS.WALLET_RESTRICTED_ERROR_TITLE })} + title="Wallet Restricted" slotIcon={} > - {stringGetter({ key: STRING_KEYS.MIGRATION_BLOCKED_MESSAGE })} + Because you appear to be a resident of, or using this user interface from, a jurisdiction that violates our terms of use, or have engaged in activity that violates our terms of use, you have been blocked and this transaction cannot be completed.
    ); diff --git a/src/views/menus/AccountMenu.tsx b/src/views/menus/AccountMenu.tsx index 4fca0a0..d4e3eae 100644 --- a/src/views/menus/AccountMenu.tsx +++ b/src/views/menus/AccountMenu.tsx @@ -37,7 +37,7 @@ export const AccountMenu = () => { const dispatch = useDispatch(); const onboardingState = useSelector(getOnboardingState); - const { evmAddress, walletType, dydxAddress, hdKey } = useAccounts(); + const { evmAddress, walletType, DoraAddress, hdKey } = useAccounts(); const { DYDXBalance } = useAccountBalance(); const onRecoverKeys = () => { @@ -52,47 +52,13 @@ export const AccountMenu = () => { onboardingState === OnboardingState.AccountConnected && ( - - - -
    - {stringGetter({ - key: TOOLTIP_STRING_KEYS.DYDX_ADDRESS_BODY, - params: { - DYDX_ADDRESS: {truncateAddress(dydxAddress)}, - EVM_ADDRESS: truncateAddress(evmAddress, '0x'), - }, - })} -
    - - } - > - - {stringGetter({ key: STRING_KEYS.DYDX_CHAIN_ADDRESS })} - -
    - {truncateAddress(dydxAddress)} -
    - - -
    - - {walletType && ( + {/* {walletType && ( - )} + )} */} - {stringGetter({ key: STRING_KEYS.SOURCE_ADDRESS })} {truncateAddress(evmAddress, '0x')} @@ -106,42 +72,14 @@ export const AccountMenu = () => { type={ButtonType.Link} /> - - - {stringGetter({ - key: STRING_KEYS.ASSET_BALANCE, - params: { ASSET: 'DYDX' }, - })} - - -
    ) } items={[ - onboardingState === OnboardingState.WalletConnected && { - value: 'ConnectToChain', - label: ( - -

    {stringGetter({ key: STRING_KEYS.MISSING_KEYS_DESCRIPTION })}

    - -
    - ), - onSelect: onRecoverKeys, - separator: true, - }, - onboardingState === OnboardingState.AccountConnected && - hdKey && { - value: 'MnemonicExport', - icon: , - label: stringGetter({ key: STRING_KEYS.EXPORT_SECRET_PHRASE }), - highlightColor: 'negative', - onSelect: () => dispatch(openDialog({ type: DialogTypes.MnemonicExport })), - }, { value: 'Disconnect', icon: , - label: stringGetter({ key: STRING_KEYS.DISCONNECT }), + label: "Disconnect", highlightColor: 'negative', onSelect: () => dispatch(openDialog({ type: DialogTypes.DisconnectWallet })), }, @@ -154,7 +92,7 @@ export const AccountMenu = () => { ) : onboardingState === OnboardingState.AccountConnected ? ( walletType && ) : null} - {!isTablet && {truncateAddress(dydxAddress)}} + {!isTablet && {truncateAddress(evmAddress, '0x')}} ); };