├── .editorconfig
├── .eslintrc.js
├── .gitattributes
├── .gitignore
├── .husky
├── .gitignore
└── commit-msg
├── .prettierrc.js
├── .vscode
└── settings.json
├── LICENSE
├── README.md
├── __tests__
└── fake_test.ts
├── commitlint.config.js
├── index.html
├── package-lock.json
├── package.json
├── postcss.config.js
├── src
├── abi
│ ├── erc20-abi.ts
│ ├── lockbox-abi.ts
│ ├── multipay-abi.ts
│ └── pns-abi.ts
├── assets
│ ├── bitcoin.svg
│ ├── ethereum.svg
│ ├── images
│ │ ├── arrow-blue.svg
│ │ ├── arrow-white.svg
│ │ ├── bg.jpg
│ │ ├── bg1.jpg
│ │ ├── bitcoin.svg
│ │ ├── check.svg
│ │ ├── check_circle.png
│ │ ├── circle-1.svg
│ │ ├── complete.jpg
│ │ ├── connected.svg
│ │ ├── ethereum.svg
│ │ ├── failed.jpg
│ │ ├── help.svg
│ │ ├── inputBlock.svg
│ │ ├── inputBlock1.svg
│ │ ├── inputCheck.svg
│ │ ├── logo.svg
│ │ ├── solana.svg
│ │ └── visa.svg
│ ├── solana.svg
│ └── visa.svg
├── components
│ ├── RegistrationForm.tsx
│ ├── common
│ │ ├── Button.tsx
│ │ ├── FormInput.tsx
│ │ ├── LogoHeader.tsx
│ │ ├── SearchInput.tsx
│ │ └── index.ts
│ ├── index.ts
│ └── layout
│ │ ├── MainLayout.tsx
│ │ └── index.ts
├── config
│ └── web3modal.tsx
├── domain-register
│ ├── BillingAndInfo.tsx
│ ├── DomainRegister.tsx
│ ├── DomainSearchResults.tsx
│ └── index.tsx
├── favicon.svg
├── index.css
├── logo.svg
├── main.tsx
├── providers
│ ├── WalletHistoryProvider.tsx
│ └── index.tsx
├── services
│ └── wallet-service.ts
├── types
│ ├── index.ts
│ └── wallet.ts
├── vite-env.d.ts
└── web3-register
│ ├── RegistrationComplete.tsx
│ ├── RegistrationFailed.tsx
│ ├── Web3Register.tsx
│ ├── components
│ ├── RegistrationForm.tsx
│ ├── RegistrationService.ts
│ ├── TransactionHistory.tsx
│ ├── WalletConnector.tsx
│ ├── WalletStatus.tsx
│ └── index.ts
│ └── index.tsx
├── tailwind.config.js
├── tsconfig.json
├── tsconfig.node.json
├── vercel.json
├── vite.config.ts
└── yarn.lock
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [**/**.{yml,ts,tsx,js,json,jsx,html}]
4 | indent_style = space
5 | indent_size = 2
6 | insert_final_newline = true
7 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: [
3 | 'eslint:recommended',
4 | 'plugin:@typescript-eslint/recommended',
5 | 'plugin:prettier/recommended'
6 | ],
7 | env: {
8 | node: true,
9 | mocha: true,
10 | es6: true
11 | },
12 | plugins: ['@typescript-eslint', 'prettier'],
13 | parserOptions: {
14 | parser: '@typescript-eslint/parser'
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Fix how tsconfig.json files are displayed in GitHub's web view,
2 | # by telling it to read said files as JSON With Comments, the
3 | # JSON extension used by TypeScript.
4 | tsconfig.*.json linguist-language=JSON-with-Comments
5 | tsconfig.json linguist-language=JSON-with-Comments
6 |
7 | # Also fix how Visual Studio Code configuration files are read.
8 | .vscode/*.json linguist-language=JSON-with-Comments
9 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Created by https://www.toptal.com/developers/gitignore/api/node,jetbrains+all,visualstudiocode,macos
3 | # Edit at https://www.toptal.com/developers/gitignore?templates=node,jetbrains+all,visualstudiocode,macos
4 |
5 | ### JetBrains+all ###
6 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
7 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
8 |
9 | # User-specific stuff
10 | .idea/**/workspace.xml
11 | .idea/**/tasks.xml
12 | .idea/**/usage.statistics.xml
13 | .idea/**/dictionaries
14 | .idea/**/shelf
15 |
16 | # AWS User-specific
17 | .idea/**/aws.xml
18 |
19 | # Generated files
20 | .idea/**/contentModel.xml
21 |
22 | # Sensitive or high-churn files
23 | .idea/**/dataSources/
24 | .idea/**/dataSources.ids
25 | .idea/**/dataSources.local.xml
26 | .idea/**/sqlDataSources.xml
27 | .idea/**/dynamic.xml
28 | .idea/**/uiDesigner.xml
29 | .idea/**/dbnavigator.xml
30 |
31 | # Gradle
32 | .idea/**/gradle.xml
33 | .idea/**/libraries
34 |
35 | # Gradle and Maven with auto-import
36 | # When using Gradle or Maven with auto-import, you should exclude module files,
37 | # since they will be recreated, and may cause churn. Uncomment if using
38 | # auto-import.
39 | # .idea/artifacts
40 | # .idea/compiler.xml
41 | # .idea/jarRepositories.xml
42 | # .idea/modules.xml
43 | # .idea/*.iml
44 | # .idea/modules
45 | # *.iml
46 | # *.ipr
47 |
48 | # CMake
49 | cmake-build-*/
50 |
51 | # Mongo Explorer plugin
52 | .idea/**/mongoSettings.xml
53 |
54 | # File-based project format
55 | *.iws
56 |
57 | # IntelliJ
58 | out/
59 |
60 | # mpeltonen/sbt-idea plugin
61 | .idea_modules/
62 |
63 | # JIRA plugin
64 | atlassian-ide-plugin.xml
65 |
66 | # Cursive Clojure plugin
67 | .idea/replstate.xml
68 |
69 | # Crashlytics plugin (for Android Studio and IntelliJ)
70 | com_crashlytics_export_strings.xml
71 | crashlytics.properties
72 | crashlytics-build.properties
73 | fabric.properties
74 |
75 | # Editor-based Rest Client
76 | .idea/httpRequests
77 |
78 | # Android studio 3.1+ serialized cache file
79 | .idea/caches/build_file_checksums.ser
80 |
81 | ### JetBrains+all Patch ###
82 | # Ignores the whole .idea folder and all .iml files
83 | # See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360
84 |
85 | .idea/
86 |
87 | # Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
88 |
89 | *.iml
90 | modules.xml
91 | .idea/misc.xml
92 | *.ipr
93 |
94 | # Sonarlint plugin
95 | .idea/sonarlint
96 |
97 | ### macOS ###
98 | # General
99 | .DS_Store
100 | .AppleDouble
101 | .LSOverride
102 |
103 | # Icon must end with two \r
104 | Icon
105 |
106 | # Thumbnails
107 | ._*
108 |
109 | # Files that might appear in the root of a volume
110 | .DocumentRevisions-V100
111 | .fseventsd
112 | .Spotlight-V100
113 | .TemporaryItems
114 | .Trashes
115 | .VolumeIcon.icns
116 | .com.apple.timemachine.donotpresent
117 |
118 | # Directories potentially created on remote AFP share
119 | .AppleDB
120 | .AppleDesktop
121 | Network Trash Folder
122 | Temporary Items
123 | .apdisk
124 |
125 | ### Node ###
126 | # Logs
127 | logs
128 | *.log
129 | npm-debug.log*
130 | yarn-debug.log*
131 | yarn-error.log*
132 | lerna-debug.log*
133 | .pnpm-debug.log*
134 |
135 | # Diagnostic reports (https://nodejs.org/api/report.html)
136 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
137 |
138 | # Runtime data
139 | pids
140 | *.pid
141 | *.seed
142 | *.pid.lock
143 |
144 | # Directory for instrumented libs generated by jscoverage/JSCover
145 | lib-cov
146 |
147 | # Coverage directory used by tools like istanbul
148 | coverage
149 | *.lcov
150 |
151 | # nyc test coverage
152 | .nyc_output
153 |
154 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
155 | .grunt
156 |
157 | # Bower dependency directory (https://bower.io/)
158 | bower_components
159 |
160 | # node-waf configuration
161 | .lock-wscript
162 |
163 | # Compiled binary addons (https://nodejs.org/api/addons.html)
164 | build/Release
165 |
166 | # Dependency directories
167 | node_modules/
168 | jspm_packages/
169 |
170 | # Snowpack dependency directory (https://snowpack.dev/)
171 | web_modules/
172 |
173 | # TypeScript cache
174 | *.tsbuildinfo
175 |
176 | # Optional npm cache directory
177 | .npm
178 |
179 | # Optional eslint cache
180 | .eslintcache
181 |
182 | # Microbundle cache
183 | .rpt2_cache/
184 | .rts2_cache_cjs/
185 | .rts2_cache_es/
186 | .rts2_cache_umd/
187 |
188 | # Optional REPL history
189 | .node_repl_history
190 |
191 | # Output of 'npm pack'
192 | *.tgz
193 |
194 | # Yarn Integrity file
195 | .yarn-integrity
196 |
197 | # dotenv environment variables file
198 | .env
199 | .env.test
200 | .env.production
201 |
202 | # parcel-bundler cache (https://parceljs.org/)
203 | .cache
204 | .parcel-cache
205 |
206 | # Next.js build output
207 | .next
208 | out
209 |
210 | # Nuxt.js build / generate output
211 | .nuxt
212 | dist
213 |
214 | # Gatsby files
215 | .cache/
216 | # Comment in the public line in if your project uses Gatsby and not Next.js
217 | # https://nextjs.org/blog/next-9-1#public-directory-support
218 | # public
219 |
220 | # vuepress build output
221 | .vuepress/dist
222 |
223 | # Serverless directories
224 | .serverless/
225 |
226 | # FuseBox cache
227 | .fusebox/
228 |
229 | # DynamoDB Local files
230 | .dynamodb/
231 |
232 | # TernJS port file
233 | .tern-port
234 |
235 | # Stores VSCode versions used for testing VSCode extensions
236 | .vscode-test
237 |
238 | # yarn v2
239 | .yarn/cache
240 | .yarn/unplugged
241 | .yarn/build-state.yml
242 | .yarn/install-state.gz
243 | .pnp.*
244 |
245 | ### Node Patch ###
246 | # Serverless Webpack directories
247 | .webpack/
248 |
249 | ### VisualStudioCode ###
250 | .vscode/*
251 | !.vscode/settings.json
252 | !.vscode/tasks.json
253 | !.vscode/launch.json
254 | !.vscode/extensions.json
255 | *.code-workspace
256 |
257 | # Local History for Visual Studio Code
258 | .history/
259 |
260 | ### VisualStudioCode Patch ###
261 | # Ignore all local history of files
262 | .history
263 | .ionide
264 |
265 | # End of https://www.toptal.com/developers/gitignore/api/node,jetbrains+all,visualstudiocode,macos
266 |
--------------------------------------------------------------------------------
/.husky/.gitignore:
--------------------------------------------------------------------------------
1 | _
2 |
--------------------------------------------------------------------------------
/.husky/commit-msg:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | . "$(dirname "$0")/_/husky.sh"
3 |
4 | npx --no-install commitlint --edit "$1"
5 |
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | printWidth: 80,
3 | tabWidth: 2,
4 | singleQuote: true,
5 | quoteProps: 'as-needed',
6 | trailingComma: 'none',
7 | bracketSpacing: true,
8 | semi: false,
9 | useTabs: false,
10 | bracketSameLine: false,
11 | proseWrap: 'never'
12 | }
13 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "editor.formatOnSave": true,
3 | "editor.formatOnPaste": true,
4 | "[typescript]": {
5 | "editor.defaultFormatter": "esbenp.prettier-vscode"
6 | },
7 | "[typescriptreact]": {
8 | "editor.defaultFormatter": "esbenp.prettier-vscode"
9 | },
10 | "[json]": {
11 | "editor.defaultFormatter": "esbenp.prettier-vscode"
12 | },
13 | "[javascript]": {
14 | "editor.defaultFormatter": "esbenp.prettier-vscode"
15 | },
16 | "[html]": {
17 | "editor.defaultFormatter": "esbenp.prettier-vscode"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 PDMLab
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/__tests__/fake_test.ts:
--------------------------------------------------------------------------------
1 | import { describe, expect, it } from 'vitest'
2 |
3 | describe('some fake test', () => {
4 | it('should succeed', () => {
5 | expect(true).toBeTruthy()
6 | })
7 | })
8 |
--------------------------------------------------------------------------------
/commitlint.config.js:
--------------------------------------------------------------------------------
1 | module.exports = { extends: ['@commitlint/config-conventional'] }
2 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Web3 Domain
8 |
9 |
10 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vite-react-typescript-tailwind-starter",
3 | "version": "0.0.0",
4 | "scripts": {
5 | "dev": "vite",
6 | "build": "tsc && vite build",
7 | "serve": "vite preview --port 3000",
8 | "lint": "eslint . --ext .ts,.tsx,.js",
9 | "prepare": "husky install",
10 | "test": "vitest"
11 | },
12 | "dependencies": {
13 | "@reown/appkit": "^1.7.4",
14 | "@reown/appkit-adapter-wagmi": "^1.7.4",
15 | "@tanstack/react-query": "^5.75.5",
16 | "@types/styled-components": "^5.1.34",
17 | "@web3modal/wagmi": "^5.1.11",
18 | "axios": "^1.8.4",
19 | "ethers": "^6.13.5",
20 | "react": "^18.2.0",
21 | "react-dom": "^18.2.0",
22 | "styled-components": "^6.1.8",
23 | "tailwind-scrollbar": "^2.0.0",
24 | "viem": "^2.29.0",
25 | "wagmi": "^2.15.2"
26 | },
27 | "devDependencies": {
28 | "@commitlint/cli": "^17.4.3",
29 | "@commitlint/config-conventional": "^17.4.3",
30 | "@heroicons/react": "^2.0.15",
31 | "@tailwindcss/forms": "^0.5.3",
32 | "@types/node": "18.13.0",
33 | "@types/react": "^18.2.55",
34 | "@types/react-dom": "^18.2.19",
35 | "@typescript-eslint/eslint-plugin": "^5.52.0",
36 | "@typescript-eslint/parser": "^5.52.0",
37 | "@vitejs/plugin-react": "^3.1.0",
38 | "autoprefixer": "^10.4.13",
39 | "eslint": "^8.34.0",
40 | "eslint-config-prettier": "^8.6.0",
41 | "eslint-plugin-import": "^2.27.5",
42 | "eslint-plugin-prettier": "^4.2.1",
43 | "eslint-watch": "^8.0.0",
44 | "husky": "^8.0.3",
45 | "postcss": "^8.4.21",
46 | "prettier": "^2.8.4",
47 | "react-router-dom": "6.11.2",
48 | "tailwindcss": "^3.2.6",
49 | "typescript": "^5.3.3",
50 | "vite": "^4.1.1",
51 | "vitest": "^0.32.2"
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {}
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/src/abi/erc20-abi.ts:
--------------------------------------------------------------------------------
1 | export const erc20Abi = [
2 | {
3 | inputs: [
4 | {
5 | internalType: 'string',
6 | name: '_nameX',
7 | type: 'string'
8 | },
9 | {
10 | internalType: 'string',
11 | name: '_symbolX',
12 | type: 'string'
13 | },
14 | {
15 | internalType: 'address',
16 | name: '_toX',
17 | type: 'address'
18 | },
19 | {
20 | internalType: 'uint256',
21 | name: '_totalSupplyX',
22 | type: 'uint256'
23 | }
24 | ],
25 | stateMutability: 'nonpayable',
26 | type: 'constructor'
27 | },
28 | {
29 | inputs: [
30 | {
31 | internalType: 'address',
32 | name: 'spender',
33 | type: 'address'
34 | },
35 | {
36 | internalType: 'uint256',
37 | name: 'allowance',
38 | type: 'uint256'
39 | },
40 | {
41 | internalType: 'uint256',
42 | name: 'needed',
43 | type: 'uint256'
44 | }
45 | ],
46 | name: 'ERC20InsufficientAllowance',
47 | type: 'error'
48 | },
49 | {
50 | inputs: [
51 | {
52 | internalType: 'address',
53 | name: 'sender',
54 | type: 'address'
55 | },
56 | {
57 | internalType: 'uint256',
58 | name: 'balance',
59 | type: 'uint256'
60 | },
61 | {
62 | internalType: 'uint256',
63 | name: 'needed',
64 | type: 'uint256'
65 | }
66 | ],
67 | name: 'ERC20InsufficientBalance',
68 | type: 'error'
69 | },
70 | {
71 | inputs: [
72 | {
73 | internalType: 'address',
74 | name: 'approver',
75 | type: 'address'
76 | }
77 | ],
78 | name: 'ERC20InvalidApprover',
79 | type: 'error'
80 | },
81 | {
82 | inputs: [
83 | {
84 | internalType: 'address',
85 | name: 'receiver',
86 | type: 'address'
87 | }
88 | ],
89 | name: 'ERC20InvalidReceiver',
90 | type: 'error'
91 | },
92 | {
93 | inputs: [
94 | {
95 | internalType: 'address',
96 | name: 'sender',
97 | type: 'address'
98 | }
99 | ],
100 | name: 'ERC20InvalidSender',
101 | type: 'error'
102 | },
103 | {
104 | inputs: [
105 | {
106 | internalType: 'address',
107 | name: 'spender',
108 | type: 'address'
109 | }
110 | ],
111 | name: 'ERC20InvalidSpender',
112 | type: 'error'
113 | },
114 | {
115 | anonymous: false,
116 | inputs: [
117 | {
118 | indexed: true,
119 | internalType: 'address',
120 | name: 'owner',
121 | type: 'address'
122 | },
123 | {
124 | indexed: true,
125 | internalType: 'address',
126 | name: 'spender',
127 | type: 'address'
128 | },
129 | {
130 | indexed: false,
131 | internalType: 'uint256',
132 | name: 'value',
133 | type: 'uint256'
134 | }
135 | ],
136 | name: 'Approval',
137 | type: 'event'
138 | },
139 | {
140 | anonymous: false,
141 | inputs: [
142 | {
143 | indexed: true,
144 | internalType: 'address',
145 | name: 'from',
146 | type: 'address'
147 | },
148 | {
149 | indexed: true,
150 | internalType: 'address',
151 | name: 'to',
152 | type: 'address'
153 | },
154 | {
155 | indexed: false,
156 | internalType: 'uint256',
157 | name: 'value',
158 | type: 'uint256'
159 | }
160 | ],
161 | name: 'Transfer',
162 | type: 'event'
163 | },
164 | {
165 | inputs: [
166 | {
167 | internalType: 'address',
168 | name: 'owner',
169 | type: 'address'
170 | },
171 | {
172 | internalType: 'address',
173 | name: 'spender',
174 | type: 'address'
175 | }
176 | ],
177 | name: 'allowance',
178 | outputs: [
179 | {
180 | internalType: 'uint256',
181 | name: '',
182 | type: 'uint256'
183 | }
184 | ],
185 | stateMutability: 'view',
186 | type: 'function'
187 | },
188 | {
189 | inputs: [
190 | {
191 | internalType: 'address',
192 | name: 'spender',
193 | type: 'address'
194 | },
195 | {
196 | internalType: 'uint256',
197 | name: 'value',
198 | type: 'uint256'
199 | }
200 | ],
201 | name: 'approve',
202 | outputs: [
203 | {
204 | internalType: 'bool',
205 | name: '',
206 | type: 'bool'
207 | }
208 | ],
209 | stateMutability: 'nonpayable',
210 | type: 'function'
211 | },
212 | {
213 | inputs: [
214 | {
215 | internalType: 'address',
216 | name: 'account',
217 | type: 'address'
218 | }
219 | ],
220 | name: 'balanceOf',
221 | outputs: [
222 | {
223 | internalType: 'uint256',
224 | name: '',
225 | type: 'uint256'
226 | }
227 | ],
228 | stateMutability: 'view',
229 | type: 'function'
230 | },
231 | {
232 | inputs: [],
233 | name: 'decimals',
234 | outputs: [
235 | {
236 | internalType: 'uint8',
237 | name: '',
238 | type: 'uint8'
239 | }
240 | ],
241 | stateMutability: 'view',
242 | type: 'function'
243 | },
244 | {
245 | inputs: [],
246 | name: 'name',
247 | outputs: [
248 | {
249 | internalType: 'string',
250 | name: '',
251 | type: 'string'
252 | }
253 | ],
254 | stateMutability: 'view',
255 | type: 'function'
256 | },
257 | {
258 | inputs: [],
259 | name: 'symbol',
260 | outputs: [
261 | {
262 | internalType: 'string',
263 | name: '',
264 | type: 'string'
265 | }
266 | ],
267 | stateMutability: 'view',
268 | type: 'function'
269 | },
270 | {
271 | inputs: [],
272 | name: 'totalSupply',
273 | outputs: [
274 | {
275 | internalType: 'uint256',
276 | name: '',
277 | type: 'uint256'
278 | }
279 | ],
280 | stateMutability: 'view',
281 | type: 'function'
282 | },
283 | {
284 | inputs: [
285 | {
286 | internalType: 'address',
287 | name: 'to',
288 | type: 'address'
289 | },
290 | {
291 | internalType: 'uint256',
292 | name: 'value',
293 | type: 'uint256'
294 | }
295 | ],
296 | name: 'transfer',
297 | outputs: [
298 | {
299 | internalType: 'bool',
300 | name: '',
301 | type: 'bool'
302 | }
303 | ],
304 | stateMutability: 'nonpayable',
305 | type: 'function'
306 | },
307 | {
308 | inputs: [
309 | {
310 | internalType: 'address',
311 | name: 'from',
312 | type: 'address'
313 | },
314 | {
315 | internalType: 'address',
316 | name: 'to',
317 | type: 'address'
318 | },
319 | {
320 | internalType: 'uint256',
321 | name: 'value',
322 | type: 'uint256'
323 | }
324 | ],
325 | name: 'transferFrom',
326 | outputs: [
327 | {
328 | internalType: 'bool',
329 | name: '',
330 | type: 'bool'
331 | }
332 | ],
333 | stateMutability: 'nonpayable',
334 | type: 'function'
335 | }
336 | ]
337 |
--------------------------------------------------------------------------------
/src/abi/lockbox-abi.ts:
--------------------------------------------------------------------------------
1 | export const lockboxAbi = [
2 | {
3 | inputs: [
4 | { internalType: 'address', name: 'assetAddress', type: 'address' },
5 | { internalType: 'address', name: 'lpTokenAddress', type: 'address' }
6 | ],
7 | stateMutability: 'nonpayable',
8 | type: 'constructor'
9 | },
10 | {
11 | inputs: [{ internalType: 'address', name: 'target', type: 'address' }],
12 | name: 'AddressEmptyCode',
13 | type: 'error'
14 | },
15 | {
16 | inputs: [{ internalType: 'address', name: 'account', type: 'address' }],
17 | name: 'AddressInsufficientBalance',
18 | type: 'error'
19 | },
20 | {
21 | inputs: [],
22 | name: 'ERC721EnumerableForbiddenBatchMint',
23 | type: 'error'
24 | },
25 | {
26 | inputs: [
27 | { internalType: 'address', name: 'sender', type: 'address' },
28 | { internalType: 'uint256', name: 'tokenId', type: 'uint256' },
29 | { internalType: 'address', name: 'owner', type: 'address' }
30 | ],
31 | name: 'ERC721IncorrectOwner',
32 | type: 'error'
33 | },
34 | {
35 | inputs: [
36 | { internalType: 'address', name: 'operator', type: 'address' },
37 | { internalType: 'uint256', name: 'tokenId', type: 'uint256' }
38 | ],
39 | name: 'ERC721InsufficientApproval',
40 | type: 'error'
41 | },
42 | {
43 | inputs: [{ internalType: 'address', name: 'approver', type: 'address' }],
44 | name: 'ERC721InvalidApprover',
45 | type: 'error'
46 | },
47 | {
48 | inputs: [{ internalType: 'address', name: 'operator', type: 'address' }],
49 | name: 'ERC721InvalidOperator',
50 | type: 'error'
51 | },
52 | {
53 | inputs: [{ internalType: 'address', name: 'owner', type: 'address' }],
54 | name: 'ERC721InvalidOwner',
55 | type: 'error'
56 | },
57 | {
58 | inputs: [{ internalType: 'address', name: 'receiver', type: 'address' }],
59 | name: 'ERC721InvalidReceiver',
60 | type: 'error'
61 | },
62 | {
63 | inputs: [{ internalType: 'address', name: 'sender', type: 'address' }],
64 | name: 'ERC721InvalidSender',
65 | type: 'error'
66 | },
67 | {
68 | inputs: [{ internalType: 'uint256', name: 'tokenId', type: 'uint256' }],
69 | name: 'ERC721NonexistentToken',
70 | type: 'error'
71 | },
72 | {
73 | inputs: [
74 | { internalType: 'address', name: 'owner', type: 'address' },
75 | { internalType: 'uint256', name: 'index', type: 'uint256' }
76 | ],
77 | name: 'ERC721OutOfBoundsIndex',
78 | type: 'error'
79 | },
80 | { inputs: [], name: 'EnabledAutoRelock', type: 'error' },
81 | { inputs: [], name: 'FailedInnerCall', type: 'error' },
82 | { inputs: [], name: 'MismatchUniswapPairToken', type: 'error' },
83 | { inputs: [], name: 'NoLPTokenOutstanding', type: 'error' },
84 | { inputs: [], name: 'NotOwner', type: 'error' },
85 | { inputs: [], name: 'ReentrancyGuardReentrantCall', type: 'error' },
86 | {
87 | inputs: [{ internalType: 'address', name: 'token', type: 'address' }],
88 | name: 'SafeERC20FailedOperation',
89 | type: 'error'
90 | },
91 | { inputs: [], name: 'TooManyEntries', type: 'error' },
92 | { inputs: [], name: 'UnexpiredLockup', type: 'error' },
93 | { inputs: [], name: 'WrongDuration', type: 'error' },
94 | {
95 | anonymous: false,
96 | inputs: [
97 | {
98 | indexed: true,
99 | internalType: 'address',
100 | name: 'owner',
101 | type: 'address'
102 | },
103 | {
104 | indexed: true,
105 | internalType: 'address',
106 | name: 'approved',
107 | type: 'address'
108 | },
109 | {
110 | indexed: true,
111 | internalType: 'uint256',
112 | name: 'tokenId',
113 | type: 'uint256'
114 | }
115 | ],
116 | name: 'Approval',
117 | type: 'event'
118 | },
119 | {
120 | anonymous: false,
121 | inputs: [
122 | {
123 | indexed: true,
124 | internalType: 'address',
125 | name: 'owner',
126 | type: 'address'
127 | },
128 | {
129 | indexed: true,
130 | internalType: 'address',
131 | name: 'operator',
132 | type: 'address'
133 | },
134 | {
135 | indexed: false,
136 | internalType: 'bool',
137 | name: 'approved',
138 | type: 'bool'
139 | }
140 | ],
141 | name: 'ApprovalForAll',
142 | type: 'event'
143 | },
144 | {
145 | anonymous: false,
146 | inputs: [
147 | {
148 | indexed: false,
149 | internalType: 'uint256',
150 | name: 'tokenId',
151 | type: 'uint256'
152 | },
153 | {
154 | indexed: false,
155 | internalType: 'address',
156 | name: 'addedBy',
157 | type: 'address'
158 | },
159 | {
160 | indexed: false,
161 | internalType: 'bool',
162 | name: 'resetStartTime',
163 | type: 'bool'
164 | },
165 | {
166 | indexed: false,
167 | internalType: 'uint256',
168 | name: 'amountAsset',
169 | type: 'uint256'
170 | },
171 | {
172 | indexed: false,
173 | internalType: 'uint256',
174 | name: 'amountLpToken',
175 | type: 'uint256'
176 | },
177 | {
178 | indexed: false,
179 | internalType: 'uint64',
180 | name: 'effectiveDuration',
181 | type: 'uint64'
182 | },
183 | {
184 | indexed: false,
185 | internalType: 'uint256',
186 | name: 'lpTokenValuation',
187 | type: 'uint256'
188 | }
189 | ],
190 | name: 'AssetsAdded',
191 | type: 'event'
192 | },
193 | {
194 | anonymous: false,
195 | inputs: [
196 | {
197 | indexed: false,
198 | internalType: 'uint256',
199 | name: 'tokenId',
200 | type: 'uint256'
201 | },
202 | {
203 | indexed: false,
204 | internalType: 'uint64',
205 | name: 'newDuration',
206 | type: 'uint64'
207 | },
208 | {
209 | indexed: false,
210 | internalType: 'bool',
211 | name: 'autoRelock',
212 | type: 'bool'
213 | }
214 | ],
215 | name: 'DurationChanged',
216 | type: 'event'
217 | },
218 | {
219 | anonymous: false,
220 | inputs: [
221 | {
222 | indexed: false,
223 | internalType: 'uint256',
224 | name: 'tokenId',
225 | type: 'uint256'
226 | },
227 | {
228 | indexed: false,
229 | internalType: 'string',
230 | name: 'key',
231 | type: 'string'
232 | },
233 | {
234 | indexed: false,
235 | internalType: 'bytes',
236 | name: 'value',
237 | type: 'bytes'
238 | }
239 | ],
240 | name: 'KvUpdate',
241 | type: 'event'
242 | },
243 | {
244 | anonymous: false,
245 | inputs: [
246 | {
247 | indexed: false,
248 | internalType: 'uint256',
249 | name: 'tokenId',
250 | type: 'uint256'
251 | },
252 | {
253 | indexed: false,
254 | internalType: 'address',
255 | name: 'owner',
256 | type: 'address'
257 | },
258 | {
259 | indexed: false,
260 | internalType: 'uint256',
261 | name: 'amountAsset',
262 | type: 'uint256'
263 | },
264 | {
265 | indexed: false,
266 | internalType: 'uint256',
267 | name: 'amountLpToken',
268 | type: 'uint256'
269 | },
270 | {
271 | indexed: false,
272 | internalType: 'uint64',
273 | name: 'durationSeconds',
274 | type: 'uint64'
275 | },
276 | {
277 | indexed: false,
278 | internalType: 'uint256',
279 | name: 'lpTokenValuation',
280 | type: 'uint256'
281 | },
282 | {
283 | indexed: false,
284 | internalType: 'bool',
285 | name: 'autoRelock',
286 | type: 'bool'
287 | }
288 | ],
289 | name: 'LockupCreated',
290 | type: 'event'
291 | },
292 | {
293 | anonymous: false,
294 | inputs: [
295 | {
296 | indexed: true,
297 | internalType: 'address',
298 | name: 'from',
299 | type: 'address'
300 | },
301 | {
302 | indexed: true,
303 | internalType: 'address',
304 | name: 'to',
305 | type: 'address'
306 | },
307 | {
308 | indexed: true,
309 | internalType: 'uint256',
310 | name: 'tokenId',
311 | type: 'uint256'
312 | }
313 | ],
314 | name: 'Transfer',
315 | type: 'event'
316 | },
317 | {
318 | anonymous: false,
319 | inputs: [
320 | {
321 | indexed: false,
322 | internalType: 'uint256',
323 | name: 'tokenId',
324 | type: 'uint256'
325 | },
326 | {
327 | indexed: false,
328 | internalType: 'address',
329 | name: 'owner',
330 | type: 'address'
331 | },
332 | {
333 | indexed: false,
334 | internalType: 'uint256',
335 | name: 'amountAsset',
336 | type: 'uint256'
337 | },
338 | {
339 | indexed: false,
340 | internalType: 'uint256',
341 | name: 'amountLpToken',
342 | type: 'uint256'
343 | }
344 | ],
345 | name: 'Unlocked',
346 | type: 'event'
347 | },
348 | {
349 | inputs: [
350 | { internalType: 'uint256', name: 'lockupId', type: 'uint256' },
351 | { internalType: 'uint256', name: 'assetToAdd', type: 'uint256' },
352 | { internalType: 'uint256', name: 'lpTokenToAdd', type: 'uint256' },
353 | { internalType: 'bool', name: 'resetStartTime', type: 'bool' },
354 | {
355 | components: [
356 | { internalType: 'string', name: 'key', type: 'string' },
357 | { internalType: 'bytes', name: 'val', type: 'bytes' }
358 | ],
359 | internalType: 'struct ILockBox.KeyVal[]',
360 | name: 'entriesToUpdate',
361 | type: 'tuple[]'
362 | }
363 | ],
364 | name: 'addAssets',
365 | outputs: [],
366 | stateMutability: 'nonpayable',
367 | type: 'function'
368 | },
369 | {
370 | inputs: [
371 | { internalType: 'address', name: 'to', type: 'address' },
372 | { internalType: 'uint256', name: 'tokenId', type: 'uint256' }
373 | ],
374 | name: 'approve',
375 | outputs: [],
376 | stateMutability: 'nonpayable',
377 | type: 'function'
378 | },
379 | {
380 | inputs: [],
381 | name: 'assetContract',
382 | outputs: [{ internalType: 'contract IERC20', name: '', type: 'address' }],
383 | stateMutability: 'view',
384 | type: 'function'
385 | },
386 | {
387 | inputs: [{ internalType: 'address', name: 'owner', type: 'address' }],
388 | name: 'balanceOf',
389 | outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
390 | stateMutability: 'view',
391 | type: 'function'
392 | },
393 | {
394 | inputs: [
395 | { internalType: 'uint256', name: 'lockupId', type: 'uint256' },
396 | {
397 | internalType: 'uint64',
398 | name: 'newDurationSeconds',
399 | type: 'uint64'
400 | },
401 | { internalType: 'bool', name: 'autoRelock', type: 'bool' },
402 | {
403 | components: [
404 | { internalType: 'string', name: 'key', type: 'string' },
405 | { internalType: 'bytes', name: 'val', type: 'bytes' }
406 | ],
407 | internalType: 'struct ILockBox.KeyVal[]',
408 | name: 'entriesToUpdate',
409 | type: 'tuple[]'
410 | }
411 | ],
412 | name: 'changeDuration',
413 | outputs: [],
414 | stateMutability: 'nonpayable',
415 | type: 'function'
416 | },
417 | {
418 | inputs: [],
419 | name: 'computeLpStats',
420 | outputs: [
421 | {
422 | internalType: 'uint256',
423 | name: 'currentLpTokens',
424 | type: 'uint256'
425 | },
426 | { internalType: 'uint256', name: 'currentAsset', type: 'uint256' },
427 | { internalType: 'uint256', name: 'lastLpTokens', type: 'uint256' },
428 | { internalType: 'uint256', name: 'lastAsset', type: 'uint256' },
429 | {
430 | internalType: 'uint256',
431 | name: 'averageLpTokens',
432 | type: 'uint256'
433 | },
434 | { internalType: 'uint256', name: 'averageAsset', type: 'uint256' },
435 | { internalType: 'uint32', name: 'timestamp', type: 'uint32' },
436 | { internalType: 'bool', name: 'useAverage', type: 'bool' },
437 | { internalType: 'bool', name: 'persistUpdate', type: 'bool' }
438 | ],
439 | stateMutability: 'view',
440 | type: 'function'
441 | },
442 | {
443 | inputs: [],
444 | name: 'computeUpdateLpStats',
445 | outputs: [
446 | {
447 | internalType: 'uint256',
448 | name: 'totalLpTokensOutstanding',
449 | type: 'uint256'
450 | },
451 | {
452 | internalType: 'uint256',
453 | name: 'totalAssetUnitsStaked',
454 | type: 'uint256'
455 | }
456 | ],
457 | stateMutability: 'nonpayable',
458 | type: 'function'
459 | },
460 | {
461 | inputs: [
462 | { internalType: 'uint256', name: 'amountAsset', type: 'uint256' },
463 | { internalType: 'uint256', name: 'amountLpToken', type: 'uint256' },
464 | { internalType: 'uint64', name: 'durationSeconds', type: 'uint64' },
465 | { internalType: 'bool', name: 'autoRelock', type: 'bool' },
466 | {
467 | components: [
468 | { internalType: 'string', name: 'key', type: 'string' },
469 | { internalType: 'bytes', name: 'val', type: 'bytes' }
470 | ],
471 | internalType: 'struct ILockBox.KeyVal[]',
472 | name: 'entriesToUpdate',
473 | type: 'tuple[]'
474 | }
475 | ],
476 | name: 'createLockup',
477 | outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
478 | stateMutability: 'nonpayable',
479 | type: 'function'
480 | },
481 | {
482 | inputs: [{ internalType: 'uint256', name: 'tokenId', type: 'uint256' }],
483 | name: 'getApproved',
484 | outputs: [{ internalType: 'address', name: '', type: 'address' }],
485 | stateMutability: 'view',
486 | type: 'function'
487 | },
488 | {
489 | inputs: [],
490 | name: 'getCurrentLpStats',
491 | outputs: [
492 | {
493 | internalType: 'uint256',
494 | name: 'totalLpTokensOutstanding',
495 | type: 'uint256'
496 | },
497 | {
498 | internalType: 'uint256',
499 | name: 'totalAssetUnitsStaked',
500 | type: 'uint256'
501 | },
502 | { internalType: 'uint32', name: 'timestamp', type: 'uint32' }
503 | ],
504 | stateMutability: 'view',
505 | type: 'function'
506 | },
507 | {
508 | inputs: [],
509 | name: 'getLpStats',
510 | outputs: [
511 | { internalType: 'uint256', name: 'lastLpTokens', type: 'uint256' },
512 | { internalType: 'uint256', name: 'lastAsset', type: 'uint256' },
513 | {
514 | internalType: 'uint256',
515 | name: 'averageLpTokens',
516 | type: 'uint256'
517 | },
518 | { internalType: 'uint256', name: 'averageAsset', type: 'uint256' },
519 | { internalType: 'uint32', name: 'timestamp', type: 'uint32' }
520 | ],
521 | stateMutability: 'view',
522 | type: 'function'
523 | },
524 | {
525 | inputs: [
526 | { internalType: 'address', name: 'owner', type: 'address' },
527 | { internalType: 'address', name: 'operator', type: 'address' }
528 | ],
529 | name: 'isApprovedForAll',
530 | outputs: [{ internalType: 'bool', name: '', type: 'bool' }],
531 | stateMutability: 'view',
532 | type: 'function'
533 | },
534 | {
535 | inputs: [
536 | { internalType: 'uint256', name: '', type: 'uint256' },
537 | { internalType: 'string', name: '', type: 'string' }
538 | ],
539 | name: 'kvStore',
540 | outputs: [{ internalType: 'bytes', name: '', type: 'bytes' }],
541 | stateMutability: 'view',
542 | type: 'function'
543 | },
544 | {
545 | inputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
546 | name: 'lockups',
547 | outputs: [
548 | { internalType: 'uint256', name: 'amountAsset', type: 'uint256' },
549 | { internalType: 'uint256', name: 'amountLpToken', type: 'uint256' },
550 | {
551 | internalType: 'uint256',
552 | name: 'lpTokenValuation',
553 | type: 'uint256'
554 | },
555 | {
556 | internalType: 'uint256',
557 | name: 'assetSecondsLocked',
558 | type: 'uint256'
559 | },
560 | {
561 | internalType: 'uint256',
562 | name: 'lpSecondsLocked',
563 | type: 'uint256'
564 | },
565 | { internalType: 'uint64', name: 'createTime', type: 'uint64' },
566 | { internalType: 'uint64', name: 'lastDepositTime', type: 'uint64' },
567 | { internalType: 'uint64', name: 'durationSeconds', type: 'uint64' },
568 | { internalType: 'bool', name: 'autoRelock', type: 'bool' }
569 | ],
570 | stateMutability: 'view',
571 | type: 'function'
572 | },
573 | {
574 | inputs: [],
575 | name: 'lpTokenContract',
576 | outputs: [{ internalType: 'contract IERC20', name: '', type: 'address' }],
577 | stateMutability: 'view',
578 | type: 'function'
579 | },
580 | {
581 | inputs: [],
582 | name: 'name',
583 | outputs: [{ internalType: 'string', name: '', type: 'string' }],
584 | stateMutability: 'view',
585 | type: 'function'
586 | },
587 | {
588 | inputs: [{ internalType: 'uint256', name: 'tokenId', type: 'uint256' }],
589 | name: 'ownerOf',
590 | outputs: [{ internalType: 'address', name: '', type: 'address' }],
591 | stateMutability: 'view',
592 | type: 'function'
593 | },
594 | {
595 | inputs: [
596 | { internalType: 'address', name: 'from', type: 'address' },
597 | { internalType: 'address', name: 'to', type: 'address' },
598 | { internalType: 'uint256', name: 'tokenId', type: 'uint256' }
599 | ],
600 | name: 'safeTransferFrom',
601 | outputs: [],
602 | stateMutability: 'nonpayable',
603 | type: 'function'
604 | },
605 | {
606 | inputs: [
607 | { internalType: 'address', name: 'from', type: 'address' },
608 | { internalType: 'address', name: 'to', type: 'address' },
609 | { internalType: 'uint256', name: 'tokenId', type: 'uint256' },
610 | { internalType: 'bytes', name: 'data', type: 'bytes' }
611 | ],
612 | name: 'safeTransferFrom',
613 | outputs: [],
614 | stateMutability: 'nonpayable',
615 | type: 'function'
616 | },
617 | {
618 | inputs: [
619 | { internalType: 'address', name: 'operator', type: 'address' },
620 | { internalType: 'bool', name: 'approved', type: 'bool' }
621 | ],
622 | name: 'setApprovalForAll',
623 | outputs: [],
624 | stateMutability: 'nonpayable',
625 | type: 'function'
626 | },
627 | {
628 | inputs: [
629 | { internalType: 'uint256', name: 'tokenId', type: 'uint256' },
630 | {
631 | components: [
632 | { internalType: 'string', name: 'key', type: 'string' },
633 | { internalType: 'bytes', name: 'val', type: 'bytes' }
634 | ],
635 | internalType: 'struct ILockBox.KeyVal[]',
636 | name: 'entriesToUpdate',
637 | type: 'tuple[]'
638 | }
639 | ],
640 | name: 'setKvStore',
641 | outputs: [],
642 | stateMutability: 'nonpayable',
643 | type: 'function'
644 | },
645 | {
646 | inputs: [{ internalType: 'bytes4', name: 'interfaceId', type: 'bytes4' }],
647 | name: 'supportsInterface',
648 | outputs: [{ internalType: 'bool', name: '', type: 'bool' }],
649 | stateMutability: 'view',
650 | type: 'function'
651 | },
652 | {
653 | inputs: [],
654 | name: 'symbol',
655 | outputs: [{ internalType: 'string', name: '', type: 'string' }],
656 | stateMutability: 'view',
657 | type: 'function'
658 | },
659 | {
660 | inputs: [{ internalType: 'uint256', name: 'index', type: 'uint256' }],
661 | name: 'tokenByIndex',
662 | outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
663 | stateMutability: 'view',
664 | type: 'function'
665 | },
666 | {
667 | inputs: [
668 | { internalType: 'address', name: 'owner', type: 'address' },
669 | { internalType: 'uint256', name: 'index', type: 'uint256' }
670 | ],
671 | name: 'tokenOfOwnerByIndex',
672 | outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
673 | stateMutability: 'view',
674 | type: 'function'
675 | },
676 | {
677 | inputs: [{ internalType: 'uint256', name: 'tokenId', type: 'uint256' }],
678 | name: 'tokenURI',
679 | outputs: [{ internalType: 'string', name: '', type: 'string' }],
680 | stateMutability: 'view',
681 | type: 'function'
682 | },
683 | {
684 | inputs: [],
685 | name: 'totalAssetLocked',
686 | outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
687 | stateMutability: 'view',
688 | type: 'function'
689 | },
690 | {
691 | inputs: [],
692 | name: 'totalLpTokenLocked',
693 | outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
694 | stateMutability: 'view',
695 | type: 'function'
696 | },
697 | {
698 | inputs: [],
699 | name: 'totalSupply',
700 | outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
701 | stateMutability: 'view',
702 | type: 'function'
703 | },
704 | {
705 | inputs: [
706 | { internalType: 'address', name: 'from', type: 'address' },
707 | { internalType: 'address', name: 'to', type: 'address' },
708 | { internalType: 'uint256', name: 'tokenId', type: 'uint256' }
709 | ],
710 | name: 'transferFrom',
711 | outputs: [],
712 | stateMutability: 'nonpayable',
713 | type: 'function'
714 | },
715 | {
716 | inputs: [{ internalType: 'uint256', name: 'lockupId', type: 'uint256' }],
717 | name: 'unlock',
718 | outputs: [],
719 | stateMutability: 'nonpayable',
720 | type: 'function'
721 | }
722 | ]
723 |
--------------------------------------------------------------------------------
/src/abi/multipay-abi.ts:
--------------------------------------------------------------------------------
1 | export const multiPlayAbi = [
2 | {
3 | inputs: [
4 | {
5 | internalType: 'contract ILockBox',
6 | name: 'lockboxContract',
7 | type: 'address'
8 | }
9 | ],
10 | name: 'createEmptyStake',
11 | outputs: [],
12 | stateMutability: 'nonpayable',
13 | type: 'function'
14 | },
15 | {
16 | inputs: [{ internalType: 'address', name: 'user', type: 'address' }],
17 | name: 'getStake',
18 | outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
19 | stateMutability: 'view',
20 | type: 'function'
21 | },
22 | {
23 | inputs: [
24 | {
25 | internalType: 'contract IYieldVault',
26 | name: 'yv',
27 | type: 'address'
28 | },
29 | { internalType: 'uint256[]', name: 'tokenIds', type: 'uint256[]' }
30 | ],
31 | name: 'lpComputeYields',
32 | outputs: [
33 | { internalType: 'uint256[]', name: 'available', type: 'uint256[]' }
34 | ],
35 | stateMutability: 'view',
36 | type: 'function'
37 | },
38 | {
39 | inputs: [
40 | { internalType: 'contract IERC20', name: 'token', type: 'address' },
41 | { internalType: 'address', name: 'from', type: 'address' },
42 | {
43 | components: [
44 | { internalType: 'address', name: 'recipient', type: 'address' },
45 | { internalType: 'uint256', name: 'amount', type: 'uint256' }
46 | ],
47 | internalType: 'struct Multipay.Payout[]',
48 | name: 'payouts',
49 | type: 'tuple[]'
50 | }
51 | ],
52 | name: 'multipay',
53 | outputs: [],
54 | stateMutability: 'nonpayable',
55 | type: 'function'
56 | },
57 | {
58 | inputs: [
59 | {
60 | internalType: 'contract IAssign',
61 | name: 'assignContract',
62 | type: 'address'
63 | },
64 | {
65 | components: [
66 | {
67 | internalType: 'enum IInfra.UnitType',
68 | name: 't',
69 | type: 'uint8'
70 | },
71 | {
72 | internalType: 'uint64',
73 | name: 'parentDomain',
74 | type: 'uint64'
75 | },
76 | { internalType: 'string', name: 'name', type: 'string' },
77 | {
78 | internalType: 'uint256',
79 | name: 'yieldCredits',
80 | type: 'uint256'
81 | },
82 | { internalType: 'address', name: 'to', type: 'address' }
83 | ],
84 | internalType: 'struct Multipay.Registration[]',
85 | name: 'regs',
86 | type: 'tuple[]'
87 | }
88 | ],
89 | name: 'multireg',
90 | outputs: [],
91 | stateMutability: 'nonpayable',
92 | type: 'function'
93 | },
94 | {
95 | inputs: [
96 | {
97 | internalType: 'contract IERC721',
98 | name: 'nftContract',
99 | type: 'address'
100 | },
101 | { internalType: 'address', name: 'from', type: 'address' },
102 | {
103 | components: [
104 | { internalType: 'address', name: 'recipient', type: 'address' },
105 | { internalType: 'uint256', name: 'nftId', type: 'uint256' }
106 | ],
107 | internalType: 'struct Multipay.NftSend[]',
108 | name: 'sends',
109 | type: 'tuple[]'
110 | }
111 | ],
112 | name: 'nftSend',
113 | outputs: [],
114 | stateMutability: 'nonpayable',
115 | type: 'function'
116 | },
117 | {
118 | inputs: [
119 | { internalType: 'address', name: '', type: 'address' },
120 | { internalType: 'address', name: '', type: 'address' },
121 | { internalType: 'uint256', name: '', type: 'uint256' },
122 | { internalType: 'bytes', name: '', type: 'bytes' }
123 | ],
124 | name: 'onERC721Received',
125 | outputs: [{ internalType: 'bytes4', name: '', type: 'bytes4' }],
126 | stateMutability: 'nonpayable',
127 | type: 'function'
128 | },
129 | {
130 | inputs: [
131 | { internalType: 'contract IERC20', name: 'token', type: 'address' },
132 | {
133 | internalType: 'contract ILockBox',
134 | name: 'lockboxContract',
135 | type: 'address'
136 | },
137 | { internalType: 'contract IPNS', name: 'ipns', type: 'address' },
138 | {
139 | internalType: 'uint256',
140 | name: 'maxStakeAmount',
141 | type: 'uint256'
142 | },
143 | { internalType: 'bytes', name: 'name', type: 'bytes' },
144 | { internalType: 'bytes', name: 'records', type: 'bytes' }
145 | ],
146 | name: 'stakeDomain',
147 | outputs: [],
148 | stateMutability: 'nonpayable',
149 | type: 'function'
150 | }
151 | ]
152 |
--------------------------------------------------------------------------------
/src/abi/pns-abi.ts:
--------------------------------------------------------------------------------
1 | export const pnsAbi = [
2 | {
3 | inputs: [
4 | {
5 | internalType: 'contract ILockBox',
6 | name: '_lockboxContract',
7 | type: 'address'
8 | },
9 | { internalType: 'bool', name: '_whitelistActive', type: 'bool' },
10 | { internalType: 'address', name: 'pricer', type: 'address' }
11 | ],
12 | stateMutability: 'nonpayable',
13 | type: 'constructor'
14 | },
15 | {
16 | inputs: [{ internalType: 'address', name: 'owner', type: 'address' }],
17 | name: 'OwnableInvalidOwner',
18 | type: 'error'
19 | },
20 | {
21 | inputs: [{ internalType: 'address', name: 'account', type: 'address' }],
22 | name: 'OwnableUnauthorizedAccount',
23 | type: 'error'
24 | },
25 | {
26 | anonymous: false,
27 | inputs: [
28 | {
29 | indexed: false,
30 | internalType: 'address',
31 | name: 'sender',
32 | type: 'address'
33 | },
34 | {
35 | indexed: false,
36 | internalType: 'uint64',
37 | name: 'id',
38 | type: 'uint64'
39 | },
40 | {
41 | indexed: false,
42 | internalType: 'bool',
43 | name: 'isBlacklisted',
44 | type: 'bool'
45 | }
46 | ],
47 | name: 'Blacklist',
48 | type: 'event'
49 | },
50 | {
51 | anonymous: false,
52 | inputs: [
53 | {
54 | indexed: false,
55 | internalType: 'address',
56 | name: 'sender',
57 | type: 'address'
58 | },
59 | {
60 | indexed: false,
61 | internalType: 'uint64',
62 | name: 'id',
63 | type: 'uint64'
64 | },
65 | {
66 | indexed: false,
67 | internalType: 'uint64',
68 | name: 'parentId',
69 | type: 'uint64'
70 | },
71 | {
72 | indexed: false,
73 | internalType: 'bytes',
74 | name: 'name',
75 | type: 'bytes'
76 | },
77 | {
78 | indexed: false,
79 | internalType: 'bytes',
80 | name: 'records',
81 | type: 'bytes'
82 | }
83 | ],
84 | name: 'CreateSubdomain',
85 | type: 'event'
86 | },
87 | {
88 | anonymous: false,
89 | inputs: [
90 | {
91 | indexed: false,
92 | internalType: 'address',
93 | name: 'sender',
94 | type: 'address'
95 | },
96 | {
97 | indexed: false,
98 | internalType: 'uint64',
99 | name: 'id',
100 | type: 'uint64'
101 | }
102 | ],
103 | name: 'Destroy',
104 | type: 'event'
105 | },
106 | {
107 | anonymous: false,
108 | inputs: [
109 | {
110 | indexed: false,
111 | internalType: 'address',
112 | name: 'sender',
113 | type: 'address'
114 | },
115 | {
116 | indexed: false,
117 | internalType: 'bytes32',
118 | name: 'nameHash',
119 | type: 'bytes32'
120 | },
121 | {
122 | indexed: false,
123 | internalType: 'uint64',
124 | name: 'lockupId',
125 | type: 'uint64'
126 | }
127 | ],
128 | name: 'DestroyPrereg',
129 | type: 'event'
130 | },
131 | {
132 | anonymous: false,
133 | inputs: [
134 | {
135 | indexed: true,
136 | internalType: 'address',
137 | name: 'previousOwner',
138 | type: 'address'
139 | },
140 | {
141 | indexed: true,
142 | internalType: 'address',
143 | name: 'newOwner',
144 | type: 'address'
145 | }
146 | ],
147 | name: 'OwnershipTransferred',
148 | type: 'event'
149 | },
150 | {
151 | anonymous: false,
152 | inputs: [
153 | {
154 | indexed: false,
155 | internalType: 'address',
156 | name: 'sender',
157 | type: 'address'
158 | },
159 | {
160 | indexed: false,
161 | internalType: 'bytes32',
162 | name: 'nameHash',
163 | type: 'bytes32'
164 | },
165 | {
166 | indexed: false,
167 | internalType: 'uint64',
168 | name: 'lockupId',
169 | type: 'uint64'
170 | }
171 | ],
172 | name: 'Preregister',
173 | type: 'event'
174 | },
175 | {
176 | anonymous: false,
177 | inputs: [
178 | {
179 | indexed: false,
180 | internalType: 'address',
181 | name: 'sender',
182 | type: 'address'
183 | },
184 | {
185 | indexed: false,
186 | internalType: 'uint64',
187 | name: 'id',
188 | type: 'uint64'
189 | },
190 | {
191 | indexed: false,
192 | internalType: 'uint64',
193 | name: 'lockupId',
194 | type: 'uint64'
195 | },
196 | {
197 | indexed: false,
198 | internalType: 'bytes',
199 | name: 'name',
200 | type: 'bytes'
201 | },
202 | {
203 | indexed: false,
204 | internalType: 'bytes',
205 | name: 'records',
206 | type: 'bytes'
207 | }
208 | ],
209 | name: 'Register',
210 | type: 'event'
211 | },
212 | {
213 | anonymous: false,
214 | inputs: [
215 | {
216 | indexed: false,
217 | internalType: 'address',
218 | name: 'sender',
219 | type: 'address'
220 | },
221 | {
222 | indexed: false,
223 | internalType: 'uint64',
224 | name: 'id',
225 | type: 'uint64'
226 | },
227 | {
228 | indexed: false,
229 | internalType: 'uint64',
230 | name: 'oldLockupId',
231 | type: 'uint64'
232 | },
233 | {
234 | indexed: false,
235 | internalType: 'uint64',
236 | name: 'newLockupId',
237 | type: 'uint64'
238 | },
239 | {
240 | indexed: false,
241 | internalType: 'bytes',
242 | name: 'records',
243 | type: 'bytes'
244 | }
245 | ],
246 | name: 'Takeover',
247 | type: 'event'
248 | },
249 | {
250 | anonymous: false,
251 | inputs: [
252 | {
253 | indexed: false,
254 | internalType: 'address',
255 | name: 'sender',
256 | type: 'address'
257 | },
258 | {
259 | indexed: false,
260 | internalType: 'uint64',
261 | name: 'id',
262 | type: 'uint64'
263 | },
264 | {
265 | indexed: false,
266 | internalType: 'bytes',
267 | name: 'records',
268 | type: 'bytes'
269 | }
270 | ],
271 | name: 'UpdateRecords',
272 | type: 'event'
273 | },
274 | {
275 | inputs: [],
276 | name: 'PREREG_LIFETIME_SECONDS',
277 | outputs: [{ internalType: 'uint64', name: '', type: 'uint64' }],
278 | stateMutability: 'view',
279 | type: 'function'
280 | },
281 | {
282 | inputs: [
283 | { internalType: 'uint64', name: 'id', type: 'uint64' },
284 | { internalType: 'address', name: 'who', type: 'address' }
285 | ],
286 | name: 'authorizedFor',
287 | outputs: [{ internalType: 'bool', name: '', type: 'bool' }],
288 | stateMutability: 'view',
289 | type: 'function'
290 | },
291 | {
292 | inputs: [
293 | { internalType: 'uint64', name: 'lockupId', type: 'uint64' },
294 | { internalType: 'bytes', name: 'name', type: 'bytes' },
295 | { internalType: 'bytes', name: 'records', type: 'bytes' }
296 | ],
297 | name: 'computePreregHash',
298 | outputs: [{ internalType: 'bytes32', name: '', type: 'bytes32' }],
299 | stateMutability: 'pure',
300 | type: 'function'
301 | },
302 | {
303 | inputs: [
304 | { internalType: 'uint64', name: 'parentId', type: 'uint64' },
305 | { internalType: 'bytes', name: 'subdomainLabel', type: 'bytes' },
306 | { internalType: 'bytes', name: 'records', type: 'bytes' }
307 | ],
308 | name: 'createSubdomain',
309 | outputs: [],
310 | stateMutability: 'nonpayable',
311 | type: 'function'
312 | },
313 | {
314 | inputs: [],
315 | name: 'currentMinLockup',
316 | outputs: [{ internalType: 'uint256', name: 'price', type: 'uint256' }],
317 | stateMutability: 'view',
318 | type: 'function'
319 | },
320 | {
321 | inputs: [{ internalType: 'uint64', name: 'id', type: 'uint64' }],
322 | name: 'destroy',
323 | outputs: [],
324 | stateMutability: 'nonpayable',
325 | type: 'function'
326 | },
327 | {
328 | inputs: [
329 | { internalType: 'bytes32', name: 'nameHash', type: 'bytes32' },
330 | { internalType: 'uint64', name: 'lockupId', type: 'uint64' }
331 | ],
332 | name: 'destroyPrereg',
333 | outputs: [],
334 | stateMutability: 'nonpayable',
335 | type: 'function'
336 | },
337 | {
338 | inputs: [{ internalType: 'uint64', name: 'last', type: 'uint64' }],
339 | name: 'domainIdFreeList',
340 | outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
341 | stateMutability: 'view',
342 | type: 'function'
343 | },
344 | {
345 | inputs: [],
346 | name: 'getAdmin',
347 | outputs: [{ internalType: 'address', name: '', type: 'address' }],
348 | stateMutability: 'view',
349 | type: 'function'
350 | },
351 | {
352 | inputs: [{ internalType: 'uint64', name: 'id', type: 'uint64' }],
353 | name: 'getDomain',
354 | outputs: [
355 | { internalType: 'uint64', name: 'owner', type: 'uint64' },
356 | { internalType: 'uint8', name: 'subdomains', type: 'uint8' },
357 | { internalType: 'bool', name: 'blacklisted', type: 'bool' },
358 | { internalType: 'bytes', name: 'name', type: 'bytes' },
359 | { internalType: 'bytes', name: 'records', type: 'bytes' }
360 | ],
361 | stateMutability: 'view',
362 | type: 'function'
363 | },
364 | {
365 | inputs: [{ internalType: 'bytes', name: 'fqdn', type: 'bytes' }],
366 | name: 'getDomainIdByFQDN',
367 | outputs: [{ internalType: 'uint64', name: '', type: 'uint64' }],
368 | stateMutability: 'view',
369 | type: 'function'
370 | },
371 | {
372 | inputs: [{ internalType: 'uint64', name: 'lockupId', type: 'uint64' }],
373 | name: 'getDomainIdByLockupId',
374 | outputs: [{ internalType: 'uint64', name: '', type: 'uint64' }],
375 | stateMutability: 'view',
376 | type: 'function'
377 | },
378 | {
379 | inputs: [{ internalType: 'uint64[]', name: 'ids', type: 'uint64[]' }],
380 | name: 'getDomains',
381 | outputs: [
382 | {
383 | components: [
384 | { internalType: 'uint64', name: 'owner', type: 'uint64' },
385 | { internalType: 'uint8', name: 'subdomains', type: 'uint8' },
386 | { internalType: 'bool', name: 'blacklisted', type: 'bool' },
387 | { internalType: 'bytes', name: 'name', type: 'bytes' },
388 | { internalType: 'bytes', name: 'records', type: 'bytes' }
389 | ],
390 | internalType: 'struct IPNS.Domain[]',
391 | name: 'out',
392 | type: 'tuple[]'
393 | }
394 | ],
395 | stateMutability: 'view',
396 | type: 'function'
397 | },
398 | {
399 | inputs: [{ internalType: 'uint256', name: 'lockupId', type: 'uint256' }],
400 | name: 'getLockupValuation',
401 | outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
402 | stateMutability: 'view',
403 | type: 'function'
404 | },
405 | {
406 | inputs: [{ internalType: 'bytes32', name: 'nameHash', type: 'bytes32' }],
407 | name: 'getPrereg',
408 | outputs: [
409 | { internalType: 'uint64', name: 'timestamp', type: 'uint64' },
410 | { internalType: 'uint64', name: 'lockupId', type: 'uint64' }
411 | ],
412 | stateMutability: 'view',
413 | type: 'function'
414 | },
415 | {
416 | inputs: [],
417 | name: 'getPricingInfo',
418 | outputs: [
419 | { internalType: 'address', name: 'pricer', type: 'address' },
420 | { internalType: 'uint64', name: 'lastRegTime', type: 'uint64' },
421 | { internalType: 'uint256', name: 'lastRegPrice', type: 'uint256' }
422 | ],
423 | stateMutability: 'view',
424 | type: 'function'
425 | },
426 | {
427 | inputs: [{ internalType: 'address', name: 'addr', type: 'address' }],
428 | name: 'isAddressWhitelisted',
429 | outputs: [{ internalType: 'bool', name: '', type: 'bool' }],
430 | stateMutability: 'view',
431 | type: 'function'
432 | },
433 | {
434 | inputs: [],
435 | name: 'isRegistrationWhitelistActive',
436 | outputs: [{ internalType: 'bool', name: '', type: 'bool' }],
437 | stateMutability: 'view',
438 | type: 'function'
439 | },
440 | {
441 | inputs: [],
442 | name: 'lockboxContract',
443 | outputs: [{ internalType: 'contract ILockBox', name: '', type: 'address' }],
444 | stateMutability: 'view',
445 | type: 'function'
446 | },
447 | {
448 | inputs: [],
449 | name: 'nextDomainId',
450 | outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
451 | stateMutability: 'view',
452 | type: 'function'
453 | },
454 | {
455 | inputs: [],
456 | name: 'owner',
457 | outputs: [{ internalType: 'address', name: '', type: 'address' }],
458 | stateMutability: 'view',
459 | type: 'function'
460 | },
461 | {
462 | inputs: [{ internalType: 'uint64', name: 'id', type: 'uint64' }],
463 | name: 'ownerLockup',
464 | outputs: [{ internalType: 'uint64', name: '', type: 'uint64' }],
465 | stateMutability: 'view',
466 | type: 'function'
467 | },
468 | {
469 | inputs: [
470 | { internalType: 'bytes32', name: 'nameHash', type: 'bytes32' },
471 | { internalType: 'uint64', name: 'lockupId', type: 'uint64' }
472 | ],
473 | name: 'preregister',
474 | outputs: [],
475 | stateMutability: 'nonpayable',
476 | type: 'function'
477 | },
478 | {
479 | inputs: [
480 | { internalType: 'uint64', name: 'lockupId', type: 'uint64' },
481 | { internalType: 'bytes', name: 'name', type: 'bytes' },
482 | { internalType: 'bytes', name: 'records', type: 'bytes' }
483 | ],
484 | name: 'register',
485 | outputs: [],
486 | stateMutability: 'nonpayable',
487 | type: 'function'
488 | },
489 | {
490 | inputs: [],
491 | name: 'renounceOwnership',
492 | outputs: [],
493 | stateMutability: 'nonpayable',
494 | type: 'function'
495 | },
496 | {
497 | inputs: [{ internalType: 'address', name: 'admin', type: 'address' }],
498 | name: 'setAdmin',
499 | outputs: [],
500 | stateMutability: 'nonpayable',
501 | type: 'function'
502 | },
503 | {
504 | inputs: [
505 | { internalType: 'uint64', name: 'id', type: 'uint64' },
506 | { internalType: 'bool', name: 'blacklisted', type: 'bool' }
507 | ],
508 | name: 'setDomainBlacklisted',
509 | outputs: [],
510 | stateMutability: 'nonpayable',
511 | type: 'function'
512 | },
513 | {
514 | inputs: [{ internalType: 'address', name: 'lbox', type: 'address' }],
515 | name: 'setLockbox',
516 | outputs: [],
517 | stateMutability: 'nonpayable',
518 | type: 'function'
519 | },
520 | {
521 | inputs: [{ internalType: 'address', name: 'pricer', type: 'address' }],
522 | name: 'setPricer',
523 | outputs: [],
524 | stateMutability: 'nonpayable',
525 | type: 'function'
526 | },
527 | {
528 | inputs: [
529 | { internalType: 'uint64', name: 'id', type: 'uint64' },
530 | { internalType: 'uint64', name: 'newLockupId', type: 'uint64' },
531 | { internalType: 'bytes', name: 'records', type: 'bytes' }
532 | ],
533 | name: 'takeover',
534 | outputs: [],
535 | stateMutability: 'nonpayable',
536 | type: 'function'
537 | },
538 | {
539 | inputs: [{ internalType: 'address', name: 'newOwner', type: 'address' }],
540 | name: 'transferOwnership',
541 | outputs: [],
542 | stateMutability: 'nonpayable',
543 | type: 'function'
544 | },
545 | {
546 | inputs: [
547 | {
548 | components: [
549 | { internalType: 'uint64', name: 'parentId', type: 'uint64' },
550 | {
551 | internalType: 'bytes',
552 | name: 'subdomainLabel',
553 | type: 'bytes'
554 | },
555 | { internalType: 'bytes', name: 'records', type: 'bytes' }
556 | ],
557 | internalType: 'struct IPNS.NewSubdomain[]',
558 | name: 'newSubdomains',
559 | type: 'tuple[]'
560 | },
561 | {
562 | components: [
563 | { internalType: 'uint64', name: 'id', type: 'uint64' },
564 | { internalType: 'bytes', name: 'records', type: 'bytes' }
565 | ],
566 | internalType: 'struct IPNS.RecordUpdate[]',
567 | name: 'recordUpdates',
568 | type: 'tuple[]'
569 | },
570 | {
571 | internalType: 'uint64[]',
572 | name: 'destroyDomains',
573 | type: 'uint64[]'
574 | }
575 | ],
576 | name: 'updateMultiple',
577 | outputs: [],
578 | stateMutability: 'nonpayable',
579 | type: 'function'
580 | },
581 | {
582 | inputs: [
583 | { internalType: 'uint64', name: 'id', type: 'uint64' },
584 | { internalType: 'bytes', name: 'records', type: 'bytes' }
585 | ],
586 | name: 'updateRecords',
587 | outputs: [],
588 | stateMutability: 'nonpayable',
589 | type: 'function'
590 | },
591 | {
592 | inputs: [
593 | {
594 | components: [
595 | { internalType: 'address', name: 'addr', type: 'address' },
596 | { internalType: 'bool', name: 'allowed', type: 'bool' }
597 | ],
598 | internalType: 'struct IPNS.AllowedAddress[]',
599 | name: 'allowed',
600 | type: 'tuple[]'
601 | },
602 | { internalType: 'bool', name: 'activate', type: 'bool' }
603 | ],
604 | name: 'updateRegistrationWhitelist',
605 | outputs: [],
606 | stateMutability: 'nonpayable',
607 | type: 'function'
608 | }
609 | ]
610 |
--------------------------------------------------------------------------------
/src/assets/bitcoin.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/assets/ethereum.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/src/assets/images/arrow-blue.svg:
--------------------------------------------------------------------------------
1 |
8 |
12 |
--------------------------------------------------------------------------------
/src/assets/images/arrow-white.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/src/assets/images/bg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Upwork-Job32/web3_domain.frontend/db7a9d1d92e26b5359d0c0304405ab2952cc95f8/src/assets/images/bg.jpg
--------------------------------------------------------------------------------
/src/assets/images/bg1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Upwork-Job32/web3_domain.frontend/db7a9d1d92e26b5359d0c0304405ab2952cc95f8/src/assets/images/bg1.jpg
--------------------------------------------------------------------------------
/src/assets/images/bitcoin.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/src/assets/images/check.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/assets/images/check_circle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Upwork-Job32/web3_domain.frontend/db7a9d1d92e26b5359d0c0304405ab2952cc95f8/src/assets/images/check_circle.png
--------------------------------------------------------------------------------
/src/assets/images/circle-1.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/src/assets/images/complete.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Upwork-Job32/web3_domain.frontend/db7a9d1d92e26b5359d0c0304405ab2952cc95f8/src/assets/images/complete.jpg
--------------------------------------------------------------------------------
/src/assets/images/connected.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/assets/images/ethereum.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/assets/images/failed.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Upwork-Job32/web3_domain.frontend/db7a9d1d92e26b5359d0c0304405ab2952cc95f8/src/assets/images/failed.jpg
--------------------------------------------------------------------------------
/src/assets/images/help.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/assets/images/inputBlock1.svg:
--------------------------------------------------------------------------------
1 | image.png
--------------------------------------------------------------------------------
/src/assets/images/inputCheck.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/src/assets/images/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/assets/images/solana.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/src/assets/images/visa.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/assets/solana.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/assets/visa.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/src/components/RegistrationForm.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react'
2 | import { useWeb3Modal } from '@web3modal/wagmi/react'
3 | import { useAccount } from 'wagmi'
4 | import circleCheck from '../assets/images/check_circle.png'
5 | import circle1 from '../assets/images/circle-1.svg'
6 |
7 | type FormData = {
8 | name: string
9 | phone: string
10 | email: string
11 | domainName: string
12 | custody: 'self' | 'hosted'
13 | walletAddress?: string
14 | }
15 |
16 | interface RegistrationFormProps {
17 | onSubmit: (data: FormData) => void
18 | initialData: Omit & { walletAddress?: string }
19 | className?: string
20 | }
21 |
22 | const RegistrationForm: React.FC = ({
23 | onSubmit,
24 | initialData,
25 | className = ''
26 | }) => {
27 | const [formData, setFormData] = useState(initialData)
28 | const { open } = useWeb3Modal()
29 | const { address, isConnected } = useAccount()
30 |
31 | useEffect(() => {
32 | if (isConnected && address) {
33 | setFormData((prevData) => ({
34 | ...prevData,
35 | walletAddress: address
36 | }))
37 | } else {
38 | setFormData((prevData) => ({
39 | ...prevData,
40 | walletAddress: undefined
41 | }))
42 | }
43 | }, [address, isConnected])
44 |
45 | const handleSubmit = (e: React.FormEvent) => {
46 | e.preventDefault()
47 | onSubmit(formData)
48 | }
49 |
50 | return (
51 |
153 | )
154 | }
155 |
156 | export default RegistrationForm
157 |
--------------------------------------------------------------------------------
/src/components/common/Button.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | interface ButtonProps extends React.ButtonHTMLAttributes {
4 | variant?: 'primary' | 'secondary' | 'outline'
5 | size?: 'sm' | 'md' | 'lg'
6 | fullWidth?: boolean
7 | children: React.ReactNode
8 | }
9 |
10 | const Button: React.FC = ({
11 | variant = 'primary',
12 | size = 'md',
13 | fullWidth = false,
14 | children,
15 | className = '',
16 | ...props
17 | }) => {
18 | const baseStyles = 'rounded-full transition-all duration-300 font-normal'
19 |
20 | const variantStyles = {
21 | primary: 'bg-[#0D4AE7] text-white shadow-[0_0_20px_rgba(45,128,255,0.15)]',
22 | secondary:
23 | 'text-white bg-transparent hover:border-[rgba(255,255,255,0.25)] border-2 border-white backdrop-blur-[32px]',
24 | outline: 'border-2 border-white text-white hover:bg-white/10'
25 | }
26 |
27 | const sizeStyles = {
28 | sm: 'text-sm py-2 px-4',
29 | md: 'text-base py-[10px] px-[40px]',
30 | lg: 'text-lg py-3 px-6'
31 | }
32 |
33 | const widthStyle = fullWidth ? 'w-full' : 'w-auto'
34 |
35 | return (
36 |
40 | {children}
41 |
42 | )
43 | }
44 |
45 | export default Button
46 |
--------------------------------------------------------------------------------
/src/components/common/FormInput.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | interface FormInputProps {
4 | value: string
5 | onChange: (value: string) => void
6 | placeholder: string
7 | type?: string
8 | }
9 |
10 | const FormInput: React.FC = ({
11 | value,
12 | onChange,
13 | placeholder,
14 | type = 'text'
15 | }) => {
16 | return (
17 | onChange(e.target.value)}
21 | placeholder={placeholder}
22 | className="w-full bg-white rounded-full px-6 py-4 text-black placeholder:text-gray-400 mb-[10px]"
23 | />
24 | )
25 | }
26 |
27 | export default FormInput
28 |
--------------------------------------------------------------------------------
/src/components/common/LogoHeader.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { useNavigate } from 'react-router-dom'
3 | import logo from '../../assets/images/logo.svg'
4 |
5 | const LogoHeader: React.FC = () => {
6 | const navigate = useNavigate()
7 |
8 | return (
9 |
10 |
navigate('/')}
15 | />
16 |
17 | )
18 | }
19 |
20 | export default LogoHeader
21 |
--------------------------------------------------------------------------------
/src/components/common/SearchInput.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Button from './Button'
3 |
4 | interface SearchInputProps {
5 | value: string
6 | onChange: (value: string) => void
7 | onSubmit: (e: React.FormEvent) => void
8 | placeholder?: string
9 | className?: string
10 | }
11 |
12 | const SearchInput: React.FC = ({
13 | value,
14 | onChange,
15 | onSubmit,
16 | placeholder = 'Search...',
17 | className = ''
18 | }) => {
19 | return (
20 |
23 | onChange(e.target.value)}
27 | placeholder={placeholder}
28 | className="w-full h-full bg-transparent text-black text-sm sm:text-base placeholder:text-white/40 pl-4 sm:pl-6 md:pl-[24px] focus:outline-none rounded-full"
29 | />
30 |
35 | Search
36 |
37 |
38 | )
39 | }
40 |
41 | export default SearchInput
42 |
--------------------------------------------------------------------------------
/src/components/common/index.ts:
--------------------------------------------------------------------------------
1 | export { default as Button } from './Button'
2 | export { default as FormInput } from './FormInput'
3 | export { default as SearchInput } from './SearchInput'
4 | export { default as LogoHeader } from './LogoHeader'
5 |
--------------------------------------------------------------------------------
/src/components/index.ts:
--------------------------------------------------------------------------------
1 | export * from './common'
2 | export * from './layout'
3 |
4 | export { default as RegistrationForm } from './RegistrationForm'
5 |
--------------------------------------------------------------------------------
/src/components/layout/MainLayout.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import bgImage from '../../assets/images/bg.jpg'
3 | import LogoHeader from '../common/LogoHeader'
4 |
5 | interface MainLayoutProps {
6 | children: React.ReactNode
7 | }
8 |
9 | const MainLayout: React.FC = ({ children }) => {
10 | return (
11 |
12 |
16 |
17 |
18 |
19 |
{children}
20 |
21 | )
22 | }
23 |
24 | export default MainLayout
25 |
--------------------------------------------------------------------------------
/src/components/layout/index.ts:
--------------------------------------------------------------------------------
1 | export { default as MainLayout } from './MainLayout'
2 |
--------------------------------------------------------------------------------
/src/config/web3modal.tsx:
--------------------------------------------------------------------------------
1 | import { createWeb3Modal } from '@web3modal/wagmi/react'
2 | import { defaultWagmiConfig } from '@web3modal/base/adapters/evm/wagmi'
3 | import { WagmiProvider } from 'wagmi'
4 | import { base } from 'viem/chains'
5 | import React from 'react'
6 |
7 | const projectId = '5932391dbb0b923a14c367f7c2970edd'
8 |
9 | const metadata = {
10 | name: 'Web3 Domain Registration',
11 | description: 'Register your Web3 Domain',
12 | url: 'https://web3modal.com',
13 | icons: ['https://avatars.githubusercontent.com/u/37784886']
14 | }
15 |
16 | const chains = [base] as const
17 | export const wagmiConfig = defaultWagmiConfig({
18 | chains,
19 | projectId,
20 | metadata,
21 | enableCoinbase: true,
22 | enableWalletConnect: true,
23 | enableInjected: true,
24 | enableEIP6963: true
25 | })
26 |
27 | createWeb3Modal({
28 | wagmiConfig: wagmiConfig,
29 | projectId,
30 | enableAnalytics: true,
31 | defaultChain: base,
32 | featuredWalletIds: ['coinbaseWallet']
33 | })
34 |
35 | interface Web3ModalProviderProps {
36 | children: React.ReactNode
37 | }
38 |
39 | export const Web3ModalProvider: React.FC = ({
40 | children
41 | }) => {
42 | return {children}
43 | }
44 |
--------------------------------------------------------------------------------
/src/domain-register/BillingAndInfo.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react'
2 | import { useLocation } from 'react-router-dom'
3 | import RegistrationForm from '../components/RegistrationForm'
4 | import bgImage from '../assets/images/bg.jpg'
5 | import LogoHeader from '../components/common/LogoHeader'
6 |
7 | type FormData = {
8 | name: string
9 | phone: string
10 | email: string
11 | domainName: string
12 | custody: 'self' | 'hosted'
13 | walletAddress?: string
14 | }
15 |
16 | interface LocationState {
17 | domain: string
18 | custody?: 'self' | 'hosted'
19 | }
20 |
21 | const DomainRegistration: React.FC = () => {
22 | const location = useLocation()
23 | const state = location.state as LocationState | null
24 |
25 | const [formData, setFormData] = useState({
26 | name: '',
27 | phone: '',
28 | email: '',
29 | domainName: state?.domain || '',
30 | custody: state?.custody ?? 'self'
31 | })
32 | console.log(state?.domain)
33 | useEffect(() => {
34 | if (state?.custody) {
35 | setFormData((prev) => ({
36 | ...prev,
37 | custody: state.custody ?? 'self'
38 | }))
39 | }
40 | }, [state])
41 |
42 | const handleFormSubmit = (data: FormData) => {
43 | const { name, email, phone, domainName, custody, walletAddress } = data
44 |
45 | const form = document.createElement('form')
46 | form.method = 'POST'
47 | form.action = 'https://pktpal.com/cart/add'
48 | form.style.display = 'none'
49 |
50 | const appendInput = (name: string, value: string) => {
51 | const input = document.createElement('input')
52 | input.type = 'hidden'
53 | input.name = name
54 | input.value = value
55 | form.appendChild(input)
56 | }
57 |
58 | appendInput('items[0][id]', '50393018106131')
59 | appendInput('items[0][quantity]', '1')
60 | appendInput('items[0][properties][name]', name)
61 | appendInput('items[0][properties][email]', email)
62 | appendInput('items[0][properties][number]', phone)
63 | appendInput('items[0][properties][domain]', domainName)
64 | appendInput('items[0][properties][custody]', custody)
65 |
66 | if (walletAddress) {
67 | appendInput('items[0][properties][walletAddress]', walletAddress)
68 | }
69 |
70 | if (custody === 'hosted') {
71 | appendInput('items[1][id]', '50421796274451')
72 | appendInput('items[1][quantity]', '1')
73 | }
74 |
75 | document.body.appendChild(form)
76 | form.submit()
77 | }
78 |
79 | return (
80 |
81 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 | Fill out the form
93 |
94 |
99 |
100 |
101 |
102 |
103 |
104 |
105 | )
106 | }
107 |
108 | export default DomainRegistration
109 |
--------------------------------------------------------------------------------
/src/domain-register/DomainRegister.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | import { useNavigate } from 'react-router-dom'
3 | import bgImage from '../assets/images/bg.jpg'
4 | import LogoHeader from '../components/common/LogoHeader'
5 |
6 | const setupAnimations = () => {
7 | const style = document.createElement('style')
8 | style.textContent = `
9 | @keyframes fadeIn {
10 | from { opacity: 0; }
11 | to { opacity: 1; }
12 | }
13 |
14 | @keyframes fadeInUp {
15 | from {
16 | opacity: 0;
17 | transform: translateY(20px);
18 | }
19 | to {
20 | opacity: 1;
21 | transform: translateY(0);
22 | }
23 | }
24 |
25 | .animate-fade-in {
26 | opacity: 0;
27 | animation: fadeIn 0.7s ease-out forwards;
28 | }
29 |
30 | .animate-fade-in-up {
31 | opacity: 0;
32 | animation: fadeInUp 0.7s ease-out forwards;
33 | }
34 |
35 | .delay-200 {
36 | animation-delay: 0.2s;
37 | }
38 | `
39 | document.head.appendChild(style)
40 | }
41 |
42 | const DomainRegister: React.FC = () => {
43 | const navigate = useNavigate()
44 | const [searchQuery, setSearchQuery] = useState('')
45 | const [isValidInput, setIsValidInput] = useState(true)
46 |
47 | const validateInput = (input: string) => {
48 | return /^[a-z0-9-]*$/.test(input)
49 | }
50 |
51 | const handleInputChange = (e: React.ChangeEvent) => {
52 | const value = e.target.value
53 | setSearchQuery(value)
54 | if (value.slice(value.length - 4, value.length) == '.pkt') {
55 | console.log(value.slice(value.length - 4, value.length - 1))
56 | setIsValidInput(validateInput(value.slice(0, value.length - 4)))
57 | } else {
58 | setIsValidInput(validateInput(value))
59 | }
60 | }
61 |
62 | const handleSearch = (e: React.FormEvent) => {
63 | e.preventDefault()
64 | if (searchQuery.length > 0 && isValidInput) {
65 | navigate('/search', {
66 | state: {
67 | query: searchQuery,
68 | fromDomainRegister: true
69 | }
70 | })
71 | }
72 | }
73 |
74 | return (
75 |
76 |
80 |
81 |
82 |
83 |
84 |
85 |
86 | Claim your
87 |
88 | PKT Domain
89 |
90 |
91 |
92 |
93 | Domain Registrar
94 |
95 | navigate('/web3-register')}
97 | className="rounded-full text-sm sm:text-base transition-all duration-300 text-white sm:w-[200px] w-[100%] h-[45px] bg-transparent hover:border-[rgba(255,255,255,0.25)] border-2 border-white backdrop-blur-[32px]"
98 | >
99 | Web3 Registrar
100 |
101 |
102 |
103 |
122 | {!isValidInput && (
123 |
124 | Only lowercase letters (a-z), numbers (0-9) and hyphens (-) are
125 | allowed
126 |
127 | )}
128 |
129 |
130 |
131 | )
132 | }
133 |
134 | setupAnimations()
135 |
136 | export default DomainRegister
137 |
--------------------------------------------------------------------------------
/src/domain-register/DomainSearchResults.tsx:
--------------------------------------------------------------------------------
1 | import React, { useCallback, useEffect, useState } from 'react'
2 | import { useNavigate, useLocation } from 'react-router-dom'
3 | import { ethers } from 'ethers'
4 | import bgImage from '../assets/images/bg.jpg'
5 | import circleCheck from '../assets/images/check_circle.png'
6 | import circleBlock from '../assets/images/inputBlock.svg'
7 | import whiteArrow from '../assets/images/arrow-white.svg'
8 | import LogoHeader from '../components/common/LogoHeader'
9 |
10 | interface DomainData {
11 | min_value: number
12 | name: string
13 | is_available: boolean
14 | service_charge: string
15 | is_valid: boolean
16 | }
17 |
18 | const DomainSearchResults: React.FC = () => {
19 | const navigate = useNavigate()
20 | const location = useLocation()
21 |
22 | const [custodyType, setCustodyType] = useState<'self' | 'hosted'>('self')
23 | const [loading, setLoading] = useState(false)
24 | const [isValidInput, setIsValidInput] = useState(true)
25 |
26 | const [searchQuery, setSearchQuery] = useState(location.state?.query)
27 | const [domain, setDomain] = useState('')
28 | const [data, setData] = useState({
29 | min_value: 0,
30 | name: '',
31 | is_valid: false,
32 | is_available: false,
33 | service_charge: ''
34 | })
35 |
36 | const validateInput = (input: string) => {
37 | return /^[a-z0-9-]*$/.test(input)
38 | }
39 |
40 | const handleInputChange = (e: React.ChangeEvent) => {
41 | const value = e.target.value
42 | setSearchQuery(value)
43 | if (value.slice(value.length - 4, value.length) == '.pkt') {
44 | setIsValidInput(validateInput(value.slice(0, value.length - 4)))
45 | } else {
46 | setIsValidInput(validateInput(value))
47 | }
48 | }
49 |
50 | const fetchMinValue = useCallback(async () => {
51 | if (!isValidInput || !searchQuery) return
52 | if (
53 | searchQuery.slice(searchQuery.length - 4, searchQuery.length) == '.pkt'
54 | ) {
55 | setDomain(searchQuery.slice(0, searchQuery.length - 4))
56 | } else {
57 | setDomain(searchQuery)
58 | }
59 |
60 | setLoading(true)
61 | console.log(domain)
62 | if (!domain) return
63 | try {
64 | const response = await fetch(
65 | `https://app.pkt.cash/api/v1/pns/domain-available/${domain}`
66 | )
67 | const data = await response.json()
68 |
69 | if (data.min_value) {
70 | const minValue = (await ethers.formatEther(
71 | BigInt(data.min_value)
72 | )) as string
73 |
74 | setData(() => ({
75 | ...data,
76 | min_value: Math.floor(Number(minValue))
77 | }))
78 | }
79 | } catch (error) {
80 | console.error('Error fetching minimum value:', error)
81 | } finally {
82 | setLoading(false)
83 | }
84 | }, [isValidInput, searchQuery, domain, setData, setDomain])
85 |
86 | useEffect(() => {
87 | fetchMinValue()
88 | }, [fetchMinValue])
89 |
90 | const handleSearch = async (e: React.FormEvent) => {
91 | e.preventDefault()
92 | if (isValidInput && searchQuery) {
93 | await fetchMinValue()
94 | }
95 | }
96 |
97 | const handleRegister = (domain: string) => {
98 | console.log(domain)
99 | navigate('/register-domain', {
100 | state: {
101 | domain,
102 | custody: custodyType
103 | }
104 | })
105 | }
106 |
107 | return (
108 |
109 |
113 |
114 |
115 |
116 |
117 |
118 | setCustodyType('self')}
120 | className={`sm:w-[200px] sm:h-[36px] w-[47%] h-[45px] rounded-full text-[16px] sm:mr-[20px] bg-gradient-to-r from-[#3CADEF] via-[#3CADEF] to-[#339DDB] text-white ${
121 | custodyType === 'self' ? ' opacity-[100%] ' : ' opacity-[70%]'
122 | }`}
123 | >
124 | Custody
125 |
126 | setCustodyType('hosted')}
128 | className={`sm:w-[200px] sm:h-[36px] w-[47%] h-[45px] rounded-full text-[16px] sm:mr-[20px] bg-gradient-to-r from-[#3CADEF] via-[#3CADEF] to-[#339DDB] text-white ${
129 | custodyType === 'hosted' ? ' opacity-[100%] ' : ' opacity-[70%]'
130 | }`}
131 | >
132 | Hosted
133 |
134 |
135 |
136 |
137 | {
144 | if (e.key === 'Enter' && isValidInput && searchQuery) {
145 | handleSearch(e)
146 | }
147 | }}
148 | />
149 |
154 | Search
155 |
156 |
157 | {!isValidInput && (
158 |
159 | Only lowercase letters (a-z) and numbers (0-9) are allowed
160 |
161 | )}
162 | {loading === false ? (
163 |
164 |
171 |
172 |
181 | {data.is_valid ? (
182 |
183 | {`${domain}.pkt`}
184 |
185 | ) : (
186 |
187 | {`${domain}.pkt`}
188 |
189 | )}
190 |
191 | {data.is_available && data.is_valid ? (
192 |
193 |
handleRegister(`${domain}.pkt`)}
195 | className="relative flex items-center justify-center text-white rounded-full bg-[#0D4AE7] font-normal w-[120px] sm:w-[160px] h-[56px]"
196 | >
197 | Register
198 |
203 |
204 |
205 | ) : (
206 |
207 |
208 | Not Available
209 |
210 |
211 | )}
212 |
213 |
214 | ) : (
215 |
218 | )}
219 |
220 |
221 | )
222 | }
223 |
224 | export default DomainSearchResults
225 |
--------------------------------------------------------------------------------
/src/domain-register/index.tsx:
--------------------------------------------------------------------------------
1 | export { default as DomainRegister } from './DomainRegister'
2 | export { default as BillingAndInfo } from './BillingAndInfo'
3 | export { default as DomainSearchResults } from './DomainSearchResults'
4 | export { default as LogoHeader } from '../components/common/LogoHeader'
5 | export { default as RegistrationForm } from '../components/RegistrationForm'
6 |
--------------------------------------------------------------------------------
/src/favicon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 |
6 |
7 | @layer base {
8 | body {
9 | @apply bg-[#000B1E] text-white ;
10 | font-family: 'Inter';
11 | }
12 | }
13 |
14 | @layer utilities {
15 | .animate-fall {
16 | animation: fall 20s linear infinite;
17 | }
18 |
19 | .animate-spin-slow {
20 | animation: spin 3s linear infinite;
21 | }
22 | .custom-scrollbar::-webkit-scrollbar {
23 | width: 20px;
24 | }
25 |
26 | .custom-scrollbar::-webkit-scrollbar-track {
27 | box-shadow: inset 0 0 5px grey;
28 | border-radius: 10px;
29 | }
30 |
31 | .custom-scrollbar::-webkit-scrollbar-thumb {
32 | background: red;
33 | border-radius: 10px;
34 | }
35 | }
36 |
37 | @keyframes fall {
38 | from {
39 | transform: translateY(-100%);
40 | }
41 | to {
42 | transform: translateY(100%);
43 | }
44 | }
45 |
46 | @keyframes spin {
47 | from {
48 | transform: rotate(0deg);
49 | }
50 | to {
51 | transform: rotate(360deg);
52 | }
53 | }
54 |
55 | /* Gradient Backgrounds */
56 | .bg-gradient-radial {
57 | background: radial-gradient(circle at center, var(--tw-gradient-from) 0%, var(--tw-gradient-via) 50%, var(--tw-gradient-to) 100%);
58 | }
59 |
60 | .bg-gradient-blue {
61 | background: linear-gradient(103deg, #000309 20.67%, rgba(1,43,90,0.90) 47.01%, rgba(1,148,254,0.53) 66.96%, rgba(4,34,107,0.24) 76.55%);
62 | }
63 |
64 | /* Custom Scrollbar */
65 | ::-webkit-scrollbar {
66 | width: 8px;
67 | }
68 |
69 | ::-webkit-scrollbar-track {
70 | @apply bg-white/5;
71 | }
72 |
73 | ::-webkit-scrollbar-thumb {
74 | @apply bg-white/20 rounded-full hover:bg-white/30 transition-colors;
75 | }
76 |
77 | ::-webkit-scrollbar-thumb:hover {
78 | @apply bg-white/30;
79 | }
80 |
81 |
82 |
83 |
--------------------------------------------------------------------------------
/src/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { createRoot } from 'react-dom/client'
3 | import { BrowserRouter, Route, Routes } from 'react-router-dom'
4 |
5 | import './index.css'
6 |
7 | import { Providers } from './providers'
8 | import { Web3ModalProvider } from './config/web3modal'
9 |
10 | import {
11 | DomainRegister,
12 | BillingAndInfo,
13 | DomainSearchResults
14 | } from './domain-register'
15 |
16 | import {
17 | Web3Register,
18 | RegistrationComplete,
19 | RegistrationFailed
20 | } from './web3-register'
21 |
22 | createRoot(document.getElementById('root') as HTMLElement).render(
23 |
24 |
25 |
26 |
27 |
28 | } />
29 | } />
30 | } />
31 |
32 | } />
33 | }
36 | />
37 | }
40 | />
41 |
42 |
43 |
44 |
45 |
46 | )
47 |
--------------------------------------------------------------------------------
/src/providers/WalletHistoryProvider.tsx:
--------------------------------------------------------------------------------
1 | import { createContext, useState, ReactNode } from 'react'
2 | import { WalletHistoryType, WalletHistoryContextType } from '../types/wallet'
3 |
4 | /**
5 | * Type definition for a transaction history entry
6 | */
7 | export type TransactionHistoryItem = {
8 | step: number
9 | status: string
10 | txId?: string
11 | stakeAmount?: string
12 | timestamp: number
13 | domainName?: string
14 | currentPrice?: string
15 | maxPkt?: string
16 | stakeId?: string
17 | }
18 |
19 | const defaultContext: WalletHistoryContextType = {
20 | walletHistory: [],
21 | setWalletHistory: () => {}
22 | }
23 |
24 | export const WalletErrorContext =
25 | createContext(defaultContext)
26 |
27 | export const WalletHistoryProvider = ({
28 | children
29 | }: {
30 | children: ReactNode
31 | }) => {
32 | const [walletHistory, setWalletHistory] = useState([])
33 |
34 | return (
35 |
36 | {children}
37 |
38 | )
39 | }
40 |
--------------------------------------------------------------------------------
/src/providers/index.tsx:
--------------------------------------------------------------------------------
1 | import { ReactNode } from 'react'
2 | import {
3 | WalletHistoryProvider,
4 | WalletErrorContext
5 | } from './WalletHistoryProvider'
6 |
7 | interface ProvidersProps {
8 | children: ReactNode
9 | }
10 |
11 | export { WalletHistoryProvider, WalletErrorContext }
12 |
13 | export type {
14 | WalletHistoryType,
15 | TransactionHistoryItem,
16 | WalletHistoryContextType
17 | } from '../types/wallet'
18 |
19 | export const Providers = ({ children }: ProvidersProps) => {
20 | return {children}
21 | }
22 |
--------------------------------------------------------------------------------
/src/services/wallet-service.ts:
--------------------------------------------------------------------------------
1 | import { ethers } from 'ethers'
2 | import { WalletState } from '../types/wallet'
3 |
4 | export const delay = (ms: number): Promise =>
5 | new Promise((resolve) => setTimeout(resolve, ms))
6 |
7 | export const stringToHex = (str: string): string => {
8 | return (
9 | '0x' +
10 | Array.from(str)
11 | .map((c) => c.charCodeAt(0).toString(16).padStart(2, '0'))
12 | .join('')
13 | )
14 | }
15 |
16 | export const getStakeLabel = (status: WalletState['status']): string => {
17 | return status === 'pre_allocating' ? 'Got stake:' : 'Actual stake amount:'
18 | }
19 |
20 | export const CONSTANTS = {
21 | PKT_BASE: '0x917f39bb33b2483dd19546b1e8d2f09ce481ee44',
22 | LOCKBOX: '0x14d15765c66e8f0c7f8757d1d19137b714dfcc60',
23 | PNS: '0xDc8eb1D1052a2078B33dd188201eAf3F080E0258',
24 | MULTIPAY: '0x3e11E1F68F736209662d5695148986585ca2c971',
25 | BASE_PRICE: 1000n * 10n ** 18n, // 1000 PKT in wei
26 | PRICE_HALVING_INTERVAL: 60 * 60 * 1000 // 1 hour in milliseconds
27 | }
28 |
29 | export const isWalletAvailable = (): boolean => {
30 | return typeof window.ethereum !== 'undefined'
31 | }
32 |
33 | export const connectWallet =
34 | async (): Promise => {
35 | if (!isWalletAvailable()) {
36 | return null
37 | }
38 |
39 | try {
40 | await window.ethereum!.request({ method: 'eth_requestAccounts' })
41 |
42 | try {
43 | await window.ethereum!.request({
44 | method: 'wallet_switchEthereumChain',
45 | params: [{ chainId: '0x2105' }]
46 | })
47 | } catch (switchError: any) {
48 | if (switchError.code === 4902) {
49 | try {
50 | await window.ethereum!.request({
51 | method: 'wallet_addEthereumChain',
52 | params: [
53 | {
54 | chainId: '0x2105',
55 | chainName: 'Base',
56 | nativeCurrency: {
57 | name: 'ETH',
58 | symbol: 'ETH',
59 | decimals: 18
60 | },
61 | rpcUrls: ['https://mainnet.base.org'],
62 | blockExplorerUrls: ['https://basescan.org']
63 | }
64 | ]
65 | })
66 | } catch (addError) {
67 | console.error('Failed to add Base network:', addError)
68 | return null
69 | }
70 | } else {
71 | console.error('Failed to switch to Base network:', switchError)
72 | return null
73 | }
74 | }
75 |
76 | return new ethers.BrowserProvider(window.ethereum!)
77 | } catch (error) {
78 | console.error('Error connecting to wallet:', error)
79 | return null
80 | }
81 | }
82 |
83 | export const calculatePrice = (
84 | domainName: string,
85 | purchaseHistory: { domain: string; timestamp: number }[]
86 | ): bigint => {
87 | const now = Date.now()
88 |
89 | let price = CONSTANTS.BASE_PRICE
90 | const recentPurchases = purchaseHistory.filter(
91 | (purchase) => purchase.domain === domainName
92 | )
93 |
94 | for (let i = 0; i < recentPurchases.length; i++) {
95 | price *= 2n
96 | }
97 |
98 | if (recentPurchases.length > 0) {
99 | const lastPurchase = recentPurchases[recentPurchases.length - 1]
100 | const hoursSinceLastPurchase = Math.floor(
101 | (now - lastPurchase.timestamp) / CONSTANTS.PRICE_HALVING_INTERVAL
102 | )
103 |
104 | for (
105 | let i = 0;
106 | i < hoursSinceLastPurchase && price > CONSTANTS.BASE_PRICE;
107 | i++
108 | ) {
109 | price /= 2n
110 | }
111 | }
112 |
113 | return price
114 | }
115 |
116 | export const fetchMinValue = async (
117 | address: string,
118 | domain?: string
119 | ): Promise => {
120 | try {
121 | if (domain) {
122 | const response = await fetch(
123 | `https://app.pkt.cash/api/v1/pns/domain-available/${domain}`
124 | )
125 | const data = await response.json()
126 | if (data.min_value) {
127 | return BigInt(data.min_value)
128 | }
129 | }
130 |
131 | const response = await fetch(
132 | `https://app.pkt.cash/api/v1/pns/qualifying-lockups/${address}`
133 | )
134 | const data = await response.json()
135 | if (data.min_value) {
136 | return BigInt(data.min_value)
137 | }
138 | } catch (error) {
139 | console.error('Error fetching minimum value:', error)
140 | }
141 | return CONSTANTS.BASE_PRICE
142 | }
143 |
144 | export const checkDomainAvailability = async (
145 | domainName: string
146 | ): Promise<{
147 | isAvailable: boolean
148 | isValid: boolean
149 | minValue?: bigint
150 | errorMessage?: string
151 | }> => {
152 | try {
153 | const response = await fetch(
154 | `https://app.pkt.cash/api/v1/pns/domain-available/${domainName}`
155 | )
156 | const data = await response.json()
157 |
158 | let errorMessage: string | undefined
159 |
160 | if (!data.is_available) {
161 | errorMessage = `Domain ${domainName}.pkt is already registered`
162 | } else if (!data.is_valid) {
163 | errorMessage = `Domain ${domainName}.pkt is not valid (can only be letters and numbers split up by dashes)`
164 | }
165 |
166 | return {
167 | isAvailable: data.is_available,
168 | isValid: data.is_valid,
169 | minValue: data.min_value ? BigInt(data.min_value) : undefined,
170 | errorMessage
171 | }
172 | } catch (error) {
173 | console.error('Error checking domain availability:', error)
174 | return {
175 | isAvailable: false,
176 | isValid: false,
177 | errorMessage: 'Error checking domain availability'
178 | }
179 | }
180 | }
181 |
--------------------------------------------------------------------------------
/src/types/index.ts:
--------------------------------------------------------------------------------
1 | export interface LocationState {
2 | custody?: 'self' | 'hosted'
3 | query?: string
4 | fromDomainRegister?: boolean
5 | }
6 |
7 | export type RegistrarType = 'domain' | 'web3'
8 | export type PaymentMethodType = 'crypto' | 'credit'
9 |
10 | export interface RegistrationFormData {
11 | domainName: string
12 | registrar: RegistrarType
13 | paymentMethod: PaymentMethodType
14 | custody: 'self' | 'hosted'
15 | }
16 |
--------------------------------------------------------------------------------
/src/types/wallet.ts:
--------------------------------------------------------------------------------
1 | import { ethers } from 'ethers'
2 |
3 | /**
4 | * Type definition for a transaction history entry
5 | */
6 | export type TransactionHistoryItem = {
7 | step: number
8 | status: string
9 | txId?: string
10 | stakeAmount?: string
11 | timestamp: number
12 | domainName?: string
13 | currentPrice?: string
14 | maxPkt?: string
15 | stakeId?: string
16 | }
17 |
18 |
19 | export type WalletHistoryType = TransactionHistoryItem[]
20 |
21 | /**
22 | * Type for wallet state in the registration process
23 | */
24 | export interface WalletState {
25 | status:
26 | | 'loading'
27 | | 'connected'
28 | | 'pre_allocating'
29 | | 'authorizing'
30 | | 'staking'
31 | | 'reserved'
32 | | 'not_found'
33 | | 'price_changed'
34 | | 'low_funds'
35 | | 'success'
36 | | 'failed'
37 | step?: number
38 | txId?: string
39 | stakeAmount?: string
40 | address?: string
41 | label?: string
42 | domainId?: string
43 | stakeId?: string
44 | provider?: ethers.BrowserProvider
45 | signer?: ethers.JsonRpcSigner
46 | erc20abi?: Array
47 | lockboxabi?: Array
48 | multipayabi?: Array
49 | pnsabi?: Array
50 | transactionHistory?: WalletHistoryType
51 | }
52 |
53 | /**
54 | * Type definition for the wallet error context
55 | */
56 | export interface WalletHistoryContextType {
57 | walletHistory: WalletHistoryType
58 | setWalletHistory: (walletHistory: WalletHistoryType) => void
59 | }
60 |
--------------------------------------------------------------------------------
/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/src/web3-register/RegistrationComplete.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { useLocation } from 'react-router-dom'
3 | import bg from '../assets/images/complete.jpg'
4 | const RegistrationComplete: React.FC = () => {
5 | const location = useLocation()
6 | const domainId = location.state?.domainId || '299'
7 |
8 | return (
9 |
10 |
14 |
15 |
16 |
17 | Registration complete
18 |
19 |
20 |
21 | Your Domain ID {domainId} is now available in your PKT dashboard
22 |
23 |
24 |
29 | View Dashboard
30 |
31 |
32 |
33 | )
34 | }
35 |
36 | export default RegistrationComplete
37 |
--------------------------------------------------------------------------------
/src/web3-register/RegistrationFailed.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext, useEffect, useState } from 'react'
2 | import { useNavigate, useLocation } from 'react-router-dom'
3 | import bg from '../assets/images/failed.jpg'
4 | import help from '../assets/images/help.svg'
5 | import MainLayout from '../components/layout/MainLayout'
6 | import { WalletErrorContext } from '../providers'
7 | import { WalletHistoryType, TransactionHistoryItem } from '../types/wallet'
8 | import { WalletHistoryContextType } from '../types/wallet'
9 |
10 | interface LocationState {
11 | txId?: string
12 | stakeAmount?: string
13 | step?: number
14 | history?: TransactionHistoryItem[]
15 | }
16 |
17 | const RegistrationFailed: React.FC = () => {
18 | const navigate = useNavigate()
19 | const location = useLocation()
20 | const state = location.state as LocationState
21 | // const txId = state?.txId
22 | // const stakeAmount = state?.stakeAmount
23 | const step = state?.step || 4 // Default to step 4 if not provided
24 | const stateHistory = state?.history || []
25 | const { walletHistory } = useContext(
26 | WalletErrorContext
27 | ) as WalletHistoryContextType
28 |
29 | // Combine both sources of history, prioritizing state history as it's more recent
30 | const [combinedHistory, setCombinedHistory] = useState([])
31 |
32 | useEffect(() => {
33 | const mergedHistory = [...stateHistory]
34 |
35 | if (walletHistory && walletHistory.length > 0) {
36 | walletHistory.forEach((item) => {
37 | const isDuplicate = mergedHistory.some(
38 | (existingItem) =>
39 | existingItem.step === item.step &&
40 | existingItem.timestamp === item.timestamp
41 | )
42 | if (!isDuplicate) {
43 | mergedHistory.push(item)
44 | }
45 | })
46 | }
47 |
48 | mergedHistory.sort((a, b) => a.step - b.step)
49 |
50 | setCombinedHistory(mergedHistory)
51 |
52 | console.log('State History:', stateHistory)
53 | console.log('Context History:', walletHistory)
54 | console.log('Combined History:', mergedHistory)
55 | }, [stateHistory, walletHistory])
56 |
57 | const renderHistory = () => {
58 | if (combinedHistory.length === 0) return null
59 |
60 | return (
61 |
62 | {combinedHistory
63 | .filter((item) => item.step <= step)
64 | .map((item, index) => (
65 |
66 |
67 | Step {item.step}: {item.status}
68 |
69 | {item.txId && (
70 | <>
71 |
- TXID:
72 |
73 | {item.txId}
74 |
75 | >
76 | )}
77 | {item.stakeId && (
78 | <>
79 |
80 | - Stake ID:
81 |
82 |
83 | {item.stakeId}
84 |
85 | >
86 | )}
87 | {item.domainName && (
88 | <>
89 |
90 | - Domain:
91 |
92 |
93 | {item.domainName}
94 |
95 | >
96 | )}
97 | {item.currentPrice && (
98 | <>
99 |
100 | - Current Price:
101 |
102 |
103 | {item.currentPrice} PKT
104 |
105 | >
106 | )}
107 | {item.maxPkt && (
108 | <>
109 |
110 | - Max PKT:
111 |
112 |
113 | {item.maxPkt} PKT
114 |
115 | >
116 | )}
117 |
118 | ))}
119 |
120 | )
121 | }
122 |
123 | return (
124 |
125 |
126 |
127 |
131 |
132 |
133 |
134 | Registration failed
135 |
136 |
137 | Please make sure you have PKT and ETH on Base in your Web3
138 | Wallet
139 |
140 |
141 |
142 | {renderHistory()}
143 |
144 |
145 |
146 |
navigate('/web3-register')}
148 | className="rounded-full text-sm sm:text-base transition-all duration-300 text-white sm:w-[200px] w-[100%] h-[45px] bg-[#0D4AE7] shadow-[0_0_20px_rgba(45,128,255,0.15)]"
149 | >
150 | Try again
151 |
152 |
window.open('https://docs.pkt.cash', '_blank')}
154 | className="rounded-full flex items-center justify-center text-sm sm:text-base transition-all duration-300 text-white sm:w-[200px] w-[100%] h-[45px] bg-transparent border-2 border-white"
155 | >
156 | Get help
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 | )
166 | }
167 |
168 | export default RegistrationFailed
169 |
--------------------------------------------------------------------------------
/src/web3-register/Web3Register.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect, useRef, useContext } from 'react'
2 | import { useNavigate } from 'react-router-dom'
3 |
4 | import bgImage from '../assets/images/bg.jpg'
5 |
6 | import MainLayout from '../components/layout/MainLayout'
7 | import { WalletErrorContext } from '../providers'
8 | import { WalletState, WalletHistoryContextType } from '../types/wallet'
9 | import {
10 | fetchMinValue,
11 | checkDomainAvailability,
12 | calculatePrice
13 | } from '../services/wallet-service'
14 |
15 | import {
16 | WalletStatus,
17 | RegistrationForm,
18 | TransactionHistory,
19 | WalletConnector,
20 | registerDomain
21 | } from './components'
22 |
23 | import LogoHeader from '../components/common/LogoHeader'
24 |
25 | const BASE_PRICE = 1000n * 10n ** 18n
26 |
27 | interface PurchaseHistory {
28 | domain: string
29 | timestamp: number
30 | }
31 |
32 | declare global {
33 | interface Window {
34 | ethereum?: any
35 | }
36 | }
37 |
38 | const Web3Register: React.FC = () => {
39 | const navigate = useNavigate()
40 | const { setWalletHistory } = useContext(
41 | WalletErrorContext
42 | ) as WalletHistoryContextType
43 | const scroll = useRef(null)
44 | const [walletState, setWalletState] = useState({
45 | status: 'loading',
46 | transactionHistory: []
47 | })
48 | const [domainName, setDomainName] = useState('')
49 | const [stakeAmount, setStakeAmount] = useState('')
50 |
51 | const [purchaseHistory] = useState([])
52 | const [currentPrice, setCurrentPrice] = useState(BASE_PRICE)
53 | const [isAvailableRegister, setIsAvailableRegister] = useState(false)
54 | const [isBlockInput, setIsBlockInput] = useState(false)
55 |
56 | const simulateWalletFlow = async () => {
57 | console.log('Simulating wallet flow')
58 | }
59 |
60 | const handleDomainChange = async (value: string) => {
61 | setDomainName(value)
62 | setIsAvailableRegister(true)
63 |
64 | if (value) {
65 | const result = await checkDomainAvailability(value)
66 | setIsBlockInput(!result.isAvailable || !result.isValid)
67 | setIsAvailableRegister(result.isAvailable && result.isValid)
68 |
69 | if (result.minValue) {
70 | setCurrentPrice(result.minValue)
71 | }
72 | }
73 | }
74 |
75 | const handleStakeAmountChange = (value: string) => {
76 | setStakeAmount(value)
77 | }
78 |
79 | const handleSubmit = async (e: React.FormEvent) => {
80 | e.preventDefault()
81 | await registerDomain(
82 | walletState,
83 | setWalletState,
84 | setWalletHistory,
85 | setIsAvailableRegister,
86 | navigate,
87 | domainName,
88 | stakeAmount,
89 | currentPrice
90 | )
91 | }
92 |
93 | const renderStepHistory = (currentStep: number) => {
94 | return (
95 |
99 | )
100 | }
101 |
102 | // Update price when wallet is connected or domain name changes
103 | useEffect(() => {
104 | const address = walletState.address
105 | if (address && domainName) {
106 | fetchMinValue(address, domainName).then((price) => setCurrentPrice(price))
107 | } else if (address) {
108 | fetchMinValue(address).then((price) => setCurrentPrice(price))
109 | }
110 | }, [walletState.address, domainName])
111 |
112 | // Update price every 30 seconds
113 | useEffect(() => {
114 | const address = walletState.address
115 | if (address) {
116 | const interval = setInterval(() => {
117 | if (domainName) {
118 | fetchMinValue(address, domainName).then((price) =>
119 | setCurrentPrice(price)
120 | )
121 | } else {
122 | fetchMinValue(address).then((price) => setCurrentPrice(price))
123 | }
124 | }, 30000) // Check every 30 seconds
125 |
126 | return () => clearInterval(interval)
127 | }
128 | }, [walletState.address, domainName])
129 |
130 | useEffect(() => {
131 | const interval = setInterval(() => {
132 | if (domainName) {
133 | setCurrentPrice(calculatePrice(domainName, purchaseHistory))
134 | }
135 | }, 60000)
136 |
137 | return () => clearInterval(interval)
138 | }, [domainName, purchaseHistory])
139 |
140 | useEffect(() => {
141 | if (domainName) {
142 | setCurrentPrice(calculatePrice(domainName, purchaseHistory))
143 | }
144 | }, [domainName])
145 |
146 | return (
147 |
148 |
153 |
154 |
155 |
159 |
160 |
161 |
162 |
163 |
164 | Claim your
165 |
166 | PKT Domain
167 |
168 |
169 |
170 | This is a tool for claiming a PKT domain without first making a
171 | stake. It does the staking in the same transaction while registering
172 | the domain.
173 |
174 |
175 |
176 | {
178 | navigate('/')
179 | }}
180 | className="rounded-full text-sm sm:text-base transition-all duration-300 text-white sm:w-[200px] w-[100%] h-[45px] bg-transparent hover:border-[rgba(255,255,255,0.25)] border-2 border-white backdrop-blur-[32px]"
181 | >
182 | Domain Registrar
183 |
184 |
185 | Web3 Registrar
186 |
187 |
188 |
189 |
218 |
219 |
220 |
221 | )
222 | }
223 |
224 | export default Web3Register
225 |
--------------------------------------------------------------------------------
/src/web3-register/components/RegistrationForm.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { ethers } from 'ethers'
3 | import FormInput from '../../components/common/FormInput'
4 | import inputBlock from '../../assets/images/inputBlock.svg'
5 | import inputCheck from '../../assets/images/inputCheck.svg'
6 |
7 | interface RegistrationFormProps {
8 | domainName: string
9 | stakeAmount: string
10 | currentPrice: bigint
11 | isAvailableRegister: boolean
12 | isBlockInput: boolean
13 | onDomainChange: (value: string) => void
14 | onStakeAmountChange: (value: string) => void
15 | onSubmit: (e: React.FormEvent) => void
16 | scrollRef: React.RefObject
17 | }
18 |
19 | const RegistrationForm: React.FC = ({
20 | domainName,
21 | stakeAmount,
22 | currentPrice,
23 | isAvailableRegister,
24 | isBlockInput,
25 | onDomainChange,
26 | onStakeAmountChange,
27 | onSubmit,
28 | scrollRef
29 | }) => {
30 | return (
31 |
76 | )
77 | }
78 |
79 | export default RegistrationForm
80 |
--------------------------------------------------------------------------------
/src/web3-register/components/RegistrationService.ts:
--------------------------------------------------------------------------------
1 | import { ethers } from 'ethers'
2 | import { WalletState, TransactionHistoryItem } from '../../types/wallet'
3 | import {
4 | stringToHex,
5 | fetchMinValue,
6 | CONSTANTS,
7 | delay
8 | } from '../../services/wallet-service'
9 |
10 | /**
11 | * Helper function to update transaction history in the wallet state
12 | */
13 | export const updateTransactionHistory = (
14 | walletState: WalletState,
15 | setWalletState: React.Dispatch>,
16 | setWalletHistory: (history: TransactionHistoryItem[]) => void,
17 | currentStep: number,
18 | status: WalletState['status'],
19 | values: {
20 | stakeAmount?: string
21 | domainName?: string
22 | currentPrice?: string
23 | maxPkt?: string
24 | stakeId?: string
25 | txId?: string
26 | }
27 | ) => {
28 | setWalletState((prev) => {
29 | console.log(walletState)
30 | const newHistory = [...(prev.transactionHistory || [])]
31 | // Remove any existing entry for this step
32 | const stepIndex = newHistory.findIndex((step) => step.step === currentStep)
33 | if (stepIndex >= 0) {
34 | newHistory.splice(stepIndex, 1)
35 | }
36 | // Add new entry
37 | newHistory.push({
38 | step: currentStep,
39 | status,
40 | ...values,
41 | timestamp: Date.now()
42 | })
43 |
44 | // Update the context with the new history
45 | setWalletHistory(newHistory)
46 | console.log(`Updating wallet history for step ${currentStep}:`, newHistory)
47 |
48 | return {
49 | ...prev,
50 | status,
51 | step: currentStep,
52 | ...values,
53 | transactionHistory: newHistory
54 | }
55 | })
56 | }
57 |
58 | /**
59 | * Helper function to handle transaction errors
60 | */
61 | export const handleTransactionError = async (
62 | error: any,
63 | step: number,
64 | walletState: WalletState,
65 | setIsAvailableRegister: React.Dispatch>,
66 | setWalletHistory: (history: TransactionHistoryItem[]) => void,
67 | navigate: (path: string, options?: any) => void
68 | ) => {
69 | console.error('Transaction error:', error)
70 | setIsAvailableRegister(true)
71 |
72 | // Store the history in a local variable to ensure we have it for navigation
73 | const historyToSave = walletState.transactionHistory || []
74 |
75 | // Update the context
76 | if (historyToSave.length > 0) {
77 | setWalletHistory(historyToSave)
78 | console.log('Setting wallet history in context:', historyToSave)
79 | }
80 |
81 | await delay(500) // Shorter delay to give context time to update
82 |
83 | // Navigate with history as state parameter for redundancy
84 | navigate('/web3-register/failed', {
85 | state: {
86 | history: historyToSave,
87 | step: walletState.step || step
88 | }
89 | })
90 | }
91 |
92 | /**
93 | * Execute the domain registration process
94 | */
95 | export const registerDomain = async (
96 | walletState: WalletState,
97 | setWalletState: React.Dispatch>,
98 | setWalletHistory: (history: TransactionHistoryItem[]) => void,
99 | setIsAvailableRegister: React.Dispatch>,
100 | navigate: (path: string, options?: any) => void,
101 | domainName: string,
102 | stakeAmount: string,
103 | currentPrice: bigint
104 | ) => {
105 | const { PKT_BASE, LOCKBOX, PNS, MULTIPAY } = CONSTANTS
106 |
107 | setIsAvailableRegister(false)
108 | let maxPkt: bigint
109 | let myStakeId = 0
110 |
111 | try {
112 | if (stakeAmount === '') {
113 | maxPkt = 50000n * 10n ** 18n
114 | } else {
115 | maxPkt = ethers.parseEther(stakeAmount)
116 | }
117 | } catch (e) {
118 | const err = `The maximum PKT to stake must be a valid integer`
119 | console.log(err)
120 | alert(err)
121 | return
122 | }
123 |
124 | if (!/^[a-z0-9-]+$/.test(domainName)) {
125 | const err = `Domains can only have the letters, numbers and dashes`
126 | console.log(err)
127 | alert(err)
128 | return
129 | }
130 |
131 | try {
132 | // Get current minimum value from API
133 | const minValue = await fetchMinValue(walletState.address!, domainName)
134 |
135 | // Check if stake amount meets minimum value
136 | if (maxPkt < minValue) {
137 | const err = `The maximum PKT to stake is less than required amount: ${ethers.formatEther(
138 | minValue
139 | )}`
140 | console.log(err)
141 | setWalletState((prev) => ({ ...prev, status: 'price_changed' }))
142 | return
143 | }
144 |
145 | const availableRes = await fetch(
146 | `https://app.pkt.cash/api/v1/pns/domain-available/${domainName}`
147 | )
148 | const available = await availableRes.json()
149 | console.log(available, domainName, 'available')
150 |
151 | if (!available.is_available) {
152 | const err = `Domain ${domainName}.pkt is already registered`
153 | console.log(err)
154 | alert(err)
155 | return
156 | }
157 |
158 | if (!available.is_valid) {
159 | const err = `Domain ${domainName}.pkt is not valid (can only be letters and numbers split up by dashes)`
160 | console.log(err)
161 | alert(err)
162 | return
163 | }
164 |
165 | if (maxPkt < BigInt(available.min_value)) {
166 | const err = `The maximum PKT to stake is less than required amount: ${ethers.formatEther(
167 | available.min_value
168 | )}`
169 | console.log(err)
170 | alert(err)
171 | return
172 | }
173 |
174 | if (
175 | !walletState.signer ||
176 | !walletState.erc20abi ||
177 | !walletState.lockboxabi ||
178 | !walletState.multipayabi ||
179 | !walletState.pnsabi
180 | ) {
181 | console.error('Wallet state is not properly initialized')
182 | return
183 | }
184 |
185 | const token = new ethers.Contract(
186 | PKT_BASE,
187 | walletState.erc20abi,
188 | walletState.signer
189 | )
190 | const lockbox = new ethers.Contract(
191 | LOCKBOX,
192 | walletState.lockboxabi,
193 | walletState.signer
194 | )
195 | const multipay = new ethers.Contract(
196 | MULTIPAY,
197 | walletState.multipayabi,
198 | walletState.signer
199 | )
200 | const pns = new ethers.Contract(PNS, walletState.pnsabi, walletState.signer)
201 |
202 | // Step 1: Pre-allocating stake
203 | console.log(`[1/4] Pre-allocating stake`)
204 | updateTransactionHistory(
205 | walletState,
206 | setWalletState,
207 | setWalletHistory,
208 | 1,
209 | 'pre_allocating',
210 | {
211 | domainName,
212 | currentPrice: Number(ethers.formatEther(currentPrice)).toLocaleString(),
213 | maxPkt: stakeAmount
214 | }
215 | )
216 |
217 | try {
218 | myStakeId = await multipay.getStake(walletState.address)
219 | if (myStakeId > 0) {
220 | console.log(` - Got stake: ${myStakeId}`)
221 | updateTransactionHistory(
222 | walletState,
223 | setWalletState,
224 | setWalletHistory,
225 | 1,
226 | 'pre_allocating',
227 | {
228 | stakeId: myStakeId.toString(),
229 | domainName,
230 | currentPrice: Number(
231 | ethers.formatEther(currentPrice)
232 | ).toLocaleString(),
233 | maxPkt: stakeAmount
234 | }
235 | )
236 | } else {
237 | const gasLimit = await multipay.createEmptyStake.estimateGas(LOCKBOX)
238 | const res = await multipay.createEmptyStake(LOCKBOX, { gasLimit })
239 | const tx = await res.wait()
240 | console.log(` - TXID: ${tx.hash}`)
241 | await showTempTxId(walletState, setWalletState, tx.hash)
242 |
243 | myStakeId = await multipay.getStake(walletState.address)
244 | console.log(` - Got stake: ${myStakeId}`)
245 | updateTransactionHistory(
246 | walletState,
247 | setWalletState,
248 | setWalletHistory,
249 | 1,
250 | 'pre_allocating',
251 | {
252 | stakeId: myStakeId.toString(),
253 | domainName,
254 | currentPrice: Number(
255 | ethers.formatEther(currentPrice)
256 | ).toLocaleString(),
257 | maxPkt: stakeAmount,
258 | txId: tx.hash
259 | }
260 | )
261 | }
262 | } catch (error) {
263 | console.error('Error in pre-allocation:', error)
264 | await handleTransactionError(
265 | error,
266 | 1,
267 | walletState,
268 | setIsAvailableRegister,
269 | setWalletHistory,
270 | navigate
271 | )
272 | return
273 | }
274 |
275 | // Step 2: Authorizing
276 | console.log(
277 | `[2/4] Authorizing Multipay contract to stake up to ${ethers.formatEther(
278 | maxPkt
279 | )} PKT`
280 | )
281 | updateTransactionHistory(
282 | walletState,
283 | setWalletState,
284 | setWalletHistory,
285 | 2,
286 | 'authorizing',
287 | {
288 | stakeId: myStakeId.toString(),
289 | domainName,
290 | currentPrice: Number(ethers.formatEther(currentPrice)).toLocaleString(),
291 | maxPkt: stakeAmount
292 | }
293 | )
294 |
295 | try {
296 | const balance = await token.balanceOf(walletState.address)
297 | console.log('User balance:', ethers.formatEther(balance), 'PKT')
298 |
299 | if (balance < maxPkt) {
300 | setWalletState((prev) => ({ ...prev, status: 'low_funds' }))
301 | return
302 | }
303 |
304 | const gasLimit = await token.approve.estimateGas(MULTIPAY, maxPkt)
305 | const res = await token.approve(MULTIPAY, maxPkt, { gasLimit })
306 | const tx = await res.wait()
307 | console.log(` - TXID: ${tx.hash}`)
308 | await showTempTxId(walletState, setWalletState, tx.hash)
309 | updateTransactionHistory(
310 | walletState,
311 | setWalletState,
312 | setWalletHistory,
313 | 2,
314 | 'authorizing',
315 | {
316 | stakeId: myStakeId.toString(),
317 | domainName,
318 | currentPrice: Number(
319 | ethers.formatEther(currentPrice)
320 | ).toLocaleString(),
321 | maxPkt: stakeAmount,
322 | txId: tx.hash
323 | }
324 | )
325 | } catch (error) {
326 | console.error('Approval error:', error)
327 | await handleTransactionError(
328 | error,
329 | 2,
330 | walletState,
331 | setIsAvailableRegister,
332 | setWalletHistory,
333 | navigate
334 | )
335 | return
336 | }
337 |
338 | // Step 3: Staking Domain
339 | console.log(`[3/4] Calling Multipay.stakeDomain()`)
340 | updateTransactionHistory(
341 | walletState,
342 | setWalletState,
343 | setWalletHistory,
344 | 3,
345 | 'staking',
346 | {
347 | stakeId: myStakeId.toString(),
348 | domainName,
349 | currentPrice: Number(ethers.formatEther(currentPrice)).toLocaleString(),
350 | maxPkt: stakeAmount
351 | }
352 | )
353 |
354 | try {
355 | const encodedDomain = stringToHex(domainName)
356 | const args = [PKT_BASE, LOCKBOX, PNS, maxPkt, encodedDomain, '0x']
357 | const gasLimit = await multipay.stakeDomain.estimateGas(...args)
358 | const res = await multipay.stakeDomain(...args, { gasLimit })
359 | const tx = await res.wait()
360 | console.log(` - TXID: ${tx.hash}`)
361 |
362 | const stakeObj = await lockbox.lockups(myStakeId)
363 | const actualStake = ethers.formatEther(stakeObj[0])
364 | console.log(`Actual PKT staked: ${actualStake}`)
365 |
366 | updateTransactionHistory(
367 | walletState,
368 | setWalletState,
369 | setWalletHistory,
370 | 3,
371 | 'staking',
372 | {
373 | stakeId: myStakeId.toString(),
374 | domainName,
375 | currentPrice: Number(
376 | ethers.formatEther(currentPrice)
377 | ).toLocaleString(),
378 | maxPkt: stakeAmount,
379 | txId: tx.hash
380 | }
381 | )
382 | } catch (error) {
383 | console.error('Stake domain error:', error)
384 | await handleTransactionError(
385 | error,
386 | 3,
387 | walletState,
388 | setIsAvailableRegister,
389 | setWalletHistory,
390 | navigate
391 | )
392 | return
393 | }
394 |
395 | // Step 4: Finalizing Registration
396 | console.log(`[4/4] Calling PNS.register() to finalize registration`)
397 | updateTransactionHistory(
398 | walletState,
399 | setWalletState,
400 | setWalletHistory,
401 | 4,
402 | 'reserved',
403 | {
404 | stakeId: myStakeId.toString(),
405 | domainName,
406 | currentPrice: Number(ethers.formatEther(currentPrice)).toLocaleString(),
407 | maxPkt: stakeAmount
408 | }
409 | )
410 |
411 | try {
412 | const encodedDomain = stringToHex(domainName)
413 | const args = [myStakeId, encodedDomain, '0x']
414 | const gasLimit = await pns.register.estimateGas(...args)
415 | const res = await pns.register(...args, { gasLimit })
416 | const tx = await res.wait()
417 | console.log(` - TXID: ${tx.hash}`)
418 | await showTempTxId(walletState, setWalletState, tx.hash)
419 |
420 | const did = await pns.getDomainIdByLockupId(myStakeId)
421 | console.log(`Got domain ID ${did}`)
422 |
423 | updateTransactionHistory(
424 | walletState,
425 | setWalletState,
426 | setWalletHistory,
427 | 4,
428 | 'reserved',
429 | {
430 | stakeAmount: myStakeId.toString(),
431 | domainName,
432 | currentPrice: Number(
433 | ethers.formatEther(currentPrice)
434 | ).toLocaleString(),
435 | maxPkt: stakeAmount,
436 | txId: tx.hash
437 | }
438 | )
439 | await delay(2000)
440 | navigate('/web3-register/success', {
441 | state: { domainId: myStakeId.toString() }
442 | })
443 | } catch (error) {
444 | console.error('Error finalizing registration:', error)
445 | await handleTransactionError(
446 | error,
447 | 4,
448 | walletState,
449 | setIsAvailableRegister,
450 | setWalletHistory,
451 | navigate
452 | )
453 | }
454 | } catch (error) {
455 | console.error(error)
456 | setWalletState((prev) => ({ ...prev, status: 'connected' }))
457 | }
458 | }
459 |
460 | /**
461 | * Helper function to show transaction ID temporarily
462 | */
463 | export const showTempTxId = async (
464 | walletState: WalletState,
465 | setWalletState: React.Dispatch>,
466 | txHash: string
467 | ) => {
468 | setWalletState((prev) => {
469 | const newHistory = [...(prev.transactionHistory || [])]
470 | const currentStep = prev.step || 1
471 | console.log(walletState)
472 | // Update or add the current step's transaction
473 | const stepIndex = newHistory.findIndex((step) => step.step === currentStep)
474 | if (stepIndex >= 0) {
475 | newHistory[stepIndex] = {
476 | ...newHistory[stepIndex],
477 | txId: txHash,
478 | stakeAmount: prev.stakeAmount,
479 | timestamp: Date.now()
480 | }
481 | } else {
482 | newHistory.push({
483 | step: currentStep,
484 | status: prev.status || '',
485 | txId: txHash,
486 | stakeAmount: prev.stakeAmount,
487 | timestamp: Date.now()
488 | })
489 | }
490 |
491 | return { ...prev, txId: txHash, transactionHistory: newHistory }
492 | })
493 | await delay(2000)
494 | setWalletState((prev) => ({ ...prev, txId: undefined }))
495 | }
496 |
--------------------------------------------------------------------------------
/src/web3-register/components/TransactionHistory.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { TransactionHistoryItem } from '../../types/wallet'
3 |
4 | interface TransactionHistoryProps {
5 | transactionHistory?: TransactionHistoryItem[]
6 | currentStep: number
7 | }
8 |
9 | const TransactionHistory: React.FC = ({
10 | transactionHistory,
11 | currentStep
12 | }) => {
13 | if (!transactionHistory) return null
14 |
15 | return (
16 |
17 | {transactionHistory
18 | .filter((step) => step.step < currentStep)
19 | .sort((a, b) => b.step - a.step)
20 | .map((step, index) => (
21 |
22 |
23 | Step {step.step}: {step.status}
24 |
25 | {step.txId && (
26 |
27 | TXID: {step.txId}
28 |
29 | )}
30 | {step.stakeAmount && (
31 |
32 | Stake Amount: {step.stakeAmount}
33 |
34 | )}
35 | {step.domainName && (
36 |
37 | Domain: {step.domainName}
38 |
39 | )}
40 | {step.currentPrice && (
41 |
42 | Current Price: {step.currentPrice} PKT
43 |
44 | )}
45 | {step.maxPkt && (
46 |
47 | Max PKT: {step.maxPkt} PKT
48 |
49 | )}
50 |
51 | ))}
52 |
53 | )
54 | }
55 |
56 | export default TransactionHistory
57 |
--------------------------------------------------------------------------------
/src/web3-register/components/WalletConnector.tsx:
--------------------------------------------------------------------------------
1 | import { useEffect } from 'react'
2 | import { ethers } from 'ethers'
3 | import { WalletState } from '../../types/wallet'
4 |
5 | import { erc20Abi } from '../../abi/erc20-abi'
6 | import { lockboxAbi } from '../../abi/lockbox-abi'
7 | import { multiPlayAbi } from '../../abi/multipay-abi'
8 | import { pnsAbi } from '../../abi/pns-abi'
9 |
10 | interface WalletConnectorProps {
11 | walletState: WalletState
12 | setWalletState: React.Dispatch>
13 | simulateWalletFlow: () => Promise
14 | }
15 |
16 | const WalletConnector: React.FC = ({
17 | walletState,
18 | setWalletState,
19 | simulateWalletFlow
20 | }) => {
21 | const handleAccountsChanged = (accounts: string[]) => {
22 | if (accounts.length === 0) {
23 | setWalletState({ status: 'not_found', transactionHistory: [] })
24 | } else {
25 | simulateWalletFlow()
26 | }
27 | }
28 |
29 | const handleChainChanged = () => {
30 | simulateWalletFlow()
31 | }
32 |
33 | useEffect(() => {
34 | const initWallet = async () => {
35 | setWalletState({ ...walletState, status: 'loading' })
36 |
37 | if (typeof window.ethereum === 'undefined') {
38 | console.log('No wallet found')
39 | setWalletState({ ...walletState, status: 'not_found' })
40 | return
41 | }
42 |
43 | try {
44 | try {
45 | await window.ethereum.request({ method: 'eth_requestAccounts' })
46 | } catch (error: any) {
47 | if (error.code === -32002) {
48 | console.log(
49 | 'Wallet connection request already pending. Please check your wallet.'
50 | )
51 | setWalletState({ ...walletState, status: 'not_found' })
52 | return
53 | }
54 | throw error
55 | }
56 |
57 | try {
58 | await window.ethereum.request({
59 | method: 'wallet_switchEthereumChain',
60 | params: [{ chainId: '0x2105' }]
61 | })
62 | } catch (switchError: any) {
63 | if (switchError.code === 4902) {
64 | try {
65 | await window.ethereum.request({
66 | method: 'wallet_addEthereumChain',
67 | params: [
68 | {
69 | chainId: '0x2105',
70 | chainName: 'Base',
71 | nativeCurrency: {
72 | name: 'ETH',
73 | symbol: 'ETH',
74 | decimals: 18
75 | },
76 | rpcUrls: ['https://mainnet.base.org'],
77 | blockExplorerUrls: ['https://basescan.org']
78 | }
79 | ]
80 | })
81 | } catch (addError) {
82 | console.error('Failed to add Base network:', addError)
83 | setWalletState({ ...walletState, status: 'not_found' })
84 | return
85 | }
86 | } else {
87 | console.error('Failed to switch to Base network:', switchError)
88 | setWalletState({ ...walletState, status: 'not_found' })
89 | return
90 | }
91 | }
92 |
93 | const provider = new ethers.BrowserProvider(window.ethereum)
94 | const signer = await provider.getSigner()
95 | const address = await signer.getAddress()
96 |
97 | setWalletState({
98 | ...walletState,
99 | status: 'connected',
100 | address: address,
101 | label: address.slice(0, 6) + '...' + address.slice(-4),
102 | provider: provider,
103 | signer: signer,
104 | erc20abi: erc20Abi,
105 | lockboxabi: lockboxAbi,
106 | multipayabi: multiPlayAbi,
107 | pnsabi: pnsAbi
108 | })
109 | console.log('Ready.')
110 |
111 | window.ethereum.on('accountsChanged', handleAccountsChanged)
112 | window.ethereum.on('chainChanged', handleChainChanged)
113 | } catch (error) {
114 | console.error('Error connecting to wallet:', error)
115 | setWalletState({ ...walletState, status: 'not_found' })
116 | }
117 | }
118 |
119 | initWallet()
120 |
121 | return () => {
122 | if (window.ethereum) {
123 | window.ethereum.removeListener('accountsChanged', handleAccountsChanged)
124 | window.ethereum.removeListener('chainChanged', handleChainChanged)
125 | }
126 | }
127 | }, [])
128 |
129 | return null
130 | }
131 |
132 | export default WalletConnector
133 |
--------------------------------------------------------------------------------
/src/web3-register/components/WalletStatus.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import connected from '../../assets/images/connected.svg'
3 | import { WalletState } from '../../types/wallet'
4 |
5 | interface WalletStatusProps {
6 | walletState: WalletState
7 | renderStepHistory: (currentStep: number) => React.ReactNode
8 | }
9 |
10 | const WalletStatus: React.FC = ({
11 | walletState,
12 | renderStepHistory
13 | }) => {
14 | const getStatusMessage = (status: WalletState['status']): string => {
15 | switch (status) {
16 | case 'pre_allocating':
17 | return 'Pre-allocating stake'
18 | case 'authorizing':
19 | return 'Authorizing stake'
20 | case 'staking':
21 | return 'Staking domain'
22 | default:
23 | return ''
24 | }
25 | }
26 |
27 | const getStakeLabel = (status: WalletState['status']): string => {
28 | return status === 'pre_allocating' ? 'Got stake:' : 'Actual stake amount:'
29 | }
30 |
31 | const renderCurrentStep = () => {
32 | return (
33 |
34 |
35 | Step {walletState.step}/4: {getStatusMessage(walletState.status)}
36 |
37 |
46 |
47 | {walletState.stakeId &&
48 | walletState.stakeId !== '0' &&
49 | walletState.step === 1 && (
50 |
51 |
Stake ID:
52 |
{walletState.stakeId}
53 |
54 | )}
55 |
56 |
57 | {walletState.txId ? '- TXID:' : ''}
58 |
59 |
{walletState.txId}
60 |
61 | {walletState.stakeAmount && (
62 |
63 |
64 | {getStakeLabel(walletState.status)}
65 |
66 |
{walletState.stakeAmount}
67 |
68 | )}
69 |
70 |
71 | )
72 | }
73 |
74 | const firstStepHistory = walletState.transactionHistory?.find(
75 | (step) => step.step === 1
76 | )
77 |
78 | switch (walletState.status) {
79 | case 'loading':
80 | return (
81 |
82 |
Wallet loading...
83 |
86 |
Trying to detect wallet
87 |
88 | )
89 |
90 | case 'connected':
91 | return (
92 |
93 |
Wallet connected
94 |
95 |
100 |
101 |
102 | {walletState.label}
103 |
104 |
105 | )
106 |
107 | case 'pre_allocating':
108 | return (
109 |
110 | {renderCurrentStep()}
111 | {renderStepHistory(1)}
112 |
113 | )
114 |
115 | case 'authorizing':
116 | return (
117 |
118 | {renderCurrentStep()}
119 | {renderStepHistory(2)}
120 |
121 | )
122 |
123 | case 'staking':
124 | return (
125 |
126 | {renderCurrentStep()}
127 | {renderStepHistory(3)}
128 |
129 | )
130 |
131 | case 'reserved':
132 | return (
133 |
134 |
135 |
136 | Domain reserved
137 |
138 |
144 |
145 |
146 | Domain ID: {firstStepHistory?.stakeId || walletState.stakeId}
147 |
148 |
sending to wallet
149 |
150 |
151 |
152 | )
153 |
154 | case 'not_found':
155 | return (
156 |
157 |
158 |
159 | Wallet {' '}
160 | window.location.reload()}
162 | className="text-red-600 hover:text-red-500 transition-colors duration-200"
163 | >
164 | not found
165 |
166 |
167 |
168 | Desktop wallet browser extension is required
169 |
170 |
171 |
173 | window.open('https://discord.gg/9qgtGgx2Ku', '_blank')
174 | }
175 | className="w-[50%] bg-[#0D4AE7] text-white rounded-full py-3 flex items-center justify-center space-x-2"
176 | >
177 | Need help?
178 |
179 | ?
180 |
181 |
182 |
183 | )
184 |
185 | case 'price_changed':
186 | return (
187 |
188 |
189 |
Price is higher than input 😕
190 |
191 |
window.location.reload()}
193 | className="w-[50%] bg-[#0D4AE7] text-white rounded-full py-3"
194 | >
195 | Try again
196 |
197 |
198 | )
199 |
200 | case 'low_funds':
201 | return (
202 |
203 |
204 |
You are low on funds 😕
205 |
window.location.reload()}
207 | className="w-[200px] h-[36px] bg-[#0D4AE7] text-white rounded-full mt-[24px]"
208 | >
209 | Try again
210 |
211 |
212 |
213 | )
214 |
215 | default:
216 | return null
217 | }
218 | }
219 |
220 | export default WalletStatus
221 |
--------------------------------------------------------------------------------
/src/web3-register/components/index.ts:
--------------------------------------------------------------------------------
1 | export { default as WalletStatus } from './WalletStatus'
2 | export { default as RegistrationForm } from './RegistrationForm'
3 | export { default as TransactionHistory } from './TransactionHistory'
4 | export { default as WalletConnector } from './WalletConnector'
5 | export * from './RegistrationService'
6 |
--------------------------------------------------------------------------------
/src/web3-register/index.tsx:
--------------------------------------------------------------------------------
1 | // Export all web3 registration components
2 | export { default as Web3Register } from './Web3Register'
3 | export { default as RegistrationComplete } from './RegistrationComplete'
4 | export { default as RegistrationFailed } from './RegistrationFailed'
5 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | content: ['./dist/**/*.html', './src/**/*.{js,jsx,ts,tsx}', './*.html'],
3 | theme: {
4 | extend: {
5 | keyframes: {
6 | 'confetti-1': {
7 | '0%': { transform: 'translateY(0) rotate(0)' },
8 | '100%': { transform: 'translateY(100vh) rotate(360deg)' }
9 | },
10 | 'confetti-2': {
11 | '0%': { transform: 'translateY(0) rotate(0)' },
12 | '100%': { transform: 'translateY(100vh) rotate(-360deg)' }
13 | },
14 | 'confetti-3': {
15 | '0%': { transform: 'translateY(0) rotate(0)' },
16 | '100%': { transform: 'translateY(100vh) rotate(720deg)' }
17 | },
18 | 'confetti-4': {
19 | '0%': { transform: 'translateY(0) rotate(0)' },
20 | '100%': { transform: 'translateY(100vh) rotate(-720deg)' }
21 | },
22 | 'confetti-5': {
23 | '0%': { transform: 'translateY(0) rotate(0)' },
24 | '100%': { transform: 'translateY(100vh) rotate(1080deg)' }
25 | },
26 | 'confetti-6': {
27 | '0%': { transform: 'translateY(0) rotate(0)' },
28 | '100%': { transform: 'translateY(100vh) rotate(-1080deg)' }
29 | }
30 | },
31 | animation: {
32 | 'confetti-1': 'confetti-1 3s linear infinite',
33 | 'confetti-2': 'confetti-2 3.5s linear infinite',
34 | 'confetti-3': 'confetti-3 4s linear infinite',
35 | 'confetti-4': 'confetti-4 4.5s linear infinite',
36 | 'confetti-5': 'confetti-5 5s linear infinite',
37 | 'confetti-6': 'confetti-6 5.5s linear infinite'
38 | }
39 | }
40 | },
41 | plugins: [require('@tailwindcss/forms'), require('tailwind-scrollbar')],
42 | variants: {
43 | extend: {
44 | opacity: ['disabled'],
45 | scrollbar: ['rounded'] // Optional: enable more variants for scrollbar
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2020",
4 | "useDefineForClassFields": true,
5 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
6 | "module": "ESNext",
7 | "skipLibCheck": true,
8 | "moduleResolution": "bundler",
9 | "allowImportingTsExtensions": true,
10 | "resolveJsonModule": true,
11 | "isolatedModules": true,
12 | "noEmit": true,
13 | "jsx": "react-jsx",
14 | "strict": true,
15 | "noUnusedLocals": true,
16 | "noUnusedParameters": true,
17 | "noFallthroughCasesInSwitch": true,
18 | "baseUrl": ".",
19 | "paths": {
20 | "@/*": ["src/*"]
21 | },
22 | "esModuleInterop": true
23 | },
24 | "include": ["src"],
25 | "references": [{ "path": "./tsconfig.node.json" }]
26 | }
27 |
--------------------------------------------------------------------------------
/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "skipLibCheck": true,
5 | "module": "ESNext",
6 | "moduleResolution": "bundler",
7 | "allowSyntheticDefaultImports": true
8 | },
9 | "include": ["vite.config.ts"]
10 | }
--------------------------------------------------------------------------------
/vercel.json:
--------------------------------------------------------------------------------
1 | {
2 | "rewrites": [
3 | {
4 | "source": "/(.*)",
5 | "destination": "/index.html"
6 | }
7 | ],
8 | "headers": [
9 | {
10 | "source": "/(.*)",
11 | "headers": [
12 | {
13 | "key": "Cache-Control",
14 | "value": "public, max-age=0, must-revalidate"
15 | }
16 | ]
17 | }
18 | ]
19 | }
20 |
--------------------------------------------------------------------------------
/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import react from '@vitejs/plugin-react'
3 | import path from 'path'
4 |
5 | export default defineConfig({
6 | plugins: [react()],
7 | resolve: {
8 | alias: {
9 | '@': path.resolve(__dirname, './src')
10 | }
11 | },
12 | server: {
13 | headers: {
14 | 'Content-Security-Policy':
15 | "script-src 'self' 'unsafe-eval' 'unsafe-inline';"
16 | }
17 | }
18 | })
19 |
--------------------------------------------------------------------------------