diff --git a/.eslintignore b/.eslintignore
deleted file mode 100644
index 9f3ecb2a..00000000
--- a/.eslintignore
+++ /dev/null
@@ -1,8 +0,0 @@
-node_modules/*
-dist/*
-web-dev-server.config.js
-tsup.config.ts
-**/test/**
-vite.config.ts
-scripts/prepare-packages.ts
-tests/e2e
diff --git a/.eslintrc.js b/.eslintrc.js
deleted file mode 100644
index f2107517..00000000
--- a/.eslintrc.js
+++ /dev/null
@@ -1,9 +0,0 @@
-const Config = require('@vechain/repo-config');
-
-module.exports = {
- ...Config.EslintLibrary,
- rules: {
- ...Config.EslintLibrary.rules,
- 'import/no-extraneous-dependencies': 'error',
- },
-};
diff --git a/.gitignore b/.gitignore
index 9f120d2a..7c8abae2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -38,14 +38,5 @@ yarn-error.log*
.vscode
.parcel-cache
+veworld-dist
.reports
-
-packages/**/dist
-packages/**/.turbo
-packages/**/node_modules
-apps/**/dist
-apps/**/.turbo
-apps/**/node_modules
-docs/**/dist
-
-coverageUnit
\ No newline at end of file
diff --git a/.yarn/install-state.gz b/.yarn/install-state.gz
index 65b912cf..9cd1aa9a 100644
Binary files a/.yarn/install-state.gz and b/.yarn/install-state.gz differ
diff --git a/LICENSE b/LICENSE
index 329efe3f..51f73f5a 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
MIT License
-Copyright (c) 2023 Vechain
+Copyright (c) 2023 VeChain
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/README.md b/README.md
index 53f45718..4e252838 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,20 @@
-# vechain-dapp-kit
-
-The Vechain DAppKit is a TypeScript library that facilitates seamless interaction between vechain wallets (veworld, sync2)
-and dApps, enhancing user experience and developer convenience. Please refer to [Vechain Docs](https://docs.vechain.org/developer-resources/sdks-and-providers/dapp-kit) for full documentation and usage.
+
+
vechain-dapp-kit
+
+ A TypeScript library that facilitates seamless interaction between VeChain wallets.
+
+
+
+
+
+
+
+
+
+## Introduction
+
+The VeChain DAppKit is a TypeScript library that facilitates seamless interaction between VeChain wallets (VeWorld, sync2)
+and dApps, enhancing user experience and developer convenience. Please refer to [VeChain Docs](https://docs.vechain.org/developer-resources/sdks-and-providers/dapp-kit) for full documentation and usage.
## Table of Contents
@@ -19,34 +32,24 @@ and dApps, enhancing user experience and developer convenience. Please refer to
## Why ?
- Allow easy interaction with all wallets.
-- Currently, connex only plays nice with Sync / Sync2
+- Currently, Connex only plays nice with Sync / Sync2
- Enable a better UX for users
## Key features
-Key Features a.k.a scope
-
-1. wallet connectivity
-
- key components that handle interaction with veworld and sync 2
-
-2. customizable UI
-
- ability to totally customize the UI of components
-
-3. User Experience
+1. **Wallet Connectivity**: Key components that handle interaction with VeWorld and Sync 2.
- Consistent experience with Ethereum and other chains
+2. **Customizable UI**: Ability to totally customize the UI of components.
-4. Developer friendly
+3. **User Experience**: Consistent experience with Ethereum and other chains.
- Easy to adopt with proper documentation.
+4. **Developer friendly**: Easy to adopt with proper documentation.
---
## Contributing
-- Please refer to the [Contributing Guide](./CONTRIBUTING.md) for more information on how to contribute to the project.
+Please refer to the [Contributing Guide](./CONTRIBUTING.md) for more information on how to contribute to the project.
---
@@ -163,7 +166,7 @@ yarn test:e2e:headless
## Further Documentation & Usage
-- Please refer to [Vechain Docs](https://docs.vechain.org/developer-resources/sdks-and-providers) for more information
+- Please refer to [VeChain Docs](https://docs.vechain.org/developer-resources/sdks-and-providers) for more information
on how to use the library.
---
@@ -171,10 +174,15 @@ yarn test:e2e:headless
## Publishing
```bash
-git clone git@github.com:vechainfoundation/vechain-dapp-kit.git
-cd vechain-dapp-kit
-git checkout X.Y.Z
-yarn install:all
-yarn build:release X.Y.Z
-yarn changeset publish
+# prepare the release, this will check out the release branch, install dependencies, build packages, test and update the package versions
+yarn prepare:release X.Y.Z
+```
+
+Create the PR for the release branch `vX.Y.Z`.
+
+When the PR is merged, create the release on github called `X.Y.Z`, it will automatically tag the commit with the version `X.Y.Z`.
+
+```bash
+# publish the release
+yarn publish:release X.Y.Z
```
diff --git a/eslint.config.mjs b/eslint.config.mjs
new file mode 100644
index 00000000..c9e3ec07
--- /dev/null
+++ b/eslint.config.mjs
@@ -0,0 +1,17 @@
+import tseslint from 'typescript-eslint';
+
+export default tseslint.config({
+ ignores: ['**/*.config.ts', 'dist/**'],
+ extends: [...tseslint.configs.recommended],
+ files: ['**/*.{ts,tsx}'],
+ rules: {
+ '@typescript-eslint/no-explicit-any': 'off',
+ '@typescript-eslint/ban-ts-comment': 'off',
+ 'no-console': ['error', { allow: ['error'] }],
+ 'eslint-comments/no-unused-disable': 'off',
+ '@typescript-eslint/no-unused-vars': [
+ 'error',
+ { argsIgnorePattern: '^_' },
+ ],
+ },
+});
diff --git a/examples/sample-angular-app/.eslintrc.js b/examples/sample-angular-app/.eslintrc.js
deleted file mode 100644
index 4185cb4f..00000000
--- a/examples/sample-angular-app/.eslintrc.js
+++ /dev/null
@@ -1,9 +0,0 @@
-const Config = require('@vechain/repo-config');
-
-module.exports = {
- ...Config.EslintLibrary,
- rules: {
- 'no-constant-binary-expression': 'off',
- 'eslint-comments/disable-enable-pair': 'off',
- },
-};
diff --git a/examples/sample-angular-app/package.json b/examples/sample-angular-app/package.json
index ecb9fb57..f00ca7e8 100644
--- a/examples/sample-angular-app/package.json
+++ b/examples/sample-angular-app/package.json
@@ -8,7 +8,6 @@
"clean": "rm -rf dist .turbo .angular",
"dev": "ng serve --port 5004",
"gh-pages-build": "ng build --configuration development --base-href '/vechain-dapp-kit/angular/'",
- "lint": "eslint src --ext .js,.jsx,.ts,.tsx",
"preview": "ng serve --port 5004",
"purge": "yarn clean; rm -rf node_modules",
"watch": "ng build --watch --configuration development"
@@ -50,14 +49,13 @@
"@angular/cli": "^18.2.2",
"@angular/compiler-cli": "^18.2.2",
"@types/jasmine": "~3.10.0",
- "@types/node": "^22.9.0",
- "@vechain/repo-config": "https://github.com/vechain/repo-config#v0.0.1",
+ "@types/node": "^12.11.1",
"jasmine-core": "~4.0.0",
"karma": "~6.3.0",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage": "~2.1.0",
"karma-jasmine": "~4.0.0",
"karma-jasmine-html-reporter": "~1.7.0",
- "typescript": "5.5.4"
+ "typescript": "5.4.2"
}
}
diff --git a/examples/sample-angular-app/src/environments/environment.prod.ts b/examples/sample-angular-app/src/environments/environment.prod.ts
index b931095f..3938526c 100644
--- a/examples/sample-angular-app/src/environments/environment.prod.ts
+++ b/examples/sample-angular-app/src/environments/environment.prod.ts
@@ -1,5 +1,4 @@
// eslint-disable @typescript-eslint/no-unsafe-assignment
-// eslint-disable @typescript-eslint/no-unsafe-member-access
// Packages
import packageInfo from '../../package.json';
@@ -12,7 +11,6 @@ const baseUrl = scheme + host + port + path;
export const environment = {
production: true,
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
version: packageInfo.version,
appName: 'EasyAngular',
envName: 'prod',
diff --git a/examples/sample-angular-app/src/environments/environment.ts b/examples/sample-angular-app/src/environments/environment.ts
index 5244976c..1158e730 100644
--- a/examples/sample-angular-app/src/environments/environment.ts
+++ b/examples/sample-angular-app/src/environments/environment.ts
@@ -1,5 +1,4 @@
// eslint-disable @typescript-eslint/no-unsafe-assignment
-// eslint-disable @typescript-eslint/no-unsafe-member-access
// This file can be replaced during build by using the `fileReplacements` array.
// `ng build` replaces `environment.ts` with `environment.prod.ts`.
// The list of file replacements can be found in `angular.json`.
@@ -18,7 +17,6 @@ const baseUrl = scheme + host + port + path;
export const environment = {
production: false,
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
version: packageInfo.version,
appName: 'EasyAngular',
envName: 'local',
diff --git a/examples/sample-angular-app/src/main.ts b/examples/sample-angular-app/src/main.ts
index 5d15c9f8..ae94c856 100644
--- a/examples/sample-angular-app/src/main.ts
+++ b/examples/sample-angular-app/src/main.ts
@@ -3,5 +3,4 @@ import { AppComponent } from './app/app.component';
bootstrapApplication(AppComponent, {
providers: [],
- // eslint-disable-next-line no-console
}).catch((err) => console.error(err));
diff --git a/examples/sample-angular-app/src/polyfills.ts b/examples/sample-angular-app/src/polyfills.ts
index 3db6b9bd..730b959b 100644
--- a/examples/sample-angular-app/src/polyfills.ts
+++ b/examples/sample-angular-app/src/polyfills.ts
@@ -1,5 +1,3 @@
-/* eslint-disable @typescript-eslint/no-var-requires */
-/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/**
* This file includes polyfills needed by Angular and is loaded before the app.
* You can add your own extra polyfills to this file.
@@ -55,6 +53,7 @@ import 'zone.js'; // Included with Angular CLI.
import '@angular/localize/init';
(window as any).global = window;
+// eslint-disable-next-line @typescript-eslint/no-require-imports
global.Buffer = global.Buffer || require('buffer').Buffer;
(window as any).process = {
env: { DEBUG: undefined },
diff --git a/examples/sample-angular-app/tsconfig.spec.json b/examples/sample-angular-app/tsconfig.spec.json
deleted file mode 100644
index e6a51278..00000000
--- a/examples/sample-angular-app/tsconfig.spec.json
+++ /dev/null
@@ -1,10 +0,0 @@
-/* To learn more about this file see: https://angular.io/config/tsconfig. */
-{
- "extends": "./tsconfig.json",
- "compilerOptions": {
- "outDir": "./out-tsc/spec",
- "types": ["jasmine"]
- },
- "files": ["src/test.ts", "src/polyfills.ts"],
- "include": ["src/**/*.spec.ts", "src/**/*.d.ts"]
-}
diff --git a/examples/sample-next-app/.eslintignore b/examples/sample-next-app/.eslintignore
deleted file mode 100644
index 35e54460..00000000
--- a/examples/sample-next-app/.eslintignore
+++ /dev/null
@@ -1,2 +0,0 @@
-.eslintrc.js
-tsconfig.json
\ No newline at end of file
diff --git a/examples/sample-next-app/package.json b/examples/sample-next-app/package.json
index fe2018e1..9a6baecf 100644
--- a/examples/sample-next-app/package.json
+++ b/examples/sample-next-app/package.json
@@ -11,10 +11,10 @@
"purge": "yarn clean; rm -rf node_modules"
},
"dependencies": {
- "@vechain/dapp-kit": "workspace:^",
- "@vechain/dapp-kit-react": "workspace:^",
- "@vechain/dapp-kit-ui": "workspace:^",
- "next": "15.0.3",
+ "@vechain/dapp-kit": "*",
+ "@vechain/dapp-kit-react": "*",
+ "@vechain/dapp-kit-ui": "*",
+ "next": "14.2.10",
"react": "^18",
"react-dom": "^18"
},
@@ -23,7 +23,7 @@
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
- "eslint": "^8",
+ "eslint": "^9.12.0",
"eslint-config-next": "14.1.4",
"typescript": "5.3.3"
}
diff --git a/examples/sample-next-app/tsconfig.json b/examples/sample-next-app/tsconfig.json
index 70cb05a2..b5e2b899 100644
--- a/examples/sample-next-app/tsconfig.json
+++ b/examples/sample-next-app/tsconfig.json
@@ -24,6 +24,12 @@
},
"typeRoots": ["node_modules/@types"]
},
- "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
+ "include": [
+ "next-env.d.ts",
+ "**/*.ts",
+ "**/*.tsx",
+ ".next/types/**/*.ts",
+ "dist/types/**/*.ts"
+ ],
"exclude": ["node_modules"]
}
diff --git a/examples/sample-react-app/.eslintrc.cjs b/examples/sample-react-app/.eslintrc.cjs
deleted file mode 100644
index 74080493..00000000
--- a/examples/sample-react-app/.eslintrc.cjs
+++ /dev/null
@@ -1,18 +0,0 @@
-module.exports = {
- root: true,
- env: { browser: true, es2020: true },
- extends: [
- 'eslint:recommended',
- 'plugin:@typescript-eslint/recommended',
- 'plugin:react-hooks/recommended',
- ],
- ignorePatterns: ['dist', '.eslintrc.cjs'],
- parser: '@typescript-eslint/parser',
- plugins: ['react-refresh'],
- rules: {
- 'react-refresh/only-export-components': [
- 'warn',
- { allowConstantExport: true },
- ],
- },
-};
diff --git a/examples/sample-react-app/eslint.config.mjs b/examples/sample-react-app/eslint.config.mjs
new file mode 100644
index 00000000..c9e3ec07
--- /dev/null
+++ b/examples/sample-react-app/eslint.config.mjs
@@ -0,0 +1,17 @@
+import tseslint from 'typescript-eslint';
+
+export default tseslint.config({
+ ignores: ['**/*.config.ts', 'dist/**'],
+ extends: [...tseslint.configs.recommended],
+ files: ['**/*.{ts,tsx}'],
+ rules: {
+ '@typescript-eslint/no-explicit-any': 'off',
+ '@typescript-eslint/ban-ts-comment': 'off',
+ 'no-console': ['error', { allow: ['error'] }],
+ 'eslint-comments/no-unused-disable': 'off',
+ '@typescript-eslint/no-unused-vars': [
+ 'error',
+ { argsIgnorePattern: '^_' },
+ ],
+ },
+});
diff --git a/examples/sample-react-app/package.json b/examples/sample-react-app/package.json
index 60b5070c..22721a43 100644
--- a/examples/sample-react-app/package.json
+++ b/examples/sample-react-app/package.json
@@ -8,7 +8,7 @@
"clean": "rm -rf dist .turbo",
"dev": "vite",
"gh-pages-build": "tsc && vite build",
- "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
+ "lint": "eslint",
"preview": "vite preview --mode=development",
"purge": "yarn clean; rm -rf node_modules",
"test": "vitest"
@@ -23,7 +23,7 @@
"ethers": "6.13.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
- "vite": "^5.0.12"
+ "vite": "^5.3.6"
},
"devDependencies": {
"@originjs/vite-plugin-commonjs": "^1.0.3",
@@ -32,7 +32,7 @@
"@typescript-eslint/eslint-plugin": "^6.10.0",
"@typescript-eslint/parser": "^6.10.0",
"@vitejs/plugin-react": "^4.2.0",
- "eslint": "^8.53.0",
+ "eslint": "^9.12.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.4",
"react-test-renderer": "^18.2.0",
diff --git a/examples/sample-react-app/src/App.tsx b/examples/sample-react-app/src/App.tsx
index 8f21d7bf..76359ffb 100644
--- a/examples/sample-react-app/src/App.tsx
+++ b/examples/sample-react-app/src/App.tsx
@@ -1,81 +1,28 @@
import {
- useThor,
+ WalletButton,
useWallet,
useWalletModal,
- WalletButton,
} from '@vechain/dapp-kit-react';
-import { useEffect, useMemo, useState } from 'react';
-import { Counter } from './counter.ts';
+import { friendlyAddress } from '@vechain/dapp-kit-ui';
+import { useEffect, useState } from 'react';
function App() {
- const { account, signer } = useWallet();
- const thor = useThor();
- const [count, setCount] = useState(BigInt(0));
- const [error, setError] = useState();
- const [txId, setTxId] = useState('');
- const [loading, setLoading] = useState(false);
- const { open, onConnectionStatusChange } = useWalletModal();
- const [buttonText, setButtonText] = useState('Connect Custom Button');
-
- const counterContract = useMemo(() => {
- return Counter.load(thor, signer);
- }, [thor, signer]);
+ const { account, accountDomain, isAccountDomainLoading } = useWallet();
- useEffect(() => {
- const loadCounter = async () => {
- const counter = await counterContract.read.counter({
- revision: {},
- });
- setCount(counter[0]);
- };
-
- loadCounter();
- }, [counterContract, loading, error, txId]);
-
- useEffect(() => {
- const handleConnected = (address: string | null) => {
- if (address) {
- const formattedAddress = `${address.slice(
- 0,
- 6,
- )}...${address.slice(-4)}`;
- setButtonText(`Disconnect from ${formattedAddress}`);
- } else {
- setButtonText('Connect Custom Button');
- }
- };
-
- handleConnected(account);
-
- onConnectionStatusChange(handleConnected);
- }, [account, onConnectionStatusChange]);
+ const { open } = useWalletModal();
+ const [buttonText, setButtonText] = useState('Connect Custom Button');
useEffect(() => {
- console.log('signer', signer);
- }, [signer]);
-
- const testTx = async () => {
- setTxId('');
- setError(undefined);
- try {
- setLoading(true);
-
- // TODO: Set the delegation URL so that transactions are free
- const tx = await counterContract.transact.increment();
-
- const receipt = await tx.wait();
- if (receipt == null || receipt.reverted) {
- setError(new Error('Transaction failed'));
- return;
- }
-
- setTxId(receipt.meta.txID!);
- } catch (e) {
- setError(e as Error);
- } finally {
- setLoading(false);
+ if (account) {
+ const addressOrDomain =
+ accountDomain && !isAccountDomainLoading
+ ? accountDomain
+ : friendlyAddress(account || '');
+ setButtonText(`Disconnect from ${addressOrDomain}`);
+ } else {
+ setButtonText('Connect Custom Button');
}
- };
+ }, [account, accountDomain, isAccountDomainLoading]);
return (
@@ -84,15 +31,8 @@ function App() {
custom button:
-
-
Counter
- {account && !loading &&
}
- {loading &&
Loading...
}
- {error &&
Error: {error.message}
}
- {account && txId &&
Transaction ID: {txId}
}
-
Counter: {count.toString()}
);
}
-export default App;
+export default App;
\ No newline at end of file
diff --git a/examples/sample-react-app/src/main.tsx b/examples/sample-react-app/src/main.tsx
index f593cb6b..105360e7 100644
--- a/examples/sample-react-app/src/main.tsx
+++ b/examples/sample-react-app/src/main.tsx
@@ -19,7 +19,7 @@ const walletConnectOptions: WalletConnectOptions = {
ReactDOM.createRoot(document.getElementById('root')!).render(
{
+ return {
+ plugins: [nodePolyfills(), react()],
+ build: {
+ commonjsOptions: {
+ transformMixedEsModules: true
+ }
+ },
+ preview: {
+ port: 5001,
+ strictPort: true
+ },
+ server: {
+ port: 5001,
+ strictPort: true,
+ host: true,
+ origin: "http://0.0.0.0:5001"
+ },
+ //vitest
+ test: {
+ globals: true,
+ environment: "happy-dom",
+ setupFiles: [
+ resolve(__vite_injected_original_dirname, "test/setup/setup.ts"),
+ resolve(__vite_injected_original_dirname, "test/setup/resizeObserverMock.ts")
+ ]
+ },
+ base: mode === "production" ? "/vechain-dapp-kit/react/" : "/"
+ };
+});
+export {
+ vite_config_default as default
+};
+//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCIvVXNlcnMvZGF2aWRlY2FycGluaS9hcHBzL3ZlY2hhaW4tZGFwcC1raXQvZXhhbXBsZXMvc2FtcGxlLXJlYWN0LWFwcFwiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9maWxlbmFtZSA9IFwiL1VzZXJzL2RhdmlkZWNhcnBpbmkvYXBwcy92ZWNoYWluLWRhcHAta2l0L2V4YW1wbGVzL3NhbXBsZS1yZWFjdC1hcHAvdml0ZS5jb25maWcudHNcIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfaW1wb3J0X21ldGFfdXJsID0gXCJmaWxlOi8vL1VzZXJzL2RhdmlkZWNhcnBpbmkvYXBwcy92ZWNoYWluLWRhcHAta2l0L2V4YW1wbGVzL3NhbXBsZS1yZWFjdC1hcHAvdml0ZS5jb25maWcudHNcIjsvLy8gPHJlZmVyZW5jZSB0eXBlcz1cInZpdGVzdFwiIC8+XG5cbmltcG9ydCB7IGRlZmluZUNvbmZpZyB9IGZyb20gJ3ZpdGUnO1xuaW1wb3J0IHJlYWN0IGZyb20gJ0B2aXRlanMvcGx1Z2luLXJlYWN0JztcbmltcG9ydCB7IG5vZGVQb2x5ZmlsbHMgfSBmcm9tICd2aXRlLXBsdWdpbi1ub2RlLXBvbHlmaWxscyc7XG5pbXBvcnQgeyByZXNvbHZlIH0gZnJvbSAncGF0aCc7XG5cbmV4cG9ydCBkZWZhdWx0IGRlZmluZUNvbmZpZygoeyBtb2RlIH0pID0+IHtcbiAgICByZXR1cm4ge1xuICAgICAgICBwbHVnaW5zOiBbbm9kZVBvbHlmaWxscygpLCByZWFjdCgpXSxcbiAgICAgICAgYnVpbGQ6IHtcbiAgICAgICAgICAgIGNvbW1vbmpzT3B0aW9uczoge1xuICAgICAgICAgICAgICAgIHRyYW5zZm9ybU1peGVkRXNNb2R1bGVzOiB0cnVlLFxuICAgICAgICAgICAgfSxcbiAgICAgICAgfSxcbiAgICAgICAgcHJldmlldzoge1xuICAgICAgICAgICAgcG9ydDogNTAwMSxcbiAgICAgICAgICAgIHN0cmljdFBvcnQ6IHRydWUsXG4gICAgICAgIH0sXG4gICAgICAgIHNlcnZlcjoge1xuICAgICAgICAgICAgcG9ydDogNTAwMSxcbiAgICAgICAgICAgIHN0cmljdFBvcnQ6IHRydWUsXG4gICAgICAgICAgICBob3N0OiB0cnVlLFxuICAgICAgICAgICAgb3JpZ2luOiAnaHR0cDovLzAuMC4wLjA6NTAwMScsXG4gICAgICAgIH0sXG4gICAgICAgIC8vdml0ZXN0XG4gICAgICAgIHRlc3Q6IHtcbiAgICAgICAgICAgIGdsb2JhbHM6IHRydWUsXG4gICAgICAgICAgICBlbnZpcm9ubWVudDogJ2hhcHB5LWRvbScsXG4gICAgICAgICAgICBzZXR1cEZpbGVzOiBbXG4gICAgICAgICAgICAgICAgcmVzb2x2ZShfX2Rpcm5hbWUsICd0ZXN0L3NldHVwL3NldHVwLnRzJyksXG4gICAgICAgICAgICAgICAgcmVzb2x2ZShfX2Rpcm5hbWUsICd0ZXN0L3NldHVwL3Jlc2l6ZU9ic2VydmVyTW9jay50cycpLFxuICAgICAgICAgICAgXSxcbiAgICAgICAgfSxcbiAgICAgICAgYmFzZTogbW9kZSA9PT0gJ3Byb2R1Y3Rpb24nID8gJy92ZWNoYWluLWRhcHAta2l0L3JlYWN0LycgOiAnLycsXG4gICAgfTtcbn0pO1xuIl0sCiAgIm1hcHBpbmdzIjogIjtBQUVBLFNBQVMsb0JBQW9CO0FBQzdCLE9BQU8sV0FBVztBQUNsQixTQUFTLHFCQUFxQjtBQUM5QixTQUFTLGVBQWU7QUFMeEIsSUFBTSxtQ0FBbUM7QUFPekMsSUFBTyxzQkFBUSxhQUFhLENBQUMsRUFBRSxLQUFLLE1BQU07QUFDdEMsU0FBTztBQUFBLElBQ0gsU0FBUyxDQUFDLGNBQWMsR0FBRyxNQUFNLENBQUM7QUFBQSxJQUNsQyxPQUFPO0FBQUEsTUFDSCxpQkFBaUI7QUFBQSxRQUNiLHlCQUF5QjtBQUFBLE1BQzdCO0FBQUEsSUFDSjtBQUFBLElBQ0EsU0FBUztBQUFBLE1BQ0wsTUFBTTtBQUFBLE1BQ04sWUFBWTtBQUFBLElBQ2hCO0FBQUEsSUFDQSxRQUFRO0FBQUEsTUFDSixNQUFNO0FBQUEsTUFDTixZQUFZO0FBQUEsTUFDWixNQUFNO0FBQUEsTUFDTixRQUFRO0FBQUEsSUFDWjtBQUFBO0FBQUEsSUFFQSxNQUFNO0FBQUEsTUFDRixTQUFTO0FBQUEsTUFDVCxhQUFhO0FBQUEsTUFDYixZQUFZO0FBQUEsUUFDUixRQUFRLGtDQUFXLHFCQUFxQjtBQUFBLFFBQ3hDLFFBQVEsa0NBQVcsa0NBQWtDO0FBQUEsTUFDekQ7QUFBQSxJQUNKO0FBQUEsSUFDQSxNQUFNLFNBQVMsZUFBZSw2QkFBNkI7QUFBQSxFQUMvRDtBQUNKLENBQUM7IiwKICAibmFtZXMiOiBbXQp9Cg==
diff --git a/examples/sample-remix-app/.eslintrc.cjs b/examples/sample-remix-app/.eslintrc.cjs
deleted file mode 100644
index 4f6f59ee..00000000
--- a/examples/sample-remix-app/.eslintrc.cjs
+++ /dev/null
@@ -1,84 +0,0 @@
-/**
- * This is intended to be a basic starting point for linting in your app.
- * It relies on recommended configs out of the box for simplicity, but you can
- * and should modify this configuration to best suit your team's needs.
- */
-
-/** @type {import('eslint').Linter.Config} */
-module.exports = {
- root: true,
- parserOptions: {
- ecmaVersion: "latest",
- sourceType: "module",
- ecmaFeatures: {
- jsx: true,
- },
- },
- env: {
- browser: true,
- commonjs: true,
- es6: true,
- },
- ignorePatterns: ["!**/.server", "!**/.client"],
-
- // Base config
- extends: ["eslint:recommended"],
-
- overrides: [
- // React
- {
- files: ["**/*.{js,jsx,ts,tsx}"],
- plugins: ["react", "jsx-a11y"],
- extends: [
- "plugin:react/recommended",
- "plugin:react/jsx-runtime",
- "plugin:react-hooks/recommended",
- "plugin:jsx-a11y/recommended",
- ],
- settings: {
- react: {
- version: "detect",
- },
- formComponents: ["Form"],
- linkComponents: [
- { name: "Link", linkAttribute: "to" },
- { name: "NavLink", linkAttribute: "to" },
- ],
- "import/resolver": {
- typescript: {},
- },
- },
- },
-
- // Typescript
- {
- files: ["**/*.{ts,tsx}"],
- plugins: ["@typescript-eslint", "import"],
- parser: "@typescript-eslint/parser",
- settings: {
- "import/internal-regex": "^~/",
- "import/resolver": {
- node: {
- extensions: [".ts", ".tsx"],
- },
- typescript: {
- alwaysTryTypes: true,
- },
- },
- },
- extends: [
- "plugin:@typescript-eslint/recommended",
- "plugin:import/recommended",
- "plugin:import/typescript",
- ],
- },
-
- // Node
- {
- files: [".eslintrc.cjs"],
- env: {
- node: true,
- },
- },
- ],
-};
diff --git a/examples/sample-remix-app/package.json b/examples/sample-remix-app/package.json
index c1d776af..07c2f25a 100644
--- a/examples/sample-remix-app/package.json
+++ b/examples/sample-remix-app/package.json
@@ -8,7 +8,6 @@
"build": "remix vite:build --mode=development",
"dev": "remix vite:dev",
"gh-pages-build": "remix vite:build",
- "lint": "eslint --ignore-path .gitignore --cache --cache-location ./node_modules/.cache/eslint .",
"preview": "PORT=5007 remix-serve ./build/server/index.js",
"typecheck": "tsc"
},
@@ -16,7 +15,7 @@
"@remix-run/node": "^2.8.1",
"@remix-run/react": "^2.8.1",
"@remix-run/serve": "^2.8.1",
- "@vechain/dapp-kit-react": "^1.0.12",
+ "@vechain/dapp-kit-react": "*",
"buffer": "^6.0.3",
"isbot": "^4.1.0",
"react": "^18.2.0",
@@ -29,14 +28,14 @@
"@types/react-dom": "^18.2.7",
"@typescript-eslint/eslint-plugin": "^6.7.4",
"@typescript-eslint/parser": "^6.7.4",
- "eslint": "^8.38.0",
+ "eslint": "^9.12.0",
"eslint-import-resolver-typescript": "^3.6.1",
"eslint-plugin-import": "^2.28.1",
"eslint-plugin-jsx-a11y": "^6.7.1",
"eslint-plugin-react": "^7.33.2",
"eslint-plugin-react-hooks": "^4.6.0",
- "typescript": "5.3.3",
- "vite": "^5.1.0",
+ "typescript": "^5.1.6",
+ "vite": "^5.3.6",
"vite-plugin-node-polyfills": "^0.21.0",
"vite-tsconfig-paths": "^4.2.1"
},
diff --git a/examples/sample-svelte-app/.eslintignore b/examples/sample-svelte-app/.eslintignore
deleted file mode 100644
index ca7e2daa..00000000
--- a/examples/sample-svelte-app/.eslintignore
+++ /dev/null
@@ -1,14 +0,0 @@
-.DS_Store
-node_modules
-/build
-/.svelte-kit
-/package
-.env
-.env.*
-!.env.example
-
-# Ignore files for PNPM, NPM and YARN
-pnpm-lock.yaml
-package-lock.json
-yarn.lock
-.eslintrc.cjs
diff --git a/examples/sample-svelte-app/.eslintrc.cjs b/examples/sample-svelte-app/.eslintrc.cjs
deleted file mode 100644
index abfdd0be..00000000
--- a/examples/sample-svelte-app/.eslintrc.cjs
+++ /dev/null
@@ -1,8 +0,0 @@
-const Config = require('@vechain/repo-config');
-
-module.exports = {
- ...Config.EslintLibrary,
- rules: {
- 'no-constant-binary-expression': 'off',
- },
-};
diff --git a/examples/sample-svelte-app/eslint.config.mjs b/examples/sample-svelte-app/eslint.config.mjs
new file mode 100644
index 00000000..ef17617a
--- /dev/null
+++ b/examples/sample-svelte-app/eslint.config.mjs
@@ -0,0 +1,5 @@
+export default {
+ rules: {
+ 'no-constant-binary-expression': 'off',
+ },
+};
diff --git a/examples/sample-svelte-app/package.json b/examples/sample-svelte-app/package.json
index d4592306..0c46b905 100644
--- a/examples/sample-svelte-app/package.json
+++ b/examples/sample-svelte-app/package.json
@@ -8,7 +8,7 @@
"clean": "rm -rf .svelte-kit dist .turbo",
"dev": "vite dev",
"gh-pages-build": "BASE_PATH='/vechain-dapp-kit/svelte' vite build",
- "lint": "eslint src --ext .js,.jsx,.ts,.tsx",
+ "lint": "eslint",
"preview": "vite preview",
"purge": "yarn clean; rm -rf node_modules"
},
@@ -23,8 +23,7 @@
"@sveltejs/kit": "^1.27.4",
"@typescript-eslint/eslint-plugin": "^6.0.0",
"@typescript-eslint/parser": "^6.0.0",
- "@vechain/repo-config": "https://github.com/vechain/repo-config#v0.0.1",
- "eslint": "^8.28.0",
+ "eslint": "^9.12.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-svelte": "^2.30.0",
"prettier": "^3.0.0",
@@ -32,8 +31,8 @@
"svelte": "^4.0.5",
"svelte-check": "^3.6.0",
"tslib": "^2.4.1",
- "typescript": "5.3.3",
- "vite": "^4.5.2",
+ "typescript": "^5.0.0",
+ "vite": "^4.5.5",
"vite-plugin-node-polyfills": "^0.16.0"
}
}
diff --git a/examples/sample-svelte-app/svelte.config.js b/examples/sample-svelte-app/svelte.config.js
index dcee8126..30257126 100644
--- a/examples/sample-svelte-app/svelte.config.js
+++ b/examples/sample-svelte-app/svelte.config.js
@@ -25,5 +25,4 @@ const config = {
},
};
-// eslint-disable-next-line import/no-default-export
export default config;
diff --git a/examples/sample-vanilla-app/package.json b/examples/sample-vanilla-app/package.json
index d808ad9b..4ab7c027 100644
--- a/examples/sample-vanilla-app/package.json
+++ b/examples/sample-vanilla-app/package.json
@@ -15,7 +15,7 @@
"@vechain/dapp-kit-ui": "workspace:^"
},
"devDependencies": {
- "typescript": "5.3.3",
- "vite": "^5.2.0"
+ "typescript": "^5.2.2",
+ "vite": "^5.3.6"
}
}
diff --git a/examples/sample-vanilla-app/src/main.ts b/examples/sample-vanilla-app/src/main.ts
index 9f23b4df..1ed815ad 100644
--- a/examples/sample-vanilla-app/src/main.ts
+++ b/examples/sample-vanilla-app/src/main.ts
@@ -1,13 +1,13 @@
-import { DAppKitUI } from '@vechain/dapp-kit-ui';
+import { DAppKitUI, friendlyAddress } from '@vechain/dapp-kit-ui';
document.querySelector('#app')!.innerHTML = `
-
-
Vanilla JS
-
kit button:
-
-
custom button:
-
-
+
+
Vanilla JS
+
kit button:
+
+
custom button:
+
+
`;
const walletConnectOptions = {
@@ -21,7 +21,7 @@ const walletConnectOptions = {
};
DAppKitUI.configure({
- nodeUrl: 'https://testnet.vechain.org/',
+ nodeUrl: 'https://mainnet.vechain.org/',
walletConnectOptions,
usePersistence: true,
});
@@ -35,18 +35,28 @@ if (customButton) {
DAppKitUI.modal.open();
});
- const handleConnected = (address: string | null) => {
+ const render = () => {
+ const address = DAppKitUI.wallet.state.address;
+ const accountDomain = DAppKitUI.wallet.state.accountDomain;
+ const isAccountDomainLoading =
+ DAppKitUI.wallet.state.isAccountDomainLoading;
+
+ const addressOrDomain =
+ accountDomain && !isAccountDomainLoading
+ ? accountDomain
+ : friendlyAddress(address || '');
+
if (address) {
- const formattedAddress = `${address.slice(0, 6)}...${address.slice(
- -4,
- )}`;
- customButton.innerText = `Disconnect from ${formattedAddress}`;
+ customButton.innerText = `Disconnect from ${addressOrDomain}`;
} else {
customButton.innerText = 'Connect Custom Button';
}
};
- handleConnected(DAppKitUI.wallet.state.address);
+ render();
- DAppKitUI.modal.onConnectionStatusChange(handleConnected);
+ DAppKitUI.modal.onConnectionStatusChange(render);
+ DAppKitUI.wallet.subscribeToKey('address', render);
+ DAppKitUI.wallet.subscribeToKey('accountDomain', render);
+ DAppKitUI.wallet.subscribeToKey('isAccountDomainLoading', render);
}
diff --git a/examples/sample-vue-app/package.json b/examples/sample-vue-app/package.json
index c0ea3a64..f1ab3273 100644
--- a/examples/sample-vue-app/package.json
+++ b/examples/sample-vue-app/package.json
@@ -16,8 +16,8 @@
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.0.4",
- "typescript": "5.3.3",
- "vite": "^5.0.12",
+ "typescript": "^5.2.2",
+ "vite": "^5.3.6",
"vite-plugin-node-polyfills": "^0.17.0",
"vue-tsc": "^2.0.6"
}
diff --git a/package.json b/package.json
index 74112219..a268b2f7 100755
--- a/package.json
+++ b/package.json
@@ -9,15 +9,15 @@
"tests/*"
],
"scripts": {
+ "install:all": "yarn && yarn run build:deps",
"build": "turbo run build",
"build-react-kit": "turbo run build --filter='@vechain/dapp-kit-react'",
"build:deps": "turbo build --no-daemon --filter='@vechain/*'",
- "build:release": "ts-node scripts/prepare-packages.ts",
+ "prepare:release": "ts-node scripts/prepare-packages.ts",
+ "publish:release": "ts-node scripts/publish-packages.ts",
"clean": "rm -rf .turbo .parcel-cache .reports build && npx turbo@latest run clean",
"dev": "turbo run dev --filter='@vechain/*'",
- "format": "prettier --write \"**/*.{ts,tsx,md,json,js,jsx}\"",
"gh-pages-build": "turbo run gh-pages-build",
- "install:all": "yarn && yarn run build:deps",
"lint": "turbo run lint",
"prepare": "husky install",
"preview": "turbo run preview",
@@ -47,9 +47,8 @@
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
"@changesets/cli": "^2.27.1",
"@commitlint/config-conventional": "^18.0.0",
- "@vechain/repo-config": "https://github.com/vechain/repo-config#v0.0.1",
"commitlint": "^18.0.0",
- "eslint": "^8.4.1",
+ "eslint": "^9.12.0",
"eslint-plugin-prefer-arrow": "1.2.3",
"husky": "^8.0.0",
"lint-staged": "^15.0.2",
@@ -57,7 +56,8 @@
"punycode": "^1.4.1",
"ts-node": "^10.9.2",
"turbo": "latest",
- "typescript": "5.3.3"
+ "typescript": "5.3.3",
+ "typescript-eslint": "^8.11.0"
},
"packageManager": "yarn@4.5.1"
}
diff --git a/packages/dapp-kit-react/.eslintrc.cjs b/packages/dapp-kit-react/.eslintrc.cjs
deleted file mode 100644
index 94a56c6f..00000000
--- a/packages/dapp-kit-react/.eslintrc.cjs
+++ /dev/null
@@ -1,14 +0,0 @@
-const Config = require('@vechain/repo-config');
-
-module.exports = {
- ...Config.EslintReact,
- ignorePatterns: [
- ...Config.EslintReact.ignorePatterns,
- '*.test.ts',
- 'test/**',
- ],
- rules: {
- ...Config.EslintReact.rules,
- 'import/no-extraneous-dependencies': 'error',
- },
-};
diff --git a/packages/dapp-kit-react/eslint.config.mjs b/packages/dapp-kit-react/eslint.config.mjs
new file mode 100644
index 00000000..c9e3ec07
--- /dev/null
+++ b/packages/dapp-kit-react/eslint.config.mjs
@@ -0,0 +1,17 @@
+import tseslint from 'typescript-eslint';
+
+export default tseslint.config({
+ ignores: ['**/*.config.ts', 'dist/**'],
+ extends: [...tseslint.configs.recommended],
+ files: ['**/*.{ts,tsx}'],
+ rules: {
+ '@typescript-eslint/no-explicit-any': 'off',
+ '@typescript-eslint/ban-ts-comment': 'off',
+ 'no-console': ['error', { allow: ['error'] }],
+ 'eslint-comments/no-unused-disable': 'off',
+ '@typescript-eslint/no-unused-vars': [
+ 'error',
+ { argsIgnorePattern: '^_' },
+ ],
+ },
+});
diff --git a/packages/dapp-kit-react/package.json b/packages/dapp-kit-react/package.json
index 0cf10d0f..e12c8830 100644
--- a/packages/dapp-kit-react/package.json
+++ b/packages/dapp-kit-react/package.json
@@ -1,6 +1,6 @@
{
"name": "@vechain/dapp-kit-react",
- "version": "1.0.11",
+ "version": "1.1.1",
"homepage": "https://github.com/vechain/vechain-dapp-kit",
"repository": "github:vechain/vechain-dapp-kit",
"license": "MIT",
@@ -18,26 +18,24 @@
"scripts": {
"build": "tsup",
"clean": "rm -rf dist .turbo",
- "lint": "tsc --noEmit && eslint src --ext .js,.jsx,.ts,.tsx",
+ "lint": "eslint",
"purge": "yarn clean && rm -rf node_modules",
"test": "vitest run --coverage",
"watch": "yarn build --watch"
},
"dependencies": {
"@lit/react": "^1.0.1",
- "@vechain/dapp-kit": "workspace:^",
- "@vechain/dapp-kit-ui": "workspace:^",
- "@vechain/sdk-core": "1.0.0-rc.1",
- "@vechain/sdk-network": "1.0.0-rc.1",
- "react": "^18.2.0",
+ "@vechain/dapp-kit": "*",
+ "@vechain/dapp-kit-ui": "*",
+ "@vechain/sdk-core": "1.0.0-beta.32",
"valtio": "1.11.2"
},
"devDependencies": {
"@testing-library/react": "^14.1.2",
"@types/react": "^18.2.28",
"@types/react-dom": "^18.2.13",
- "@vechain/repo-config": "https://github.com/vechain/repo-config#v0.0.1",
- "eslint": "^8.15.0",
+ "eslint": "^9.12.0",
+ "react": "^18.2.0",
"tsup": "*",
"typescript": "*",
"vite": "^4.5.5",
diff --git a/packages/dapp-kit-react/src/DAppKitProvider.tsx b/packages/dapp-kit-react/src/DAppKitProvider/DAppKitProvider.tsx
similarity index 56%
rename from packages/dapp-kit-react/src/DAppKitProvider.tsx
rename to packages/dapp-kit-react/src/DAppKitProvider/DAppKitProvider.tsx
index 3c318675..d3c66cbf 100644
--- a/packages/dapp-kit-react/src/DAppKitProvider.tsx
+++ b/packages/dapp-kit-react/src/DAppKitProvider/DAppKitProvider.tsx
@@ -1,72 +1,27 @@
-import React, {
- createContext,
- useCallback,
- useContext,
- useEffect,
- useMemo,
- useState,
-} from 'react';
-import type { WalletSource } from '@vechain/dapp-kit';
+import React, { useCallback, useEffect, useMemo, useState } from 'react';
+import type { DAppKit, WalletSource } from '@vechain/dapp-kit';
import { DAppKitUI } from '@vechain/dapp-kit-ui';
import type { CertificateData } from '@vechain/sdk-core';
import { subscribeKey } from 'valtio/vanilla/utils';
-import type { DAppKitContext, DAppKitProviderOptions } from './types';
+import type { DAppKitContext, DAppKitProviderOptions } from '../types';
+import { Context } from './context';
-/**
- * Context
- */
-const Context = createContext(undefined);
-
-export const DAppKitProvider: React.FC = ({
+export const DAppKitProviderData = ({
children,
- nodeUrl,
- walletConnectOptions,
- usePersistence = false,
- logLevel,
- requireCertificate,
- themeMode,
- themeVariables,
- i18n,
- language,
- modalParent,
- onSourceClick,
- connectionCertificate: connectionCertificateData,
+ dAppKit
+}: {
+ children: React.ReactNode;
+ dAppKit: DAppKit;
}): React.ReactElement => {
- const dAppKit = useMemo(
- () =>
- DAppKitUI.configure({
- nodeUrl,
- walletConnectOptions,
- usePersistence,
- logLevel,
- requireCertificate,
- themeVariables,
- themeMode,
- i18n,
- language,
- modalParent,
- onSourceClick,
- connectionCertificate: connectionCertificateData,
- }),
- [
- nodeUrl,
- walletConnectOptions,
- usePersistence,
- logLevel,
- requireCertificate,
- themeVariables,
- themeMode,
- i18n,
- language,
- modalParent,
- onSourceClick,
- connectionCertificateData,
- ],
- );
-
const [account, setAccount] = useState(
dAppKit.wallet.state.address,
);
+ const [accountDomain, setAccountDomain] = useState(
+ dAppKit.wallet.state.accountDomain,
+ );
+ const [isAccountDomainLoading, setIsAccountDomainLoading] = useState(
+ dAppKit.wallet.state.isAccountDomainLoading,
+ );
const [source, setSource] = useState(
dAppKit.wallet.state.source,
);
@@ -83,6 +38,20 @@ export const DAppKitProvider: React.FC = ({
setAccount(v);
},
);
+ const domainSub = subscribeKey(
+ dAppKit.wallet.state,
+ 'accountDomain',
+ (v) => {
+ setAccountDomain(v);
+ },
+ );
+ const isAccountDomainLoadingSub = subscribeKey(
+ dAppKit.wallet.state,
+ 'isAccountDomainLoading',
+ (v) => {
+ setIsAccountDomainLoading(v);
+ },
+ );
const sourceSub = subscribeKey(dAppKit.wallet.state, 'source', (v) => {
setSource(v);
});
@@ -96,6 +65,8 @@ export const DAppKitProvider: React.FC = ({
return () => {
addressSub();
+ domainSub();
+ isAccountDomainLoadingSub();
sourceSub();
certificateSub();
};
@@ -108,6 +79,7 @@ export const DAppKitProvider: React.FC = ({
const closeModal = useCallback(() => {
DAppKitUI.modal.close();
}, []);
+
const onModalConnected = useCallback(
(callback: (address: string | null) => void) =>
DAppKitUI.modal.onConnectionStatusChange(callback),
@@ -124,6 +96,8 @@ export const DAppKitProvider: React.FC = ({
signer: dAppKit.signer,
availableWallets: dAppKit.wallet.state.availableSources,
account,
+ accountDomain,
+ isAccountDomainLoading,
source,
connectionCertificate,
},
@@ -136,43 +110,75 @@ export const DAppKitProvider: React.FC = ({
}, [
dAppKit,
account,
+ accountDomain,
+ isAccountDomainLoading,
source,
- closeModal,
+ connectionCertificate,
openModal,
+ closeModal,
onModalConnected,
- connectionCertificate,
]);
return {children};
};
-export const useThor = (): DAppKitContext['thor'] => {
- const context = useContext(Context);
-
- if (!context) {
- throw new Error('"useThor" must be used within a DAppKitProvider');
- }
-
- return context.thor;
-};
-
-export const useWallet = (): DAppKitContext['wallet'] => {
- const context = useContext(Context);
-
- if (!context) {
- throw new Error('"useWallet" must be used within a DAppKitProvider');
- }
-
- return context.wallet;
-};
-
-export const useWalletModal = (): DAppKitContext['modal'] => {
- const context = useContext(Context);
-
- if (!context) {
- throw new Error(
- '"useWalletModal" must be used within a DAppKitProvider',
+export const DAppKitProvider = ({
+ children,
+ nodeUrl,
+ genesis,
+ walletConnectOptions,
+ usePersistence = false,
+ logLevel,
+ requireCertificate,
+ themeVariables,
+ themeMode,
+ i18n,
+ language,
+ modalParent,
+ onSourceClick,
+ connectionCertificate: connectionCertificateData,
+ allowedWallets,
+}: DAppKitProviderOptions): React.ReactElement | null => {
+ const [dAppKit, setDAppKit] = useState(null);
+ useEffect(() => {
+ setDAppKit(
+ DAppKitUI.configure({
+ nodeUrl,
+ genesis,
+ walletConnectOptions,
+ usePersistence,
+ logLevel,
+ requireCertificate,
+ themeVariables,
+ themeMode,
+ i18n,
+ language,
+ modalParent,
+ onSourceClick,
+ connectionCertificate: connectionCertificateData,
+ allowedWallets,
+ }),
);
+ }, [
+ nodeUrl,
+ genesis,
+ walletConnectOptions,
+ usePersistence,
+ logLevel,
+ requireCertificate,
+ themeVariables,
+ themeMode,
+ i18n,
+ language,
+ modalParent,
+ onSourceClick,
+ connectionCertificateData,
+ allowedWallets,
+ ]);
+ if (!dAppKit) {
+ return null;
}
- return context.modal;
-};
+ return (
+ {children}
+ );
+};
\ No newline at end of file
diff --git a/packages/dapp-kit-react/src/DAppKitProvider/context.ts b/packages/dapp-kit-react/src/DAppKitProvider/context.ts
new file mode 100644
index 00000000..07719cd5
--- /dev/null
+++ b/packages/dapp-kit-react/src/DAppKitProvider/context.ts
@@ -0,0 +1,4 @@
+import { createContext } from 'react';
+import type { DAppKitContext } from '../types';
+
+export const Context = createContext(undefined);
diff --git a/packages/dapp-kit-react/src/DAppKitProvider/hooks/index.ts b/packages/dapp-kit-react/src/DAppKitProvider/hooks/index.ts
new file mode 100644
index 00000000..a3490637
--- /dev/null
+++ b/packages/dapp-kit-react/src/DAppKitProvider/hooks/index.ts
@@ -0,0 +1,3 @@
+export * from './useThor';
+export * from './useWallet';
+export * from './useWalletModal';
diff --git a/packages/dapp-kit-react/src/DAppKitProvider/hooks/useThor.ts b/packages/dapp-kit-react/src/DAppKitProvider/hooks/useThor.ts
new file mode 100644
index 00000000..77c71d69
--- /dev/null
+++ b/packages/dapp-kit-react/src/DAppKitProvider/hooks/useThor.ts
@@ -0,0 +1,16 @@
+import { useContext } from 'react';
+import { type DAppKitContext } from '../../types';
+import { Context } from '../context';
+
+/**
+ * Hook to get the thor object from the DAppKitProvider
+ */
+export const useThor = (): DAppKitContext['thor'] => {
+ const context = useContext(Context);
+
+ if (!context) {
+ throw new Error('"useThor" must be used within a DAppKitProvider');
+ }
+
+ return context.thor;
+};
diff --git a/packages/dapp-kit-react/test/useWallet.test.tsx b/packages/dapp-kit-react/src/DAppKitProvider/hooks/useWallet.test.tsx
similarity index 80%
rename from packages/dapp-kit-react/test/useWallet.test.tsx
rename to packages/dapp-kit-react/src/DAppKitProvider/hooks/useWallet.test.tsx
index c39b9675..72516203 100644
--- a/packages/dapp-kit-react/test/useWallet.test.tsx
+++ b/packages/dapp-kit-react/src/DAppKitProvider/hooks/useWallet.test.tsx
@@ -1,8 +1,7 @@
import { describe, expect, it } from 'vitest';
import { renderHook, waitFor } from '@testing-library/react';
-import { useWallet } from '../src';
-import { wrapper } from './helpers/react-test-helpers';
-import { mockedConnexSigner } from './helpers/mocked-signer';
+import { mockedConnexSigner, wrapper } from '../../../test';
+import { useWallet } from './useWallet';
window.vechain = {} as any;
window.vechain = {
@@ -46,4 +45,10 @@ describe('useWallet', () => {
expect(result.current.account).toBeNull();
});
});
+
+ it('should throw an error when used outside of DAppKitProvider', () => {
+ expect(() => renderHook(() => useWallet())).toThrow(
+ '"useWallet" must be used within a DAppKitProvider',
+ );
+ });
});
diff --git a/packages/dapp-kit-react/src/DAppKitProvider/hooks/useWallet.ts b/packages/dapp-kit-react/src/DAppKitProvider/hooks/useWallet.ts
new file mode 100644
index 00000000..da18a9bd
--- /dev/null
+++ b/packages/dapp-kit-react/src/DAppKitProvider/hooks/useWallet.ts
@@ -0,0 +1,16 @@
+import { useContext } from 'react';
+import { type DAppKitContext } from '../../types';
+import { Context } from '../context';
+
+/**
+ * Hook to get the wallet object from the DAppKitProvider
+ */
+export const useWallet = (): DAppKitContext['wallet'] => {
+ const context = useContext(Context);
+
+ if (!context) {
+ throw new Error('"useWallet" must be used within a DAppKitProvider');
+ }
+
+ return context.wallet;
+};
diff --git a/packages/dapp-kit-react/test/useWalletModal.test.tsx b/packages/dapp-kit-react/src/DAppKitProvider/hooks/useWalletModal.test.tsx
similarity index 71%
rename from packages/dapp-kit-react/test/useWalletModal.test.tsx
rename to packages/dapp-kit-react/src/DAppKitProvider/hooks/useWalletModal.test.tsx
index e2178b39..2ef3e623 100644
--- a/packages/dapp-kit-react/test/useWalletModal.test.tsx
+++ b/packages/dapp-kit-react/src/DAppKitProvider/hooks/useWalletModal.test.tsx
@@ -1,7 +1,7 @@
import { describe, expect, it } from 'vitest';
import { renderHook, waitFor } from '@testing-library/react';
-import { useWalletModal } from '../src';
-import { wrapper } from './helpers/react-test-helpers';
+import { useWalletModal } from '../..';
+import { wrapper } from '../../../test/helpers/react-test-helpers';
describe('useWalletModal', () => {
it('should be able to open the modal', async () => {
@@ -27,4 +27,9 @@ describe('useWalletModal', () => {
result.current.close();
});
+ it('should throw an error when used outside of DAppKitProvider', () => {
+ expect(() => renderHook(() => useWalletModal())).toThrow(
+ '"useWalletModal" must be used within a ConnexProvider',
+ );
+ });
});
diff --git a/packages/dapp-kit-react/src/DAppKitProvider/hooks/useWalletModal.ts b/packages/dapp-kit-react/src/DAppKitProvider/hooks/useWalletModal.ts
new file mode 100644
index 00000000..ce7fa479
--- /dev/null
+++ b/packages/dapp-kit-react/src/DAppKitProvider/hooks/useWalletModal.ts
@@ -0,0 +1,17 @@
+import { useContext } from 'react';
+import { type DAppKitContext } from '../../types';
+import { Context } from '../context';
+
+/**
+ * Hook to get the wallet modal object from the DAppKitProvider
+ */
+export const useWalletModal = (): DAppKitContext['modal'] => {
+ const context = useContext(Context);
+
+ if (!context) {
+ throw new Error(
+ '"useWalletModal" must be used within a ConnexProvider',
+ );
+ }
+ return context.modal;
+};
diff --git a/packages/dapp-kit-react/src/DAppKitProvider/index.ts b/packages/dapp-kit-react/src/DAppKitProvider/index.ts
new file mode 100644
index 00000000..22c0080f
--- /dev/null
+++ b/packages/dapp-kit-react/src/DAppKitProvider/index.ts
@@ -0,0 +1,2 @@
+export * from './DAppKitProvider';
+export * from './hooks';
diff --git a/packages/dapp-kit-react/src/hooks/index.ts b/packages/dapp-kit-react/src/hooks/index.ts
new file mode 100644
index 00000000..98a863f9
--- /dev/null
+++ b/packages/dapp-kit-react/src/hooks/index.ts
@@ -0,0 +1 @@
+export * from './useVechainDomain';
diff --git a/packages/dapp-kit-react/src/hooks/useVechainDomain/api/fetchVechainDomain.ts b/packages/dapp-kit-react/src/hooks/useVechainDomain/api/fetchVechainDomain.ts
new file mode 100644
index 00000000..f5f0a4f5
--- /dev/null
+++ b/packages/dapp-kit-react/src/hooks/useVechainDomain/api/fetchVechainDomain.ts
@@ -0,0 +1,78 @@
+import { addressUtils } from '@vechain/sdk-core';
+import type { DAppKitContext } from '../../../types';
+import { getDomain } from './getDomain';
+import { getAddress } from './getAddress';
+
+export interface VechainDomainResult {
+ address: string | undefined;
+ domain: string | undefined;
+ isValidAddressOrDomain: boolean;
+}
+
+/**
+ * Function to fetch the vechain domain of an account and vice versa by passing the connex object
+ */
+export const fetchVechainDomain = async ({
+ addressOrDomain,
+ thor,
+}: {
+ addressOrDomain?: string | null;
+ thor: DAppKitContext['thor'];
+}): Promise => {
+ if (!addressOrDomain) {
+ return {
+ address: undefined,
+ domain: undefined,
+ isValidAddressOrDomain: false,
+ };
+ }
+
+ const isValidAddress = addressUtils.isAddress(addressOrDomain);
+
+ if (isValidAddress) {
+ try {
+ const domainName = await getDomain({
+ address: addressOrDomain,
+ thor,
+ });
+ return {
+ address: addressOrDomain,
+ domain: domainName,
+ isValidAddressOrDomain: true,
+ };
+ } catch (err) {
+ console.error('Error getting domain: ', err);
+ return {
+ address: addressOrDomain,
+ domain: undefined,
+ isValidAddressOrDomain: true,
+ };
+ }
+ }
+
+ try {
+ const domainAddress = await getAddress({
+ domain: addressOrDomain,
+ thor,
+ });
+ if (domainAddress === '0x0000000000000000000000000000000000000000') {
+ return {
+ address: undefined,
+ domain: undefined,
+ isValidAddressOrDomain: false,
+ };
+ }
+ return {
+ address: domainAddress,
+ domain: addressOrDomain,
+ isValidAddressOrDomain: true,
+ };
+ } catch (err) {
+ console.error('Error getting address: ', err);
+ return {
+ address: undefined,
+ domain: undefined,
+ isValidAddressOrDomain: false,
+ };
+ }
+};
diff --git a/packages/dapp-kit-react/src/hooks/useVechainDomain/api/getAddress.test.ts b/packages/dapp-kit-react/src/hooks/useVechainDomain/api/getAddress.test.ts
new file mode 100644
index 00000000..0a102ab9
--- /dev/null
+++ b/packages/dapp-kit-react/src/hooks/useVechainDomain/api/getAddress.test.ts
@@ -0,0 +1,83 @@
+import { describe, it, expect, vi } from 'vitest';
+import { getAddress } from './getAddress';
+import { genesisBlocks, VNS_RESOLVER } from '@vechain/dapp-kit';
+import { ABIContract } from '@vechain/sdk-core';
+
+describe('getAddress', () => {
+ const mockThorClient = {
+ thor: {
+ blocks: {
+ getGenesisBlock: vi.fn(),
+ },
+ contracts: {
+ executeCall: vi.fn(),
+ },
+ },
+ } as any;
+
+ it('should return null if domain is null', async () => {
+ const result = await getAddress({ domain: null, thor: mockThorClient });
+ expect(result).toBeUndefined();
+ });
+
+ it('should use main resolver for mainnet', async () => {
+ mockThorClient.thor.blocks.getGenesisBlock.mockResolvedValue({ id: genesisBlocks.main.id });
+ mockThorClient.thor.contracts.executeCall.mockResolvedValue({
+ result: {
+ array: ['0x1234567890123456789012345678901234567890']
+ },
+ });
+
+ const result = await getAddress({ domain: 'example.vet', thor: mockThorClient.thor });
+
+ expect(mockThorClient.thor.contracts.executeCall).toHaveBeenCalledWith(VNS_RESOLVER.main, ABIContract.ofAbi(VNS_RESOLVER.abi).getFunction('getAddresses'), ['example.vet']);
+
+ expect(result).toBe('0x1234567890123456789012345678901234567890');
+ });
+
+ it('should use test resolver for testnet', async () => {
+ mockThorClient.thor.blocks.getGenesisBlock.mockResolvedValue({ id: genesisBlocks.test.id });
+ mockThorClient.thor.contracts.executeCall.mockResolvedValue({
+ result: {
+ array: ['0x1234567890123456789012345678901234567890']
+ },
+ });
+
+ const result = await getAddress({ domain: 'example.vet', thor: mockThorClient.thor });
+
+ expect(mockThorClient.thor.contracts.executeCall).toHaveBeenCalledWith(VNS_RESOLVER.test, ABIContract.ofAbi(VNS_RESOLVER.abi).getFunction('getAddresses'), ['example.vet']);
+
+ expect(result).toBe('0x1234567890123456789012345678901234567890');
+ });
+
+ it('should return the first address from the resolved addresses', async () => {
+ const expectedAddress = '0x1234567890123456789012345678901234567890';
+ mockThorClient.thor.contracts.executeCall.mockResolvedValue({
+ result: {
+ array: [expectedAddress]
+ },
+ });
+
+ const result = await getAddress({
+ domain: 'example.vet',
+ thor: mockThorClient.thor,
+ });
+
+ expect(result).toBe(expectedAddress);
+ });
+
+ it('should return null if no addresses are resolved', async () => {
+ mockThorClient.thor.contracts.executeCall.mockResolvedValue({
+ result: {
+ array: []
+ },
+ });
+
+ const result = await getAddress({
+ domain: 'example.vet',
+ thor: mockThorClient.thor,
+ });
+
+ expect(result).toBeUndefined();
+ });
+});
diff --git a/packages/dapp-kit-react/src/hooks/useVechainDomain/api/getAddress.ts b/packages/dapp-kit-react/src/hooks/useVechainDomain/api/getAddress.ts
new file mode 100644
index 00000000..9a3989d8
--- /dev/null
+++ b/packages/dapp-kit-react/src/hooks/useVechainDomain/api/getAddress.ts
@@ -0,0 +1,28 @@
+import { genesisBlocks, VNS_RESOLVER } from '@vechain/dapp-kit';
+import type { DAppKitContext } from '../../../types';
+import { ABIContract } from '@vechain/sdk-core';
+
+/**
+ * Get the address of the domain
+ */
+export const getAddress = async ({
+ domain,
+ thor,
+}: {
+ domain: string | null;
+ thor: DAppKitContext['thor'];
+}): Promise => {
+ if (!domain) return undefined;
+
+ const genesisId = await thor.blocks.getGenesisBlock();
+
+ const resolver =
+ genesisId?.id === genesisBlocks.test.id
+ ? VNS_RESOLVER.test
+ : VNS_RESOLVER.main;
+
+ const res = await thor.contracts.executeCall(resolver, ABIContract.ofAbi(VNS_RESOLVER.abi).getFunction('getAddresses'), [domain]);
+ const resArray = res.result.array as string[];
+
+ return (resArray[0] as string) || undefined;
+};
diff --git a/packages/dapp-kit-react/src/hooks/useVechainDomain/api/getDomain.test.ts b/packages/dapp-kit-react/src/hooks/useVechainDomain/api/getDomain.test.ts
new file mode 100644
index 00000000..27049fa0
--- /dev/null
+++ b/packages/dapp-kit-react/src/hooks/useVechainDomain/api/getDomain.test.ts
@@ -0,0 +1,89 @@
+import { describe, it, expect, vi } from 'vitest';
+import { getDomain } from './getDomain';
+import { genesisBlocks, VNS_RESOLVER } from '@vechain/dapp-kit';
+import { ABIContract } from '@vechain/sdk-core';
+
+describe('getDomain', () => {
+ const mockThorClient = {
+ thor: {
+ blocks: {
+ getGenesisBlock: vi.fn(),
+ },
+ contracts: {
+ executeCall: vi.fn(),
+ },
+ },
+ } as any;
+
+ it('should return null if address is null', async () => {
+ const result = await getDomain({ address: null, thor: mockThorClient });
+ expect(result).toBeUndefined();
+ });
+
+ it('should use main resolver for mainnet', async () => {
+ mockThorClient.thor.blocks.getGenesisBlock.mockResolvedValue({ id: genesisBlocks.main.id });
+ mockThorClient.thor.contracts.executeCall.mockResolvedValue({
+ result: {
+ array: ['example.vet']
+ },
+ });
+
+ const result = await getDomain({
+ address: '0x1234567890123456789012345678901234567890',
+ thor: mockThorClient.thor,
+ });
+
+ expect(mockThorClient.thor.contracts.executeCall).toHaveBeenCalledWith(VNS_RESOLVER.main, ABIContract.ofAbi(VNS_RESOLVER.abi).getFunction('getNames'), ["0x1234567890123456789012345678901234567890"]);
+
+ expect(result).toBe('example.vet');
+ });
+
+ it('should use test resolver for testnet', async () => {
+ mockThorClient.thor.blocks.getGenesisBlock.mockResolvedValue({ id: genesisBlocks.test.id });
+ mockThorClient.thor.contracts.executeCall.mockResolvedValue({
+ result: {
+ array: ['example.vet']
+ },
+ });
+
+ const result = await getDomain({
+ address: '0x1234567890123456789012345678901234567890',
+ thor: mockThorClient.thor,
+ });
+
+ expect(mockThorClient.thor.contracts.executeCall).toHaveBeenCalledWith(VNS_RESOLVER.test, ABIContract.ofAbi(VNS_RESOLVER.abi).getFunction('getNames'), ["0x1234567890123456789012345678901234567890"]);
+
+ expect(result).toBe('example.vet');
+ });
+
+ it('should return the first name from the resolved names', async () => {
+ const expectedDomain = 'example.vet';
+ mockThorClient.thor.contracts.executeCall.mockResolvedValue({
+ result: {
+ array: [expectedDomain]
+ },
+ });
+
+ const result = await getDomain({
+ address: '0x1234567890123456789012345678901234567890',
+ thor: mockThorClient.thor,
+ });
+
+ expect(result).toBe(expectedDomain);
+ });
+
+ it('should return null if no names are resolved', async () => {
+ mockThorClient.thor.contracts.executeCall.mockResolvedValue({
+ result: {
+ array: []
+ },
+ });
+
+ const result = await getDomain({
+ address: '0x1234567890123456789012345678901234567890',
+ thor: mockThorClient.thor,
+ });
+
+ expect(result).toBeUndefined();
+ });
+});
diff --git a/packages/dapp-kit-react/src/hooks/useVechainDomain/api/getDomain.ts b/packages/dapp-kit-react/src/hooks/useVechainDomain/api/getDomain.ts
new file mode 100644
index 00000000..c0318ed3
--- /dev/null
+++ b/packages/dapp-kit-react/src/hooks/useVechainDomain/api/getDomain.ts
@@ -0,0 +1,28 @@
+import { genesisBlocks, VNS_RESOLVER } from '@vechain/dapp-kit';
+import type { DAppKitContext } from '../../../types';
+import { ABIContract } from '@vechain/sdk-core';
+
+/**
+ * Get the domain of an account
+ */
+export const getDomain = async ({
+ address,
+ thor,
+}: {
+ address?: string | null;
+ thor: DAppKitContext['thor'];
+}): Promise => {
+ if (!address) return undefined;
+
+ const genesisId = await thor.blocks.getGenesisBlock();
+
+ const resolver =
+ genesisId?.id === genesisBlocks.test.id
+ ? VNS_RESOLVER.test
+ : VNS_RESOLVER.main;
+
+ const res = await thor.contracts.executeCall(resolver, ABIContract.ofAbi(VNS_RESOLVER.abi).getFunction('getNames'), [address]);
+ const resArray = res.result.array as string[];
+
+ return (resArray[0] as string) || undefined;
+};
diff --git a/packages/dapp-kit-react/src/hooks/useVechainDomain/index.ts b/packages/dapp-kit-react/src/hooks/useVechainDomain/index.ts
new file mode 100644
index 00000000..98a863f9
--- /dev/null
+++ b/packages/dapp-kit-react/src/hooks/useVechainDomain/index.ts
@@ -0,0 +1 @@
+export * from './useVechainDomain';
diff --git a/packages/dapp-kit-react/src/hooks/useVechainDomain/useVechainDomain.catch.test.tsx b/packages/dapp-kit-react/src/hooks/useVechainDomain/useVechainDomain.catch.test.tsx
new file mode 100644
index 00000000..c763a391
--- /dev/null
+++ b/packages/dapp-kit-react/src/hooks/useVechainDomain/useVechainDomain.catch.test.tsx
@@ -0,0 +1,31 @@
+import { renderHook, waitFor } from '@testing-library/react';
+import { describe, it, expect, vi } from 'vitest';
+import { useVechainDomain } from './useVechainDomain';
+import { wrapper } from '../../../test';
+
+vi.mock('./api/fetchVechainDomain', () => ({
+ ...vi.importActual('./api/fetchVechainDomain'),
+ fetchVechainDomain: vi.fn().mockImplementation(async () => {
+ throw new Error('Network error');
+ }),
+}));
+
+describe('useVechainDomain error handling', () => {
+ it('should handle error when fetching domain', async () => {
+ const { result } = renderHook(
+ () => useVechainDomain({ addressOrDomain: 'test.vet' }),
+ { wrapper },
+ );
+
+ expect(result.current.isLoading).toBe(true);
+
+ await waitFor(() => {
+ expect(result.current).toEqual({
+ address: undefined,
+ domain: undefined,
+ isLoading: false,
+ isValidAddressOrDomain: false,
+ });
+ });
+ });
+});
diff --git a/packages/dapp-kit-react/src/hooks/useVechainDomain/useVechainDomain.test.tsx b/packages/dapp-kit-react/src/hooks/useVechainDomain/useVechainDomain.test.tsx
new file mode 100644
index 00000000..8073a2ae
--- /dev/null
+++ b/packages/dapp-kit-react/src/hooks/useVechainDomain/useVechainDomain.test.tsx
@@ -0,0 +1,182 @@
+import { renderHook, waitFor } from '@testing-library/react';
+import { describe, it, expect, vi } from 'vitest';
+import { useVechainDomain } from './useVechainDomain';
+import { wrapper } from '../../../test';
+
+vi.mock('./api/getDomain', () => ({
+ getDomain: vi.fn().mockImplementation(({ address }) => {
+ if (address === '0x1234567890123456789012345678901234567890') {
+ return Promise.resolve('test.vet');
+ }
+ if (address === '0xERROR') {
+ return Promise.reject(new Error('Network error'));
+ }
+ if (address === '0x0000000000000000000000000000000000000000') {
+ return Promise.reject(new Error('Network error'));
+ }
+ return Promise.resolve(null);
+ }),
+}));
+
+vi.mock('./api/getAddress', () => ({
+ getAddress: vi.fn().mockImplementation(({ domain }) => {
+ if (domain === 'test.vet') {
+ return Promise.resolve(
+ '0x1234567890123456789012345678901234567890',
+ );
+ }
+ if (domain === 'invalid.vet') {
+ return Promise.resolve(
+ '0x0000000000000000000000000000000000000000',
+ );
+ }
+ if (domain === 'error.vet' || domain === '0xERROR') {
+ return Promise.reject(new Error('Network error'));
+ }
+ return Promise.resolve(null);
+ }),
+}));
+
+describe('useVechainDomain', () => {
+ it('should return initial state', async () => {
+ const { result } = renderHook(
+ () => useVechainDomain({ addressOrDomain: null }),
+ {
+ wrapper,
+ },
+ );
+ await waitFor(() => {
+ expect(result.current).toEqual({
+ address: undefined,
+ domain: undefined,
+ isLoading: false,
+ isValidAddressOrDomain: false,
+ });
+ });
+ });
+
+ it('should handle valid address input', async () => {
+ const { result } = renderHook(
+ () =>
+ useVechainDomain({
+ addressOrDomain:
+ '0x1234567890123456789012345678901234567890',
+ }),
+ { wrapper },
+ );
+
+ expect(result.current.isLoading).toBe(true);
+
+ await waitFor(() => {
+ expect(result.current).toEqual({
+ address: '0x1234567890123456789012345678901234567890',
+ domain: 'test.vet',
+ isLoading: false,
+ isValidAddressOrDomain: true,
+ });
+ });
+ });
+
+ it('should handle valid domain input', async () => {
+ const { result } = renderHook(
+ () => useVechainDomain({ addressOrDomain: 'test.vet' }),
+ {
+ wrapper,
+ },
+ );
+
+ expect(result.current.isLoading).toBe(true);
+
+ await waitFor(() => {
+ expect(result.current).toEqual({
+ address: '0x1234567890123456789012345678901234567890',
+ domain: 'test.vet',
+ isLoading: false,
+ isValidAddressOrDomain: true,
+ });
+ });
+ });
+
+ it('should handle invalid domain input', async () => {
+ const { result } = renderHook(
+ () => useVechainDomain({ addressOrDomain: 'invalid.vet' }),
+ {
+ wrapper,
+ },
+ );
+
+ expect(result.current.isLoading).toBe(true);
+
+ await waitFor(() => {
+ expect(result.current).toEqual({
+ address: undefined,
+ domain: undefined,
+ isLoading: false,
+ isValidAddressOrDomain: false,
+ });
+ });
+ });
+
+ it('should handle error when getting domain', async () => {
+ const { result } = renderHook(
+ () => useVechainDomain({ addressOrDomain: '0xERROR' }),
+ {
+ wrapper,
+ },
+ );
+
+ expect(result.current.isLoading).toBe(true);
+
+ await waitFor(() => {
+ expect(result.current).toEqual({
+ address: undefined,
+ domain: undefined,
+ isLoading: false,
+ isValidAddressOrDomain: false,
+ });
+ });
+ });
+
+ it('should handle error when getting address', async () => {
+ const { result } = renderHook(
+ () => useVechainDomain({ addressOrDomain: 'error.vet' }),
+ {
+ wrapper,
+ },
+ );
+
+ expect(result.current.isLoading).toBe(true);
+
+ await waitFor(() => {
+ expect(result.current).toEqual({
+ address: undefined,
+ domain: undefined,
+ isLoading: false,
+ isValidAddressOrDomain: false,
+ });
+ });
+ });
+ it('should handle error when getting zero address', async () => {
+ const { result } = renderHook(
+ () =>
+ useVechainDomain({
+ addressOrDomain:
+ '0x0000000000000000000000000000000000000000',
+ }),
+ {
+ wrapper,
+ },
+ );
+
+ expect(result.current.isLoading).toBe(true);
+
+ await waitFor(() => {
+ expect(result.current).toEqual({
+ address: '0x0000000000000000000000000000000000000000',
+ domain: undefined,
+ isLoading: false,
+ isValidAddressOrDomain: true,
+ });
+ });
+ });
+});
diff --git a/packages/dapp-kit-react/src/hooks/useVechainDomain/useVechainDomain.ts b/packages/dapp-kit-react/src/hooks/useVechainDomain/useVechainDomain.ts
new file mode 100644
index 00000000..7489bfa1
--- /dev/null
+++ b/packages/dapp-kit-react/src/hooks/useVechainDomain/useVechainDomain.ts
@@ -0,0 +1,44 @@
+import { useEffect, useState } from 'react';
+import { useThor } from '../../DAppKitProvider/hooks/useThor';
+import {
+ fetchVechainDomain,
+ type VechainDomainResult,
+} from './api/fetchVechainDomain';
+
+interface UseVechainDomainReturnType extends VechainDomainResult {
+ isLoading: boolean;
+}
+
+/**
+ * Hook to get the domain of an account and vice versa by passing the connex object
+ */
+export const useVechainDomain = ({
+ addressOrDomain,
+}: {
+ addressOrDomain?: string | null;
+}): UseVechainDomainReturnType => {
+ const thor = useThor();
+ const [result, setResult] = useState({
+ address: undefined,
+ domain: undefined,
+ isValidAddressOrDomain: false,
+ });
+ const [isLoading, setIsLoading] = useState(false);
+
+ useEffect(() => {
+ setIsLoading(true);
+ fetchVechainDomain({ addressOrDomain, thor })
+ .then(setResult)
+ .catch((err) => {
+ console.error('Error fetching vechain domain: ', err);
+ setResult({
+ address: undefined,
+ domain: undefined,
+ isValidAddressOrDomain: false,
+ });
+ })
+ .finally(() => setIsLoading(false));
+ }, [addressOrDomain, thor]);
+
+ return { ...result, isLoading };
+};
diff --git a/packages/dapp-kit-react/src/index.ts b/packages/dapp-kit-react/src/index.ts
index 92010e34..b431e5f5 100644
--- a/packages/dapp-kit-react/src/index.ts
+++ b/packages/dapp-kit-react/src/index.ts
@@ -1,3 +1,4 @@
export * from './DAppKitProvider';
export * from './types';
export * from './WalletButton';
+export * from './hooks';
diff --git a/packages/dapp-kit-react/src/types.ts b/packages/dapp-kit-react/src/types.ts
index 65e5f060..cc337590 100644
--- a/packages/dapp-kit-react/src/types.ts
+++ b/packages/dapp-kit-react/src/types.ts
@@ -2,7 +2,7 @@ import type React from 'react';
import type {
ConnectResponse,
VeChainSignerDAppKit,
- WalletSource,
+ WalletSource
} from '@vechain/dapp-kit';
import { type DAppKitUIOptions } from '@vechain/dapp-kit-ui';
import type { CertificateData } from '@vechain/sdk-core';
@@ -38,6 +38,8 @@ export interface DAppKitContext {
disconnect: () => void;
connect: () => Promise;
account: string | null;
+ accountDomain: string | null;
+ isAccountDomainLoading: boolean;
signer: VeChainSignerDAppKit | undefined;
source: WalletSource | null;
connectionCertificate: CertificateData | null;
diff --git a/packages/dapp-kit-react/test/helpers/index.ts b/packages/dapp-kit-react/test/helpers/index.ts
new file mode 100644
index 00000000..a3d2ef77
--- /dev/null
+++ b/packages/dapp-kit-react/test/helpers/index.ts
@@ -0,0 +1,2 @@
+export * from './react-test-helpers';
+export * from './mocked-signer';
diff --git a/packages/dapp-kit-react/test/index.ts b/packages/dapp-kit-react/test/index.ts
new file mode 100644
index 00000000..c5f595cf
--- /dev/null
+++ b/packages/dapp-kit-react/test/index.ts
@@ -0,0 +1 @@
+export * from './helpers';
diff --git a/packages/dapp-kit-react/tsconfig.json b/packages/dapp-kit-react/tsconfig.json
index ddad55aa..2bc962ed 100644
--- a/packages/dapp-kit-react/tsconfig.json
+++ b/packages/dapp-kit-react/tsconfig.json
@@ -17,8 +17,7 @@
"skipLibCheck": true,
"strict": true,
// React
- "jsx": "react-jsx",
- "typeRoots": ["node_modules/@types"]
+ "jsx": "react-jsx"
},
- "include": ["src/**/*.ts*", "test/**/*.test.ts*"]
+ "include": ["src/**/*.ts", "test/**/*.test.ts", "eslint.config.mjs"]
}
diff --git a/packages/dapp-kit-react/vite.config.ts b/packages/dapp-kit-react/vite.config.ts
index 9f955e09..d9cb36fb 100644
--- a/packages/dapp-kit-react/vite.config.ts
+++ b/packages/dapp-kit-react/vite.config.ts
@@ -4,7 +4,12 @@ import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
- include: ['test/**/*.test.ts', 'test/**/*.test.tsx'],
+ include: [
+ 'src/**/*.test.ts',
+ 'src/**/*.test.tsx',
+ 'test/**/*.test.ts',
+ 'test/**/*.test.tsx',
+ ],
environment: 'happy-dom',
reporters: 'dot',
setupFiles: [resolve(__dirname, 'test/setup/setup.ts')],
diff --git a/packages/dapp-kit-ui/.eslintignore b/packages/dapp-kit-ui/.eslintignore
deleted file mode 100644
index 59b446e0..00000000
--- a/packages/dapp-kit-ui/.eslintignore
+++ /dev/null
@@ -1,6 +0,0 @@
-node_modules/*
-dist/*
-web-dev-server.config.js
-tsup.config.ts
-test
-vite.config.ts
diff --git a/packages/dapp-kit-ui/.eslintrc.mjs b/packages/dapp-kit-ui/.eslintrc.mjs
deleted file mode 100644
index b53b0fd4..00000000
--- a/packages/dapp-kit-ui/.eslintrc.mjs
+++ /dev/null
@@ -1,9 +0,0 @@
-const Config = require('@vechain/repo-config');
-
-module.exports = {
- ...Config.EslintReact,
- rules: {
- ...Config.EslintReact.rules,
- 'import/no-extraneous-dependencies': 'error',
- },
-};
diff --git a/packages/dapp-kit-ui/eslint.config.mjs b/packages/dapp-kit-ui/eslint.config.mjs
new file mode 100644
index 00000000..c9e3ec07
--- /dev/null
+++ b/packages/dapp-kit-ui/eslint.config.mjs
@@ -0,0 +1,17 @@
+import tseslint from 'typescript-eslint';
+
+export default tseslint.config({
+ ignores: ['**/*.config.ts', 'dist/**'],
+ extends: [...tseslint.configs.recommended],
+ files: ['**/*.{ts,tsx}'],
+ rules: {
+ '@typescript-eslint/no-explicit-any': 'off',
+ '@typescript-eslint/ban-ts-comment': 'off',
+ 'no-console': ['error', { allow: ['error'] }],
+ 'eslint-comments/no-unused-disable': 'off',
+ '@typescript-eslint/no-unused-vars': [
+ 'error',
+ { argsIgnorePattern: '^_' },
+ ],
+ },
+});
diff --git a/packages/dapp-kit-ui/index.js b/packages/dapp-kit-ui/index.js
index b862eb26..f1e46894 100644
--- a/packages/dapp-kit-ui/index.js
+++ b/packages/dapp-kit-ui/index.js
@@ -1,6 +1,4 @@
-// eslint-disable-next-line eslint-comments/disable-enable-pair
-/* eslint-disable no-undef */
-import { DAppKitUI } from './dist';
+import { DAppKitUI, friendlyAddress } from './dist';
const walletConnectOptions = {
projectId: 'a0b855ceaf109dbc8426479a4c3d38d8',
@@ -13,7 +11,7 @@ const walletConnectOptions = {
};
const vechainDAppKitOptions = {
- nodeUrl: 'https://testnet.vechain.org/',
+ nodeUrl: 'https://mainnet.vechain.org',
walletConnectOptions,
usePersistence: true,
};
@@ -24,21 +22,33 @@ DAppKitUI.configure(vechainDAppKitOptions);
const customButton = document.getElementById('custom-button');
-customButton.addEventListener('click', async () => {
- DAppKitUI.modal.open();
-});
-
-const handleConnected = (address) => {
- if (address) {
- const formattedAddress = `${address.slice(0, 6)}...${address.slice(
- -4,
- )}`;
- customButton.innerText = `Disconnect from ${formattedAddress}`;
- } else {
- customButton.innerText = 'Connect Custom Button';
- }
-};
-
-handleConnected(DAppKitUI.wallet.state.address);
-
-DAppKitUI.modal.onConnectionStatusChange(handleConnected);
+if (customButton) {
+ customButton.addEventListener('click', () => {
+ DAppKitUI.modal.open();
+ });
+
+ const render = () => {
+ const address = DAppKitUI.wallet.state.address;
+ const accountDomain = DAppKitUI.wallet.state.accountDomain;
+ const isAccountDomainLoading =
+ DAppKitUI.wallet.state.isAccountDomainLoading;
+
+ const addressOrDomain =
+ accountDomain && !isAccountDomainLoading
+ ? accountDomain
+ : friendlyAddress(address || '');
+
+ if (address) {
+ customButton.innerText = `Disconnect from ${addressOrDomain}`;
+ } else {
+ customButton.innerText = 'Connect Custom Button';
+ }
+ };
+
+ render();
+
+ DAppKitUI.modal.onConnectionStatusChange(render);
+ DAppKitUI.wallet.subscribeToKey('address', render);
+ DAppKitUI.wallet.subscribeToKey('accountDomain', render);
+ DAppKitUI.wallet.subscribeToKey('isAccountDomainLoading', render);
+}
diff --git a/packages/dapp-kit-ui/package.json b/packages/dapp-kit-ui/package.json
index e135da42..2f4b7bfb 100644
--- a/packages/dapp-kit-ui/package.json
+++ b/packages/dapp-kit-ui/package.json
@@ -1,6 +1,6 @@
{
"name": "@vechain/dapp-kit-ui",
- "version": "1.0.11",
+ "version": "1.1.1",
"description": "Vanilla JS DAppKit",
"keywords": [
"web-components",
@@ -26,8 +26,7 @@
"build": "tsup",
"clean": "rm -rf dist .turbo",
"dev": "rm -rf ../../.parcel-cache; parcel --no-cache index.html",
- "format": "prettier \"**/*.{cjs,html,js,json,md,ts}\" --ignore-path ./.eslintignore --write",
- "lint": "tsc --noEmit && eslint src --ext .js,.jsx,.ts,.tsx",
+ "lint": "eslint",
"purge": "yarn clean && rm -rf node_modules",
"test": "vitest run --coverage",
"test:dev": "vitest run ",
@@ -50,15 +49,15 @@
"@types/qrcode": "^1.5.5",
"@typescript-eslint/eslint-plugin": "^5.25.0",
"@typescript-eslint/parser": "^5.25.0",
- "@vechain/repo-config": "https://github.com/vechain/repo-config#v0.0.1",
"@vitest/coverage-v8": "^0.34.6",
- "eslint": "^8.15.0",
+ "eslint": "^9.12.0",
"parcel": "^2.10.2",
"prettier": "^2.6.2",
"punycode": "^1.4.1",
"tsup": "^7.2.0",
"typechain": "^8.3.2",
"typescript": "~5.2.0",
+ "typescript-eslint": "^8.11.0",
"vite": "^4.5.5",
"vitest": "^0.34.6"
}
diff --git a/packages/dapp-kit-ui/src/client.ts b/packages/dapp-kit-ui/src/client.ts
index aa0d42ad..a875d61f 100644
--- a/packages/dapp-kit-ui/src/client.ts
+++ b/packages/dapp-kit-ui/src/client.ts
@@ -74,7 +74,6 @@ export const DAppKitUI = {
get(): DAppKit {
if (!dappKit) {
- // eslint-disable-next-line no-console
console.error('🚨🚨🚨 DAppKitUI not configured 🚨🚨🚨');
throw new Error('DAppKitUI not configured');
}
diff --git a/packages/dapp-kit-ui/src/components/buttons/address-button.ts b/packages/dapp-kit-ui/src/components/buttons/address-button.ts
index ef9d6f6e..322d981d 100644
--- a/packages/dapp-kit-ui/src/components/buttons/address-button.ts
+++ b/packages/dapp-kit-ui/src/components/buttons/address-button.ts
@@ -4,6 +4,7 @@ import { Font, type ThemeMode } from '../../constants';
import { friendlyAddress, getPicassoImage } from '../../utils/account';
import { buttonStyle } from '../../assets/styles';
import { DAppKitUI } from '../../client';
+import { shortenedDomain } from '@vechain/dapp-kit';
@customElement('vdk-address-button')
export class AddressButton extends LitElement {
@@ -49,6 +50,12 @@ export class AddressButton extends LitElement {
@property()
address?: string;
+ @property()
+ accountDomain = '';
+
+ @property()
+ isAccountDomainLoading = false;
+
@property()
mode: ThemeMode = 'LIGHT';
@@ -77,6 +84,11 @@ export class AddressButton extends LitElement {
/>
`;
}
+
+ const addressOrDomain =
+ this.accountDomain && !this.isAccountDomainLoading
+ ? shortenedDomain(this.accountDomain, 7)
+ : friendlyAddress(this.address ?? '');
return html`
- ${friendlyAddress(this.address ?? '')}
+ ${addressOrDomain}
`;
}
}
diff --git a/packages/dapp-kit-ui/src/components/buttons/button.ts b/packages/dapp-kit-ui/src/components/buttons/button.ts
index f9457e55..d31eb86e 100644
--- a/packages/dapp-kit-ui/src/components/buttons/button.ts
+++ b/packages/dapp-kit-ui/src/components/buttons/button.ts
@@ -36,6 +36,10 @@ export class Button extends LitElement {
private setAddressFromState(): void {
this.address = DAppKitUI.wallet.state.address ?? '';
+ this.accountDomain = DAppKitUI.wallet.state.accountDomain ?? '';
+ this.isAccountDomainLoading = Boolean(
+ DAppKitUI.wallet.state.isAccountDomainLoading,
+ );
this.requestUpdate();
}
@@ -44,6 +48,11 @@ export class Button extends LitElement {
this.i18n = DAppKitUI.configuration?.i18n ?? defaultI18n;
this.language = DAppKitUI.configuration?.language ?? 'en';
this.address = DAppKitUI.wallet.state.address ?? '';
+ this.accountDomain = DAppKitUI.wallet.state.accountDomain ?? '';
+ this.isAccountDomainLoading = Boolean(
+ DAppKitUI.wallet.state.isAccountDomainLoading,
+ );
+
this.requestUpdate();
}
@@ -55,6 +64,20 @@ export class Button extends LitElement {
this.requestUpdate();
},
);
+ DAppKitUI.wallet.subscribeToKey(
+ 'accountDomain',
+ (_accountDomain: string | null) => {
+ this.accountDomain = _accountDomain ?? '';
+ this.requestUpdate();
+ },
+ );
+ DAppKitUI.wallet.subscribeToKey(
+ 'isAccountDomainLoading',
+ (_isAccountDomainLoading: boolean) => {
+ this.isAccountDomainLoading = _isAccountDomainLoading;
+ this.requestUpdate();
+ },
+ );
}
@property()
@@ -69,6 +92,12 @@ export class Button extends LitElement {
@property()
address = '';
+ @property()
+ accountDomain = '';
+
+ @property()
+ isAccountDomainLoading = false;
+
@property()
disabled = false;
@@ -80,6 +109,8 @@ export class Button extends LitElement {
? html``
diff --git a/packages/dapp-kit-ui/src/components/modals/address-modal.ts b/packages/dapp-kit-ui/src/components/modals/address-modal.ts
index 641c4b90..229f7552 100644
--- a/packages/dapp-kit-ui/src/components/modals/address-modal.ts
+++ b/packages/dapp-kit-ui/src/components/modals/address-modal.ts
@@ -21,6 +21,7 @@ import {
LightDisconnectSvg,
} from '../../assets/icons';
import { DAppKitUI } from '../../client';
+import { shortenedDomain } from '@vechain/dapp-kit';
let openWalletModalListener: () => void;
let closeWalletModalListener: () => void;
@@ -87,7 +88,7 @@ export class AddressModal extends LitElement {
);
}
- .address {
+ .address-domain {
font-size: var(--vdk-font-size-large, ${Font.Size.Large});
font-family: var(--vdk-font-family, ${Font.Family});
font-weight: var(
@@ -99,12 +100,39 @@ export class AddressModal extends LitElement {
justify-content: center;
}
+ .secondary-address {
+ font-size: var(--vdk-font-size-small, ${Font.Size.Small});
+ font-family: var(--vdk-font-family, ${Font.Family});
+ font-weight: var(
+ --vdk-font-weight-regular,
+ ${Font.Weight.Regular}
+ );
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
+ }
+
+ .address-container {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ gap: 10px;
+ }
+
.copy-icon {
cursor: pointer;
width: 20px;
height: 20px;
margin-left: 10px;
}
+
+ .copy-icon-secondary {
+ cursor: pointer;
+ width: 15px;
+ height: 15px;
+ margin-left: 8px;
+ }
`,
];
@@ -114,6 +142,12 @@ export class AddressModal extends LitElement {
@property({ type: String })
address = '';
+ @property()
+ accountDomain = '';
+
+ @property()
+ isAccountDomainLoading = false;
+
@property({ type: Function })
onDisconnectClick?: () => void = undefined;
@@ -130,7 +164,10 @@ export class AddressModal extends LitElement {
walletConnectQRcode?: string = undefined;
@property()
- showCopiedIcon = false;
+ showCopiedMainIcon = false;
+
+ @property()
+ showCopiedSecondaryIcon = false;
constructor() {
super();
@@ -161,10 +198,19 @@ export class AddressModal extends LitElement {
override render(): TemplateResult {
const translate = useTranslate(this.i18n, this.language);
- let copyIcon = this.mode === 'LIGHT' ? LightCopySvg : DarkCopySvg;
- if (this.showCopiedIcon) {
- copyIcon = CheckSvg;
+ let copyMainIcon = this.mode === 'LIGHT' ? LightCopySvg : DarkCopySvg;
+ if (this.showCopiedMainIcon) {
+ copyMainIcon = CheckSvg;
}
+ let copySecondaryIcon =
+ this.mode === 'LIGHT' ? LightCopySvg : DarkCopySvg;
+ if (this.showCopiedSecondaryIcon) {
+ copySecondaryIcon = CheckSvg;
+ }
+ const addressOrDomain =
+ this.accountDomain && !this.isAccountDomainLoading
+ ? shortenedDomain(this.accountDomain, 18)
+ : friendlyAddress(this.address || '');
return html`
-
- ${friendlyAddress(this.address)}
+
+
+ ${addressOrDomain}
${copyIcon}
-
-
+ this.onCopyMainLabel
+ }>${copyMainIcon}
+
+ ${
+ this.accountDomain
+ ? html`
+ (${friendlyAddress(this.address, 8, 7)})
+
+ ${copySecondaryIcon}
+
+ `
+ : nothing
+ }
+