├── packages └── walletkit │ ├── src │ ├── controllers │ │ ├── index.ts │ │ └── engine.ts │ ├── utils │ │ ├── index.ts │ │ └── notifications.ts │ ├── constants │ │ ├── request.ts │ │ ├── index.ts │ │ └── client.ts │ ├── types │ │ ├── index.ts │ │ ├── engine.ts │ │ └── client.ts │ ├── index.ts │ └── client.ts │ ├── test │ ├── shared │ │ ├── index.ts │ │ ├── helpers.ts │ │ └── values.ts │ ├── multitenancy.spec.ts │ └── sign.spec.ts │ ├── rollup.config.js │ ├── tsconfig.json │ ├── .npmignore │ ├── package.json │ ├── README.md │ ├── CHANGELOG.md │ └── LICENSE.md ├── .prettierrc ├── lerna.json ├── vitest.config.ts ├── .changeset ├── config.json └── README.md ├── .github ├── ISSUE_TEMPLATE │ ├── config.yml │ ├── feature_request.md │ └── bug_report.md ├── workflows │ ├── cta.yml │ ├── publish.yml │ ├── publish_canary.yml │ └── pr_checks.yml ├── pull_request_template.md └── pr_template_release.md ├── renovate.json ├── .gitignore ├── tsconfig.json ├── rollup.config.js ├── README.md ├── .eslintrc ├── package.json └── LICENSE.md /packages/walletkit/src/controllers/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./engine.js"; 2 | -------------------------------------------------------------------------------- /packages/walletkit/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./notifications.js"; 2 | -------------------------------------------------------------------------------- /packages/walletkit/src/constants/request.ts: -------------------------------------------------------------------------------- 1 | export const REQUEST_CONTEXT = "request"; 2 | -------------------------------------------------------------------------------- /packages/walletkit/src/types/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./client.js"; 2 | export * from "./engine.js"; 3 | -------------------------------------------------------------------------------- /packages/walletkit/src/constants/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./client.js"; 2 | export * from "./request.js"; 3 | -------------------------------------------------------------------------------- /packages/walletkit/test/shared/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./values.js"; 2 | export * from "./helpers.js"; 3 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 2, 3 | "useTabs": false, 4 | "trailingComma": "all", 5 | "printWidth": 100 6 | } 7 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "npmClient": "npm", 3 | "packages": [ 4 | "packages/walletkit" 5 | ], 6 | "version": "1.2.3" 7 | } 8 | -------------------------------------------------------------------------------- /packages/walletkit/rollup.config.js: -------------------------------------------------------------------------------- 1 | import { name, dependencies } from "./package.json"; 2 | import createConfig from "../../rollup.config"; 3 | 4 | export default createConfig(name, Object.keys(dependencies)); 5 | -------------------------------------------------------------------------------- /packages/walletkit/src/index.ts: -------------------------------------------------------------------------------- 1 | import { WalletKit as Client } from "./client.js"; 2 | 3 | export * from "./constants/index.js"; 4 | export * from "./types/index.js"; 5 | 6 | export const WalletKit = Client; 7 | export default Client; 8 | -------------------------------------------------------------------------------- /packages/walletkit/test/shared/helpers.ts: -------------------------------------------------------------------------------- 1 | import { ICore } from "@walletconnect/types"; 2 | 3 | export async function disconnect(core: ICore) { 4 | if (core.relayer.connected) { 5 | await core.relayer.transportClose(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/walletkit/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": ["./src/**/*"], 4 | "compilerOptions": { 5 | "rootDir": "src", 6 | "outDir": "./dist/types", 7 | "emitDeclarationOnly": true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vitest/config"; 2 | 3 | export default defineConfig({ 4 | define: { 5 | "process.env.IS_VITEST": true, 6 | "process.env.DISABLE_GLOBAL_CORE": true, 7 | }, 8 | test: { 9 | testTimeout: 800_000, 10 | hookTimeout: 800_000, 11 | }, 12 | }); 13 | -------------------------------------------------------------------------------- /packages/walletkit/src/constants/client.ts: -------------------------------------------------------------------------------- 1 | export const PROTOCOL = "wc"; 2 | export const PROTOCOL_VERSION = 2; 3 | export const CLIENT_CONTEXT = "WalletKit"; 4 | 5 | export const CLIENT_STORAGE_PREFIX = `${PROTOCOL}@${PROTOCOL_VERSION}:${CLIENT_CONTEXT}:`; 6 | 7 | export const CLIENT_STORAGE_OPTIONS = { 8 | database: ":memory:", 9 | }; 10 | -------------------------------------------------------------------------------- /.changeset/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "changelog": ["@changesets/changelog-github", { "repo": "reown-com/reown-walletkit-js" }], 3 | "commit": false, 4 | "fixed": [ 5 | [ 6 | "@reown/walletkit" 7 | ] 8 | ], 9 | "linked": [], 10 | "access": "public", 11 | "baseBranch": "main", 12 | "updateInternalDependencies": "patch" 13 | } 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Ask a question 4 | url: https://github.com/WalletConnect/walletconnect-monorepo/discussions/new 5 | about: Ask questions and discuss with other community members. 6 | - name: View our FAQ 7 | url: https://walletconnect.com/faq 8 | about: See our frequently asked questions 9 | -------------------------------------------------------------------------------- /packages/walletkit/.npmignore: -------------------------------------------------------------------------------- 1 | *.log 2 | npm-debug.log* 3 | 4 | # Coverage directory used by tools like istanbul 5 | coverage 6 | .nyc_output 7 | 8 | # Dependency directories 9 | node_modules 10 | 11 | # npm package lock 12 | package-lock.json 13 | yarn.lock 14 | 15 | # project files 16 | src 17 | test 18 | CHANGELOG.md 19 | .travis.yml 20 | .editorconfig 21 | .eslintignore 22 | .eslintrc 23 | .babelrc 24 | .gitignore 25 | .watchmanconfig 26 | -------------------------------------------------------------------------------- /.changeset/README.md: -------------------------------------------------------------------------------- 1 | # Changesets 2 | 3 | Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works 4 | with multi-package repos, or single-package repos to help you version and publish your code. You can 5 | find the full documentation for it [in our repository](https://github.com/changesets/changesets) 6 | 7 | We have a quick list of common questions to get you started engaging with this project in 8 | [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) 9 | -------------------------------------------------------------------------------- /.github/workflows/cta.yml: -------------------------------------------------------------------------------- 1 | name: "CTA Assistant" 2 | 3 | on: 4 | issue_comment: 5 | types: [created] 6 | pull_request_target: 7 | types: [opened, closed, synchronize] 8 | 9 | permissions: 10 | actions: write 11 | contents: write 12 | pull-requests: write 13 | statuses: write 14 | 15 | jobs: 16 | cta_assistant: 17 | runs-on: ubuntu-latest 18 | steps: 19 | - name: "CTA Assistant" 20 | uses: WalletConnect/actions/github/cta-assistant@9fd44c01c5f8c169349f8822efc93dd08821a628 21 | env: 22 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:base", 5 | ":disableDevDependencies", 6 | ":prConcurrentLimit10", 7 | ":prHourlyLimit2", 8 | ":semanticCommits", 9 | ":semanticCommitScope(deps)" 10 | ], 11 | "updateInternalDeps": true, 12 | "rollbackPrs": false, 13 | "packageRules": [ 14 | { 15 | "matchPackagePatterns": ["*"], 16 | "schedule": ["every weekend"] 17 | }, 18 | { 19 | "matchPackagePatterns": ["@walletconnect/*"], 20 | "schedule": ["at any time"] 21 | } 22 | ], 23 | "ignorePaths": ["**/android/**", "**/ios/**"] 24 | } 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | lerna-debug.log 6 | *.lerna_backup 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | # Dependency directories 14 | **/node_modules 15 | jspm_packages 16 | # Optional npm cache directory 17 | .npm 18 | .eslintcache 19 | .node_repl_history 20 | *.tgz 21 | **/dist 22 | **/build 23 | .DS_Store 24 | .makeFlags* 25 | .env 26 | zip 27 | .idea 28 | .rts2_cache_* 29 | .nyc_output 30 | tsconfig.tsbuildinfo 31 | test.db 32 | test.db-journal 33 | **/tmp 34 | setup 35 | result* 36 | ops/grafana/grafana.ini 37 | .rollup.cache 38 | 39 | # react native module 40 | ## Android/IJ 41 | .classpath 42 | .cxx 43 | .gradle 44 | .idea 45 | .project 46 | .settings 47 | local.properties 48 | android.iml 49 | 50 | ## Xcode 51 | build/ 52 | *.pbxuser 53 | !default.pbxuser 54 | *.mode1v3 55 | !default.mode1v3 56 | *.mode2v3 57 | !default.mode2v3 58 | *.perspectivev3 59 | !default.perspectivev3 60 | xcuserdata 61 | *.xccheckout 62 | *.moved-aside 63 | DerivedData 64 | *.hmap 65 | *.ipa 66 | *.xcuserstate 67 | project.xcworkspace -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: 'type: bug' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **SDK Version (if relevant)** 14 | - Client: [e.g. JS, Swift, Kotlin] 15 | - Version [e.g. 22] 16 | 17 | 18 | **To Reproduce** 19 | Steps to reproduce the behavior: 20 | 1. Go to '...' 21 | 2. Click on '....' 22 | 3. Scroll down to '....' 23 | 4. See error 24 | 25 | **Expected behavior** 26 | A clear and concise description of what you expected to happen. 27 | 28 | **Screenshots** 29 | If applicable, add screenshots to help explain your problem. 30 | 31 | **Desktop (please complete the following information):** 32 | - OS: [e.g. iOS] 33 | - Browser [e.g. chrome, safari] 34 | - Version [e.g. 22] 35 | 36 | **Smartphone (please complete the following information):** 37 | - Device: [e.g. iPhone6] 38 | - OS: [e.g. iOS8.1] 39 | - Browser [e.g. stock browser, safari] 40 | - Version [e.g. 22] 41 | 42 | **Additional context** 43 | Add any other context about the problem here. 44 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": false, 4 | "allowSyntheticDefaultImports": true, 5 | "alwaysStrict": true, 6 | "composite": true, 7 | "declaration": true, 8 | "declarationMap": true, 9 | "downlevelIteration": true, 10 | "emitDecoratorMetadata": true, 11 | "esModuleInterop": true, 12 | "experimentalDecorators": true, 13 | "forceConsistentCasingInFileNames": true, 14 | "importHelpers": true, 15 | "lib": ["ES2020", "DOM"], 16 | "module": "ES2020", 17 | "target": "ES2020", 18 | "moduleResolution": "Node", 19 | "noFallthroughCasesInSwitch": true, 20 | "noImplicitAny": true, 21 | "noImplicitReturns": true, 22 | "noImplicitThis": true, 23 | "noLib": false, 24 | "noUnusedLocals": true, 25 | "noUnusedParameters": true, 26 | "preserveSymlinks": true, 27 | "removeComments": true, 28 | "resolveJsonModule": true, 29 | "skipLibCheck": true, 30 | "sourceMap": true, 31 | "strict": true, 32 | "strictFunctionTypes": true, 33 | "strictNullChecks": true, 34 | "strictPropertyInitialization": true 35 | }, 36 | "include": ["**/package.json", "packages", "providers"] 37 | } 38 | -------------------------------------------------------------------------------- /packages/walletkit/src/utils/notifications.ts: -------------------------------------------------------------------------------- 1 | import { Core } from "@walletconnect/core"; 2 | import { SessionStore } from "@walletconnect/sign-client"; 3 | import { WalletKitTypes } from "../types/index.js"; 4 | 5 | export const Notifications: WalletKitTypes.INotifications = { 6 | decryptMessage: async (params) => { 7 | const instance = { 8 | core: new Core({ 9 | storageOptions: params.storageOptions, 10 | storage: params.storage, 11 | }), 12 | } as any; 13 | await instance.core.crypto.init(); 14 | const decoded = instance.core.crypto.decode(params.topic, params.encryptedMessage); 15 | instance.core = null; 16 | return decoded; 17 | }, 18 | getMetadata: async (params) => { 19 | const instances = { 20 | core: new Core({ 21 | storageOptions: params.storageOptions, 22 | storage: params.storage, 23 | }), 24 | sessionStore: null, 25 | } as any; 26 | instances.sessionStore = new SessionStore(instances.core, instances.core.logger); 27 | await instances.sessionStore.init(); 28 | const session = instances.sessionStore.get(params.topic); 29 | const metadata = session?.peer.metadata; 30 | instances.core = null; 31 | instances.sessionStore = null; 32 | return metadata; 33 | }, 34 | }; 35 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Changesets 2 | on: 3 | push: 4 | branches: [main] 5 | 6 | concurrency: 7 | group: ${{ github.workflow }}-${{ github.ref }} 8 | cancel-in-progress: true 9 | 10 | jobs: 11 | changesets: 12 | name: Changesets 13 | permissions: 14 | contents: write 15 | id-token: write 16 | pull-requests: write 17 | runs-on: ubuntu-latest 18 | timeout-minutes: 5 19 | 20 | steps: 21 | - name: Clone repository 22 | uses: actions/checkout@v4 23 | with: 24 | # This makes Actions fetch all Git history so that Changesets can generate changelogs with the correct commits 25 | fetch-depth: 0 26 | 27 | - name: Setup Node 28 | uses: actions/setup-node@v4 29 | with: 30 | node-version: "20.x" 31 | 32 | - name: Install dependencies and build 🔧 33 | run: npm ci && npm run build 34 | 35 | - name: Setup npm auth token 36 | run: echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" > $HOME/.npmrc 37 | 38 | - name: PR or publish 39 | uses: changesets/action@v1 40 | with: 41 | title: "chore: version packages" 42 | commit: "chore: version packages" 43 | createGithubReleases: ${{ github.ref == 'refs/heads/main' }} 44 | publish: npm run changeset:publish 45 | version: npm run changeset:version 46 | env: 47 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 48 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 49 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import esbuild from "rollup-plugin-esbuild"; 2 | import { nodeResolve } from "@rollup/plugin-node-resolve"; 3 | import commonjs from "@rollup/plugin-commonjs"; 4 | import json from "@rollup/plugin-json"; 5 | 6 | const input = "./src/index.ts"; 7 | const plugins = [ 8 | nodeResolve({ preferBuiltins: false, browser: true }), 9 | json(), 10 | commonjs(), 11 | esbuild({ 12 | minify: true, 13 | tsconfig: "./tsconfig.json", 14 | loaders: { 15 | ".json": "json", 16 | }, 17 | }), 18 | ]; 19 | 20 | export default function createConfig( 21 | packageName, 22 | packageDependencies, 23 | umd = {}, 24 | cjs = {}, 25 | es = {}, 26 | ) { 27 | return [ 28 | { 29 | input, 30 | plugins, 31 | output: { 32 | file: "./dist/index.umd.js", 33 | format: "umd", 34 | exports: "named", 35 | name: packageName, 36 | sourcemap: true, 37 | ...umd, 38 | }, 39 | }, 40 | { 41 | input, 42 | plugins, 43 | external: packageDependencies, 44 | output: [ 45 | { 46 | file: "./dist/index.cjs", 47 | format: "cjs", 48 | exports: "named", 49 | name: packageName, 50 | sourcemap: true, 51 | ...cjs, 52 | }, 53 | { 54 | file: "./dist/index.js", 55 | format: "es", 56 | exports: "named", 57 | name: packageName, 58 | sourcemap: true, 59 | ...es, 60 | }, 61 | ], 62 | }, 63 | ]; 64 | } 65 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Reown-js 2 | 3 | Open protocol for connecting Wallets to Dapps 4 | 5 | ## Setup 6 | 7 | 1. Ensure [nodejs and npm](https://nodejs.org/en/) 8 | 2. Clone the repository 9 | 3. Install all package dependencies, by running `npm install` from the root folder 10 | 11 | ## Running checks for all packages 12 | 13 | To ensure all packages lint, build and test correctly, we can run the following command from the root folder: 14 | 15 | > **For tests to pass in the following command, you will need your own `TEST_PROJECT_ID` value**, 16 | > which will be generated for you when you set up a new project on [WalletConnect Cloud](https://cloud.walletconnect.com). 17 | 18 | ```zsh 19 | TEST_PROJECT_ID=YOUR_PROJECT_ID npm run check 20 | ``` 21 | 22 | ## Command Overview 23 | 24 | - `clean` - Removes build folders from all packages 25 | - `lint` - Runs [eslint](https://eslint.org/) checks 26 | - `prettier` - Runs [prettier](https://prettier.io/) checks 27 | - `build` - Builds all packages 28 | - `test` - Tests all packages 29 | - `check` - Shorthand to run lint, build and test commands 30 | - `reset` - Shorthand to run clean and check commands 31 | 32 | ## Troubleshooting 33 | 34 | 1. If you are experiencing issues with installation ensure you install `npm i -g node-gyp` 35 | 2. You will need to have xcode command line tools installed 36 | 3. If there are issues with xcode command line tools try running 37 | 38 | ```zsh 39 | sudo xcode-select --switch /Library/Developer/CommandLineTools 40 | sudo xcode-select --reset 41 | ``` 42 | 43 | ## License 44 | 45 | Apache 2.0 46 | -------------------------------------------------------------------------------- /.github/workflows/publish_canary.yml: -------------------------------------------------------------------------------- 1 | name: Publish Canary to NPM 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | branch: 7 | description: "Branch to release from" 8 | required: true 9 | default: "main" 10 | canaryVersion: 11 | description: "Custom version (e.g., 1.2.3-canary.42)" 12 | required: true 13 | 14 | jobs: 15 | build: 16 | runs-on: ubuntu-latest 17 | permissions: 18 | contents: write 19 | id-token: write 20 | steps: 21 | - name: Checkout code 22 | uses: actions/checkout@v4 23 | with: 24 | ref: ${{ github.event.inputs.branch }} 25 | 26 | - name: Validate canary version format 🔍 27 | run: | 28 | VERSION="${{ github.event.inputs.canaryVersion }}" 29 | if [[ ! "$VERSION" =~ -canary- ]]; then 30 | echo "❌ Invalid version: '$VERSION'. It must include '-canary-'." 31 | exit 1 32 | fi 33 | 34 | - name: Setup Node 35 | uses: actions/setup-node@v4 36 | with: 37 | node-version: "20.x" 38 | registry-url: "https://registry.npmjs.org" 39 | 40 | - name: Install dependencies and build 🔧 41 | run: npm ci && npm run build 42 | 43 | - name: Set version using Lerna 📦 44 | run: npx lerna version ${{ github.event.inputs.canaryVersion }} --no-push --no-git-tag-version --yes 45 | 46 | - name: Rebuild after version change 🔁 47 | run: npm run build 48 | 49 | - name: Publish canary on NPM 🚀 50 | run: npm run npm-publish:canary 51 | env: 52 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 53 | -------------------------------------------------------------------------------- /packages/walletkit/test/shared/values.ts: -------------------------------------------------------------------------------- 1 | import { CoreTypes } from "@walletconnect/types"; 2 | export const TEST_ETHEREUM_CHAIN = "eip155:1"; 3 | export const TEST_ARBITRUM_CHAIN = "eip155:42161"; 4 | export const TEST_AVALANCHE_CHAIN = "eip155:43114"; 5 | 6 | export const TEST_CHAINS = [TEST_ETHEREUM_CHAIN, TEST_ARBITRUM_CHAIN, TEST_AVALANCHE_CHAIN]; 7 | export const TEST_METHODS = [ 8 | "eth_signTransaction", 9 | "eth_sendTransaction", 10 | "personal_sign", 11 | "eth_signTypedData", 12 | ]; 13 | export const TEST_EVENTS = ["chainChanged", "accountsChanged"]; 14 | 15 | export const TEST_ETHEREUM_ADDRESS = "0x3c582121909DE92Dc89A36898633C1aE4790382b"; 16 | 17 | export const TEST_ETHEREUM_ACCOUNT = `${TEST_ETHEREUM_CHAIN}:${TEST_ETHEREUM_ADDRESS}`; 18 | export const TEST_ARBITRUM_ACCOUNT = `${TEST_ARBITRUM_CHAIN}:${TEST_ETHEREUM_ADDRESS}`; 19 | export const TEST_AVALANCHE_ACCOUNT = `${TEST_AVALANCHE_CHAIN}:${TEST_ETHEREUM_ADDRESS}`; 20 | 21 | export const TEST_ACCOUNTS = [TEST_ETHEREUM_ACCOUNT, TEST_ARBITRUM_ACCOUNT, TEST_AVALANCHE_ACCOUNT]; 22 | 23 | export const TEST_NAMESPACES = { 24 | eip155: { 25 | methods: [TEST_METHODS[0]], 26 | accounts: [TEST_ACCOUNTS[0]], 27 | events: [TEST_EVENTS[0]], 28 | }, 29 | }; 30 | 31 | export const TEST_UPDATED_NAMESPACES = { 32 | eip155: { 33 | methods: TEST_METHODS, 34 | accounts: TEST_ACCOUNTS, 35 | events: TEST_EVENTS, 36 | }, 37 | }; 38 | 39 | export const TEST_REQUIRED_NAMESPACES = { 40 | eip155: { 41 | methods: [TEST_METHODS[0]], 42 | chains: [TEST_CHAINS[0]], 43 | events: [TEST_EVENTS[0]], 44 | }, 45 | }; 46 | 47 | export const TEST_CORE_OPTIONS = { 48 | projectId: process.env.TEST_PROJECT_ID || "", 49 | logger: "fatal", 50 | relayUrl: process.env.TEST_RELAY_URL || "", 51 | }; 52 | 53 | export const TEST_METADATA: CoreTypes.Metadata = { 54 | name: "test", 55 | description: "test", 56 | url: "test", 57 | icons: [], 58 | }; 59 | -------------------------------------------------------------------------------- /packages/walletkit/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@reown/walletkit", 3 | "description": "WalletKit for WalletConnect Protocol", 4 | "version": "1.4.1", 5 | "private": false, 6 | "author": "Reown, Inc.", 7 | "homepage": "https://github.com/reown-com/", 8 | "license": "SEE LICENSE IN LICENSE.md", 9 | "main": "dist/index.cjs", 10 | "module": "dist/index.js", 11 | "unpkg": "dist/index.umd.js", 12 | "types": "dist/types/index.d.ts", 13 | "type": "module", 14 | "exports": { 15 | ".": { 16 | "types": "./dist/types/index.d.ts", 17 | "module": "./dist/index.js", 18 | "default": "./dist/index.cjs" 19 | } 20 | }, 21 | "files": [ 22 | "dist" 23 | ], 24 | "keywords": [ 25 | "wallet", 26 | "walletconnect", 27 | "reown", 28 | "walletkit" 29 | ], 30 | "sideEffects": false, 31 | "scripts": { 32 | "clean": "rm -rf dist", 33 | "build:pre": "npm run clean", 34 | "build:types": "tsc", 35 | "build:source": "rollup --config rollup.config.js", 36 | "build": "npm run lint; npm run build:pre; npm run build:source; npm run build:types", 37 | "test": " rm -rf test/tmp && vitest run --dir test", 38 | "lint": "eslint -c '../../.eslintrc' --fix './src/**/*.ts'", 39 | "prettier": "prettier --check '{src,test}/**/*.{js,ts,jsx,tsx}'" 40 | }, 41 | "dependencies": { 42 | "@walletconnect/core": "2.23.0", 43 | "@walletconnect/jsonrpc-provider": "1.0.14", 44 | "@walletconnect/jsonrpc-utils": "1.0.8", 45 | "@walletconnect/logger": "3.0.0", 46 | "@walletconnect/sign-client": "2.23.0", 47 | "@walletconnect/types": "2.23.0", 48 | "@walletconnect/utils": "2.23.0" 49 | }, 50 | "devDependencies": { 51 | "@ethersproject/wallet": "5.7.0" 52 | }, 53 | "repository": { 54 | "type": "git", 55 | "url": "git+https://github.com/reown-com/reown-walletkit-js.git", 56 | "directory": "packages/walletkit" 57 | }, 58 | "publishConfig": { 59 | "provenance": true 60 | } 61 | } -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | > Creating a release? Please use the [Release PR Template](https://github.com/WalletConnect/walletconnect-monorepo/blob/v2.0/.github/pr_template_release.md) instead. 2 | 3 | ## Description 4 | 5 | Please include a brief summary of the change. 6 | 7 | ## Type of change 8 | 9 | - [ ] Chore (non-breaking change that addresses non-functional tasks, maintenance, or code quality improvements) 10 | - [ ] Bug fix (non-breaking change which fixes an issue) 11 | - [ ] New feature (non-breaking change which adds functionality) 12 | - [ ] Draft PR (breaking/non-breaking change which needs more work for having a proper functionality _[Mark this PR as ready to review only when completely ready]_) 13 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) 14 | 15 | ## How has this been tested? 16 | 17 | Please describe the tests that you ran to verify your changes. Please also list any relevant details for your configuration. 18 | 19 | ## Fixes/Resolves (Optional) 20 | 21 | Please list any issues that are fixed by this PR in the format `#`. If it fixes multiple issues, please put each issue in a separate line. If this Pull Request does not fix any issues, please delete this section. 22 | 23 | ## Examples/Screenshots (Optional) 24 | 25 | Please attach a screenshot or screen recording of features/fixes you've implemented to verify your changes. Please also note any relevant details for your configuration. If your changes do not affect the UI, please delete this section. 26 | 27 | Use this table template to show examples of your changes: 28 | 29 | | Before | After | 30 | | ------------ | ------------ | 31 | | Content Cell | Content Cell | 32 | | Content Cell | Content Cell | 33 | 34 | ## Checklist 35 | 36 | - [ ] I have performed a self-review of my own code 37 | - [ ] My changes generate no new warnings 38 | - [ ] Any dependent changes have been merged and published in downstream modules 39 | 40 | # Additional Information (Optional) 41 | 42 | Please include any additional information that may be useful for the reviewer. 43 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "@typescript-eslint/ban-ts-ignore": ["off"], 4 | "@typescript-eslint/camelcase": ["off"], 5 | "@typescript-eslint/explicit-function-return-type": ["off"], 6 | "@typescript-eslint/interface-name-prefix": ["off"], 7 | "@typescript-eslint/no-explicit-any": ["off"], 8 | "@typescript-eslint/no-unused-expressions": ["off"], 9 | "@typescript-eslint/no-var-requires": ["off"], 10 | "@typescript-eslint/no-use-before-define": ["off"], 11 | "@typescript-eslint/no-unused-vars": ["off"], 12 | "@typescript-eslint/no-namespace": ["off"], 13 | "@typescript-eslint/ban-ts-comment": "off", 14 | "camelcase": "off", 15 | "no-unused-expressions": "off", 16 | "comma-dangle": ["error", "always-multiline"], 17 | "require-await": "warn", 18 | "no-async-promise-executor": ["off"], 19 | "no-empty-pattern": ["off"], 20 | "no-undef": ["error"], 21 | "no-var": ["error"], 22 | "object-curly-spacing": ["error", "always"], 23 | "quotes": ["error", "double", { "allowTemplateLiterals": true }], 24 | "semi": ["error", "always"], 25 | "spaced-comment": ["off"], 26 | "no-prototype-builtins": ["off"], 27 | "sort-keys": ["off"], 28 | "space-before-function-paren": ["off"], 29 | "indent": ["off"], 30 | "promise/param-names": "off", 31 | "no-console": ["error", { "allow": ["warn"] }], 32 | "no-useless-constructor": "off", 33 | "no-use-before-define": "off", 34 | "curly": "off", 35 | "prefer-promise-reject-errors": "off", 36 | "import/extensions": ["error", "ignorePackages"] 37 | }, 38 | "ignorePatterns": ["dist", "**/node_modules/*"], 39 | "env": { 40 | "browser": true, 41 | "es6": true 42 | }, 43 | "globals": { 44 | "NodeJS": true 45 | }, 46 | "extends": [ 47 | "standard", 48 | "eslint:recommended", 49 | "plugin:@typescript-eslint/eslint-recommended", 50 | "plugin:@typescript-eslint/recommended" 51 | ], 52 | "parserOptions": { 53 | "ecmaVersion": 2020, 54 | "sourceType": "module" 55 | }, 56 | "parser": "@typescript-eslint/parser", 57 | "plugins": ["prettier", "@typescript-eslint"] 58 | } 59 | -------------------------------------------------------------------------------- /.github/workflows/pr_checks.yml: -------------------------------------------------------------------------------- 1 | name: pull request checks 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - main 7 | 8 | permissions: 9 | contents: read 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | timeout-minutes: 10 15 | if: github.event.action == 'opened' || github.event.action == 'synchronize' 16 | steps: 17 | - name: checkout 18 | uses: actions/checkout@v4 19 | - name: setup-node 20 | uses: actions/setup-node@v4 21 | with: 22 | node-version: 20.x 23 | cache: "npm" 24 | cache-dependency-path: "**/package-lock.json" 25 | - name: install 26 | run: npm ci 27 | - name: build 28 | run: npm run build 29 | - uses: actions/upload-artifact@v4 30 | with: 31 | name: build-artifacts 32 | path: | 33 | packages/walletkit/dist 34 | 35 | code_style: 36 | runs-on: ubuntu-latest 37 | strategy: 38 | matrix: 39 | style-command: 40 | - lint 41 | - prettier 42 | steps: 43 | - name: checkout 44 | uses: actions/checkout@v4 45 | - name: setup-node 46 | uses: actions/setup-node@v4 47 | with: 48 | node-version: 20.x 49 | cache: "npm" 50 | cache-dependency-path: "**/package-lock.json" 51 | - name: install 52 | run: npm ci 53 | - name: check 54 | run: npm run ${{ matrix.style-command }} 55 | 56 | test: 57 | needs: [build, code_style] 58 | runs-on: ubuntu-latest 59 | strategy: 60 | matrix: 61 | test-prefix: 62 | - packages/walletkit 63 | env: 64 | TEST_RELAY_URL: ${{ secrets.TEST_RELAY_URL }} 65 | TEST_PROJECT_ID: ${{ secrets.TEST_PROJECT_ID }} 66 | steps: 67 | - name: checkout 68 | uses: actions/checkout@v4 69 | - name: setup-node 70 | uses: actions/setup-node@v4 71 | with: 72 | node-version: 20.x 73 | cache: "npm" 74 | cache-dependency-path: "**/package-lock.json" 75 | - name: install 76 | run: npm ci 77 | - uses: actions/download-artifact@v4 78 | with: 79 | name: build-artifacts 80 | - run: npm run test --prefix=${{ matrix.test-prefix }} 81 | -------------------------------------------------------------------------------- /packages/walletkit/README.md: -------------------------------------------------------------------------------- 1 | # @reown/walletkit 2 | 3 | ## Description 4 | 5 | The WalletKit SDK streamlines the integration process, making it easier for wallet developers to include the authentication and transaction signing features necessary for their users to connect and interact with all sorts of apps — now and in the future. 6 | 7 | ## Getting Started 8 | 9 | ### Install 10 | 11 | ``` 12 | npm install @reown/walletkit 13 | ``` 14 | 15 | ### Wallet Usage 16 | 17 | 1. Initialization 18 | 19 | ```javascript 20 | import { Core } from "@walletconnect/core"; 21 | import { WalletKit } from "@reown/walletkit"; 22 | 23 | const core = new Core({ 24 | projectId: process.env.PROJECT_ID, 25 | }); 26 | 27 | const walletkit = await WalletKit.init({ 28 | core, // <- pass the shared `core` instance 29 | metadata: { 30 | name: "Demo app", 31 | description: "Demo Client as Wallet/Peer", 32 | url: "www.walletconnect.com", 33 | icons: [], 34 | }, 35 | }); 36 | ``` 37 | 38 | 2. Sign Session Approval 39 | 40 | ```javascript 41 | walletkit.on("session_proposal", async (proposal) => { 42 | const session = await walletkit.approveSession({ 43 | id: proposal.id, 44 | namespaces, 45 | }); 46 | }); 47 | await walletkit.pair({ uri }); 48 | ``` 49 | 50 | 3. Sign Session Rejection 51 | 52 | ```javascript 53 | walletkit.on("session_proposal", async (proposal) => { 54 | const session = await walletkit.rejectSession({ 55 | id: proposal.id, 56 | reason: getSdkError("USER_REJECTED_METHODS"), 57 | }); 58 | }); 59 | ``` 60 | 61 | 4. Sign Session Disconnect 62 | 63 | ```javascript 64 | await walletkit.disconnectSession({ 65 | topic, 66 | reason: getSdkError("USER_DISCONNECTED"), 67 | }); 68 | ``` 69 | 70 | 5. Responding to Sign Session Requests 71 | 72 | ```javascript 73 | walletkit.on("session_request", async (event) => { 74 | const { id, method, params } = event.request; 75 | await walletkit.respondSessionRequest({ id, result: response }); 76 | }); 77 | ``` 78 | 79 | 6. Updating a Sign Session 80 | 81 | ```javascript 82 | await walletkit.updateSession({ topic, namespaces: newNs }); 83 | ``` 84 | 85 | 7. Updating a Sign Session 86 | 87 | ```javascript 88 | await walletkit.extendSession({ topic }); 89 | ``` 90 | 91 | 8. Emit Sign Session Events 92 | 93 | ```javascript 94 | await walletkit.emitSessionEvent({ 95 | topic, 96 | event: { 97 | name: "accountsChanged", 98 | data: ["0xab16a96D359eC26a11e2C2b3d8f8B8942d5Bfcdb"], 99 | }, 100 | chainId: "eip155:1", 101 | }); 102 | ``` 103 | 104 | 9. Handle Sign Events 105 | 106 | ```javascript 107 | walletkit.on("session_proposal", handler); 108 | walletkit.on("session_request", handler); 109 | walletkit.on("session_delete", handler); 110 | ``` 111 | -------------------------------------------------------------------------------- /.github/pr_template_release.md: -------------------------------------------------------------------------------- 1 | ## Release Checklist 2 | 3 | - [ ] **Canary Version QA** 4 | 5 | - [ ] I have thoroughly tested the release using a canary version: `v2...` 6 | - [ ] The canary version has been verified to work as expected. 7 | - [ ] All major features and changes have been tested and validated. 8 | 9 | - [ ] **Web3Modal Team QA** 10 | 11 | - [ ] The Web3Modal team has tested the release for compatibility and functionality. 12 | - [ ] The release is backwards compatible with Web3Modal. 13 | - [ ] Any reported issues or bugs have been addressed or documented. 14 | 15 | - [ ] **React Native Team QA** 16 | 17 | - [ ] The React Native team has tested the release for compatibility and functionality (if relevant). 18 | - [ ] The release works correctly in React Native environments and is backwards compatible with Web3Modal React Native. 19 | - [ ] Any reported issues or bugs have been addressed or documented. 20 | 21 | ## Description 22 | 23 | Provide a detailed description of the changes made in this release. Please include relevant information about new features, bug fixes, or improvements. 24 | 25 | If applicable, mention any specific areas that need additional attention during the review process. 26 | 27 | ## Testing Instructions 28 | 29 | Outline the steps needed to test the release thoroughly. Include any specific scenarios or test cases that should be executed to verify the correctness and stability of the package. If necessary, provide links to additional resources, testing environments, or tools that can assist in the testing process. 30 | 31 | ## Related Issues and Pull Requests 32 | 33 | List any related issues or pull requests that are relevant to this release, such as dependent changes to upstream packages or relevant documentation changes. If this release fixes any issues, please include the issue number in the format `#`. If this release is dependent on any other pull requests, please include the pull request number in the format `#`. 34 | 35 | ## Additional Notes 36 | 37 | Include any additional information, concerns, or considerations related to the release that might be important for the reviewers to be aware of. 38 | 39 | ## Definition of Done 40 | 41 | - [ ] The release has been tested using a canary version. 42 | - [ ] The release has been reviewed and approved by the Web3Modal team (if relevant). 43 | - [ ] The release has been reviewed and approved by the React Native team (if relevant). 44 | - [ ] All necessary documentation, including API changes or new features, has been updated. 45 | - [ ] Any dependent changes have been merged and published in downstream modules. 46 | - [ ] All tests (unit, integration, etc.) pass successfully. 47 | - [ ] The release has been properly versioned and tagged. 48 | - [ ] The release notes and changelog have been updated to reflect the changes made. 49 | 50 | Please ensure that all the items on the checklist have been completed before merging the release. 51 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Monorepo for Reown WalletKit", 3 | "private": true, 4 | "keywords": [ 5 | "wallet", 6 | "walletconnect", 7 | "ethereum", 8 | "jsonrpc", 9 | "mobile", 10 | "qrcode", 11 | "web3", 12 | "crypto", 13 | "cryptocurrency", 14 | "dapp", 15 | "reown", 16 | "walletkit" 17 | ], 18 | "author": "Reown, Inc.", 19 | "homepage": "https://github.com/reown-com/", 20 | "license": "SEE LICENSE IN LICENSE.md", 21 | "workspaces": [ 22 | "packages/walletkit" 23 | ], 24 | "scripts": { 25 | "clean": "npm run clean --workspaces --if-present", 26 | "lint": "npm run lint --workspaces --if-present", 27 | "prettier": "npm run prettier --workspaces --if-present", 28 | "build": "npm run build --workspaces --if-present", 29 | "test": "npm run test --workspaces --if-present", 30 | "test:ignoreUnhandled": "npm run test:ignoreUnhandled --workspaces --if-present", 31 | "check": "npm run lint; npm run build; npm run test", 32 | "reset": "npm run clean; npm run check", 33 | "new-version": "lerna version --no-private --no-git-tag-version --exact", 34 | "pre-publish": "npm run new-version; npm run reset", 35 | "npm-publish:rc": "lerna exec --no-private -- npm publish --access public --tag rc", 36 | "npm-publish:latest": "lerna exec --no-private -- npm publish --access public --tag latest", 37 | "npm-publish:next": "lerna exec --no-private -- npm publish --access public --tag next", 38 | "npm-publish:canary": "lerna exec --no-private -- npm publish --access public --tag canary", 39 | "changeset": "changeset", 40 | "changeset:version": "changeset version; npm i", 41 | "changeset:publish": "changeset publish" 42 | }, 43 | "repository": { 44 | "type": "git", 45 | "url": "git+https://github.com/reown-com.git" 46 | }, 47 | "bugs": { 48 | "url": "https://github.com/reown-com/issues" 49 | }, 50 | "devDependencies": { 51 | "@changesets/changelog-github": "0.4.1", 52 | "@changesets/cli": "2.29.8", 53 | "@rollup/plugin-commonjs": "22.0.2", 54 | "@rollup/plugin-json": "^6.1.0", 55 | "@rollup/plugin-node-resolve": "13.3.0", 56 | "@types/node": "18.7.3", 57 | "@types/sinon": "10.0.13", 58 | "@typescript-eslint/eslint-plugin": "5.33.0", 59 | "@typescript-eslint/parser": "5.33.0", 60 | "esbuild": "0.25.0", 61 | "eslint": "8.22.0", 62 | "eslint-config-prettier": "8.5.0", 63 | "eslint-config-standard": "17.0.0", 64 | "eslint-plugin-import": "2.26.0", 65 | "eslint-plugin-n": "15.7.0", 66 | "eslint-plugin-node": "11.1.0", 67 | "eslint-plugin-prettier": "4.2.1", 68 | "eslint-plugin-promise": "6.0.0", 69 | "eslint-plugin-react": "7.30.1", 70 | "eslint-plugin-standard": "5.0.0", 71 | "lerna": "9.0.3", 72 | "prettier": "2.7.1", 73 | "rollup": "2.79.2", 74 | "rollup-plugin-esbuild": "4.9.3", 75 | "sinon": "14.0.0", 76 | "typescript": "4.7.4", 77 | "vitest": "3.2.4" 78 | } 79 | } -------------------------------------------------------------------------------- /packages/walletkit/src/types/engine.ts: -------------------------------------------------------------------------------- 1 | import EventEmitter from "events"; 2 | import { ErrorResponse, JsonRpcResponse } from "@walletconnect/jsonrpc-utils"; 3 | import { 4 | ISignClient, 5 | PendingRequestTypes, 6 | ProposalTypes, 7 | SessionTypes, 8 | EchoClientTypes, 9 | AuthTypes, 10 | EngineTypes, 11 | } from "@walletconnect/types"; 12 | import { IWalletKit, WalletKitTypes } from "./client.js"; 13 | 14 | export abstract class IWalletKitEngine { 15 | public abstract signClient: ISignClient; 16 | 17 | constructor(public client: IWalletKit) {} 18 | // ---------- Public Methods ------------------------------------------------- // 19 | public abstract init(): Promise; 20 | 21 | public abstract pair(params: { uri: string; activatePairing?: boolean }): Promise; 22 | 23 | // ---------- Sign ------------------------------------------------- // 24 | // approve a session proposal (SIGN) 25 | public abstract approveSession(params: { 26 | id: number; 27 | namespaces: Record; 28 | sessionProperties?: ProposalTypes.SessionProperties; 29 | scopedProperties?: ProposalTypes.ScopedProperties; 30 | sessionConfig?: SessionTypes.SessionConfig; 31 | relayProtocol?: string; 32 | proposalRequestsResponses?: EngineTypes.ApproveParams["proposalRequestsResponses"]; 33 | }): Promise; 34 | 35 | // reject a session proposal (SIGN) 36 | public abstract rejectSession(params: { 37 | // proposerPublicKey: string; 38 | id: number; 39 | reason: ErrorResponse; 40 | }): Promise; 41 | 42 | // update session namespaces (SIGN) 43 | public abstract updateSession(params: { 44 | topic: string; 45 | namespaces: SessionTypes.Namespaces; 46 | }): Promise<{ acknowledged: () => Promise }>; 47 | 48 | // update session expiry (SIGN) 49 | public abstract extendSession(params: { 50 | topic: string; 51 | }): Promise<{ acknowledged: () => Promise }>; 52 | 53 | // respond JSON-RPC request (SIGN) 54 | public abstract respondSessionRequest(params: { 55 | topic: string; 56 | response: JsonRpcResponse; 57 | }): Promise; 58 | 59 | // emit session events (SIGN) 60 | public abstract emitSessionEvent(params: { 61 | topic: string; 62 | event: any; //SessionEvent; 63 | chainId: string; 64 | }): Promise; 65 | 66 | // disconnect a session (SIGN) 67 | public abstract disconnectSession(params: { 68 | topic: string; 69 | reason: ErrorResponse; 70 | }): Promise; 71 | 72 | // query all active sessions (SIGN) 73 | public abstract getActiveSessions(): Record; 74 | 75 | // query all pending session requests (SIGN) 76 | public abstract getPendingSessionProposals(): Record; 77 | 78 | // query all pending session requests (SIGN) 79 | public abstract getPendingSessionRequests(): PendingRequestTypes.Struct[]; 80 | 81 | // ---------- Multi chain Auth ------------------------------------------------- // 82 | 83 | public abstract approveSessionAuthenticate( 84 | params: AuthTypes.ApproveSessionAuthenticateParams, 85 | ): Promise<{ session: SessionTypes.Struct | undefined }>; 86 | 87 | public abstract formatAuthMessage: (params: { 88 | request: AuthTypes.BaseAuthRequestParams; 89 | iss: string; 90 | }) => string; 91 | 92 | public abstract rejectSessionAuthenticate(params: { 93 | id: number; 94 | reason: ErrorResponse; 95 | }): Promise; 96 | 97 | // ---------- Push ------------------------------------------------- // 98 | public abstract registerDeviceToken( 99 | params: EchoClientTypes.RegisterDeviceTokenParams, 100 | ): Promise; 101 | 102 | // ---------- Event Handlers ----------------------------------------------- // 103 | public abstract on: ( 104 | event: E, 105 | listener: (args: WalletKitTypes.EventArguments[E]) => void, 106 | ) => EventEmitter; 107 | 108 | public abstract once: ( 109 | event: E, 110 | listener: (args: WalletKitTypes.EventArguments[E]) => void, 111 | ) => EventEmitter; 112 | 113 | public abstract off: ( 114 | event: E, 115 | listener: (args: WalletKitTypes.EventArguments[E]) => void, 116 | ) => EventEmitter; 117 | 118 | public abstract removeListener: ( 119 | event: E, 120 | listener: (args: WalletKitTypes.EventArguments[E]) => void, 121 | ) => EventEmitter; 122 | } 123 | -------------------------------------------------------------------------------- /packages/walletkit/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # @reown/walletkit 2 | 3 | ## 1.4.1 4 | 5 | ### Patch Changes 6 | 7 | - [#47](https://github.com/reown-com/reown-walletkit-js/pull/47) [`9e2237bea9f91bc73070d863943e3bc4c382772b`](https://github.com/reown-com/reown-walletkit-js/commit/9e2237bea9f91bc73070d863943e3bc4c382772b) Thanks [@ganchoradkov](https://github.com/ganchoradkov)! - Added `scopedProperties` type and param to `approveSession` 8 | 9 | ## 1.4.0 10 | 11 | ### Minor Changes 12 | 13 | - [#42](https://github.com/reown-com/reown-walletkit-js/pull/42) [`aadfca8ff36948c0350def2a1fa968eca23faec6`](https://github.com/reown-com/reown-walletkit-js/commit/aadfca8ff36948c0350def2a1fa968eca23faec6) Thanks [@ganchoradkov](https://github.com/ganchoradkov)! - Updated `@walletconnect` deps to `2.23.0` 14 | 15 | - [#42](https://github.com/reown-com/reown-walletkit-js/pull/42) [`aadfca8ff36948c0350def2a1fa968eca23faec6`](https://github.com/reown-com/reown-walletkit-js/commit/aadfca8ff36948c0350def2a1fa968eca23faec6) Thanks [@ganchoradkov](https://github.com/ganchoradkov)! - Implemented new 1 Click Auth 16 | 17 | ## 1.3.0 18 | 19 | ### Minor Changes 20 | 21 | - [#43](https://github.com/reown-com/reown-walletkit-js/pull/43) [`efda078204aff4fe8d7b5960126bef96dc8b45dd`](https://github.com/reown-com/reown-walletkit-js/commit/efda078204aff4fe8d7b5960126bef96dc8b45dd) Thanks [@ganchoradkov](https://github.com/ganchoradkov)! - Updated `@walletconnect` deps to `2.22.0` 22 | 23 | ### Patch Changes 24 | 25 | - [#45](https://github.com/reown-com/reown-walletkit-js/pull/45) [`11b28c46e3b0e60d8ec815b7fc14959ac3099cba`](https://github.com/reown-com/reown-walletkit-js/commit/11b28c46e3b0e60d8ec815b7fc14959ac3099cba) Thanks [@ganchoradkov](https://github.com/ganchoradkov)! - Updated `@walletconnect` deps to `2.22.3` 26 | 27 | ## 1.2.11 28 | 29 | ### Patch Changes 30 | 31 | - [#41](https://github.com/reown-com/reown-walletkit-js/pull/41) [`754dcdced4bf95cee2c599ce3c500fb870fadf88`](https://github.com/reown-com/reown-walletkit-js/commit/754dcdced4bf95cee2c599ce3c500fb870fadf88) Thanks [@ganchoradkov](https://github.com/ganchoradkov)! - Updated `@walletconnect` deps to `2.21.9` 32 | 33 | - [#34](https://github.com/reown-com/reown-walletkit-js/pull/34) [`9d20faff80b2d51626f8429338fa65e3fbb6eee3`](https://github.com/reown-com/reown-walletkit-js/commit/9d20faff80b2d51626f8429338fa65e3fbb6eee3) Thanks [@ganchoradkov](https://github.com/ganchoradkov)! - Updated the `license` of the `reown-walletkit-js` repository 34 | 35 | ## 1.2.10 36 | 37 | ### Patch Changes 38 | 39 | - [#32](https://github.com/reown-com/reown-walletkit-js/pull/32) [`9a9588a5517aab040b9ce7f87fc9e00269c8a073`](https://github.com/reown-com/reown-walletkit-js/commit/9a9588a5517aab040b9ce7f87fc9e00269c8a073) Thanks [@ganchoradkov](https://github.com/ganchoradkov)! - Updated `@walletconnect` deps to `2.21.7` 40 | 41 | ## 1.2.9 42 | 43 | ### Patch Changes 44 | 45 | - [#30](https://github.com/reown-com/reown-walletkit-js/pull/30) [`c78bd989014f20ba251a0f2452700dd741c94389`](https://github.com/reown-com/reown-walletkit-js/commit/c78bd989014f20ba251a0f2452700dd741c94389) Thanks [@ganchoradkov](https://github.com/ganchoradkov)! - Updated `@walletconnect` dependencies to `2.21.6` 46 | 47 | ## 1.2.8 48 | 49 | ### Patch Changes 50 | 51 | - [#28](https://github.com/reown-com/reown-walletkit-js/pull/28) [`0e1379494037a56a47e4506f934a181c751f29b4`](https://github.com/reown-com/reown-walletkit-js/commit/0e1379494037a56a47e4506f934a181c751f29b4) Thanks [@ganchoradkov](https://github.com/ganchoradkov)! - Updated `@walletconnect` deps to `2.21.4` 52 | 53 | ## 1.2.7 54 | 55 | ### Patch Changes 56 | 57 | - [#24](https://github.com/reown-com/reown-walletkit-js/pull/24) [`3030eb3add7dc9309b2274976a7d385470d92f66`](https://github.com/reown-com/reown-walletkit-js/commit/3030eb3add7dc9309b2274976a7d385470d92f66) Thanks [@ganchoradkov](https://github.com/ganchoradkov)! - updated `@walletconnect` deps to 2.21.3 58 | 59 | ## 1.2.6 60 | 61 | ### Patch Changes 62 | 63 | - [#22](https://github.com/reown-com/reown-walletkit-js/pull/22) [`7d49bb8cc38891a8f2ffad6eb06af019b6562a66`](https://github.com/reown-com/reown-walletkit-js/commit/7d49bb8cc38891a8f2ffad6eb06af019b6562a66) Thanks [@ganchoradkov](https://github.com/ganchoradkov)! - Updated `@walletconnect` deps to `2.21.2` 64 | 65 | ## 1.2.5 66 | 67 | ### Patch Changes 68 | 69 | - [#19](https://github.com/reown-com/reown-walletkit-js/pull/19) [`a4a53a8fdb7f2f7f33ccf6b6d4029171c7312f3a`](https://github.com/reown-com/reown-walletkit-js/commit/a4a53a8fdb7f2f7f33ccf6b6d4029171c7312f3a) Thanks [@ganchoradkov](https://github.com/ganchoradkov)! - updated `@walletconnect` deps to `2.20.3` 70 | 71 | ## 1.2.4 72 | 73 | ### Patch Changes 74 | 75 | - [#17](https://github.com/reown-com/reown-walletkit-js/pull/17) [`ecc136aa5f75a50e3615fe1956af4d0217663b51`](https://github.com/reown-com/reown-walletkit-js/commit/ecc136aa5f75a50e3615fe1956af4d0217663b51) Thanks [@ganchoradkov](https://github.com/ganchoradkov)! - updated `@walletconnect` deps to 2.20.1 76 | -------------------------------------------------------------------------------- /packages/walletkit/src/types/client.ts: -------------------------------------------------------------------------------- 1 | import EventEmmiter, { EventEmitter } from "events"; 2 | import { ICore, CoreTypes, SignClientTypes } from "@walletconnect/types"; 3 | import { Logger } from "@walletconnect/logger"; 4 | import { JsonRpcPayload } from "@walletconnect/jsonrpc-utils"; 5 | import { IWalletKitEngine } from "./engine.js"; 6 | 7 | export declare namespace WalletKitTypes { 8 | type Event = 9 | | "session_proposal" 10 | | "session_request" 11 | | "session_delete" 12 | | "proposal_expire" 13 | | "session_request_expire" 14 | | "session_authenticate"; 15 | 16 | interface BaseEventArgs { 17 | id: number; 18 | topic: string; 19 | params: T; 20 | } 21 | 22 | type SessionRequest = SignClientTypes.EventArguments["session_request"]; 23 | 24 | type SessionProposal = SignClientTypes.EventArguments["session_proposal"]; 25 | 26 | type SessionDelete = Omit; 27 | 28 | type ProposalExpire = { id: number }; 29 | 30 | type SessionRequestExpire = { id: number }; 31 | 32 | type SessionAuthenticate = SignClientTypes.EventArguments["session_authenticate"]; 33 | 34 | type SignConfig = SignClientTypes.Options["signConfig"]; 35 | 36 | interface EventArguments { 37 | session_proposal: SessionProposal; 38 | session_request: SessionRequest; 39 | session_delete: Omit; 40 | proposal_expire: ProposalExpire; 41 | session_request_expire: SessionRequestExpire; 42 | session_authenticate: SessionAuthenticate; 43 | } 44 | 45 | interface Options { 46 | core: ICore; 47 | metadata: Metadata; 48 | name?: string; 49 | signConfig?: SignConfig; 50 | } 51 | 52 | type Metadata = CoreTypes.Metadata; 53 | 54 | interface INotifications { 55 | decryptMessage: (params: { 56 | topic: string; 57 | encryptedMessage: string; 58 | storageOptions?: CoreTypes.Options["storageOptions"]; 59 | storage?: CoreTypes.Options["storage"]; 60 | }) => Promise; 61 | getMetadata: (params: { 62 | topic: string; 63 | storageOptions?: CoreTypes.Options["storageOptions"]; 64 | storage?: CoreTypes.Options["storage"]; 65 | }) => Promise; 66 | } 67 | } 68 | 69 | export abstract class IWalletKitEvents extends EventEmmiter { 70 | constructor() { 71 | super(); 72 | } 73 | 74 | public abstract emit: ( 75 | event: E, 76 | args: WalletKitTypes.EventArguments[E], 77 | ) => boolean; 78 | 79 | public abstract on: ( 80 | event: E, 81 | listener: (args: WalletKitTypes.EventArguments[E]) => any, 82 | ) => this; 83 | 84 | public abstract once: ( 85 | event: E, 86 | listener: (args: WalletKitTypes.EventArguments[E]) => any, 87 | ) => this; 88 | 89 | public abstract off: ( 90 | event: E, 91 | listener: (args: WalletKitTypes.EventArguments[E]) => any, 92 | ) => this; 93 | 94 | public abstract removeListener: ( 95 | event: E, 96 | listener: (args: WalletKitTypes.EventArguments[E]) => any, 97 | ) => this; 98 | } 99 | 100 | export abstract class IWalletKit { 101 | public abstract readonly name: string; 102 | public abstract engine: IWalletKitEngine; 103 | public abstract events: EventEmitter; 104 | public abstract logger: Logger; 105 | public abstract core: ICore; 106 | public abstract metadata: WalletKitTypes.Metadata; 107 | public abstract signConfig?: WalletKitTypes.SignConfig; 108 | 109 | constructor(public opts: WalletKitTypes.Options) {} 110 | 111 | // ---------- Public Methods ----------------------------------------------- // 112 | 113 | public abstract pair: IWalletKitEngine["pair"]; 114 | 115 | // sign // 116 | public abstract approveSession: IWalletKitEngine["approveSession"]; 117 | public abstract rejectSession: IWalletKitEngine["rejectSession"]; 118 | public abstract updateSession: IWalletKitEngine["updateSession"]; 119 | public abstract extendSession: IWalletKitEngine["extendSession"]; 120 | public abstract respondSessionRequest: IWalletKitEngine["respondSessionRequest"]; 121 | public abstract disconnectSession: IWalletKitEngine["disconnectSession"]; 122 | public abstract emitSessionEvent: IWalletKitEngine["emitSessionEvent"]; 123 | public abstract getActiveSessions: IWalletKitEngine["getActiveSessions"]; 124 | public abstract getPendingSessionProposals: IWalletKitEngine["getPendingSessionProposals"]; 125 | public abstract getPendingSessionRequests: IWalletKitEngine["getPendingSessionRequests"]; 126 | // push 127 | public abstract registerDeviceToken: IWalletKitEngine["registerDeviceToken"]; 128 | // multi chain auth // 129 | public abstract approveSessionAuthenticate: IWalletKitEngine["approveSessionAuthenticate"]; 130 | public abstract formatAuthMessage: IWalletKitEngine["formatAuthMessage"]; 131 | public abstract rejectSessionAuthenticate: IWalletKitEngine["rejectSessionAuthenticate"]; 132 | 133 | // ---------- Event Handlers ----------------------------------------------- // 134 | public abstract on: ( 135 | event: E, 136 | listener: (args: WalletKitTypes.EventArguments[E]) => void, 137 | ) => EventEmitter; 138 | 139 | public abstract once: ( 140 | event: E, 141 | listener: (args: WalletKitTypes.EventArguments[E]) => void, 142 | ) => EventEmitter; 143 | 144 | public abstract off: ( 145 | event: E, 146 | listener: (args: WalletKitTypes.EventArguments[E]) => void, 147 | ) => EventEmitter; 148 | 149 | public abstract removeListener: ( 150 | event: E, 151 | listener: (args: WalletKitTypes.EventArguments[E]) => void, 152 | ) => EventEmitter; 153 | } 154 | -------------------------------------------------------------------------------- /packages/walletkit/src/client.ts: -------------------------------------------------------------------------------- 1 | import EventEmitter from "events"; 2 | import { CLIENT_CONTEXT } from "./constants/index.js"; 3 | import { Engine } from "./controllers/index.js"; 4 | import { IWalletKit, WalletKitTypes } from "./types/index.js"; 5 | import { Notifications } from "./utils/index.js"; 6 | 7 | export class WalletKit extends IWalletKit { 8 | public name: IWalletKit["name"]; 9 | public core: IWalletKit["core"]; 10 | public logger: IWalletKit["logger"]; 11 | public events: IWalletKit["events"] = new EventEmitter(); 12 | public engine: IWalletKit["engine"]; 13 | public metadata: IWalletKit["metadata"]; 14 | public static notifications: WalletKitTypes.INotifications = Notifications; 15 | public signConfig: IWalletKit["signConfig"]; 16 | 17 | static async init(opts: WalletKitTypes.Options) { 18 | const client = new WalletKit(opts); 19 | await client.initialize(); 20 | 21 | return client; 22 | } 23 | 24 | constructor(opts: WalletKitTypes.Options) { 25 | super(opts); 26 | this.metadata = opts.metadata; 27 | this.name = opts.name || CLIENT_CONTEXT; 28 | this.signConfig = opts.signConfig; 29 | this.core = opts.core; 30 | this.logger = this.core.logger; 31 | this.engine = new Engine(this); 32 | } 33 | 34 | // ---------- Events ----------------------------------------------- // 35 | 36 | public on: IWalletKit["on"] = (name, listener) => { 37 | return this.engine.on(name, listener); 38 | }; 39 | 40 | public once: IWalletKit["once"] = (name, listener) => { 41 | return this.engine.once(name, listener); 42 | }; 43 | 44 | public off: IWalletKit["off"] = (name, listener) => { 45 | return this.engine.off(name, listener); 46 | }; 47 | 48 | public removeListener: IWalletKit["removeListener"] = (name, listener) => { 49 | return this.engine.removeListener(name, listener); 50 | }; 51 | 52 | // ---------- Engine ----------------------------------------------- // 53 | 54 | public pair: IWalletKit["pair"] = async (params) => { 55 | try { 56 | return await this.engine.pair(params); 57 | } catch (error: any) { 58 | this.logger.error(error.message); 59 | throw error; 60 | } 61 | }; 62 | 63 | public approveSession: IWalletKit["approveSession"] = async (params) => { 64 | try { 65 | return await this.engine.approveSession(params); 66 | } catch (error: any) { 67 | this.logger.error(error.message); 68 | throw error; 69 | } 70 | }; 71 | 72 | public rejectSession: IWalletKit["rejectSession"] = async (params) => { 73 | try { 74 | return await this.engine.rejectSession(params); 75 | } catch (error: any) { 76 | this.logger.error(error.message); 77 | throw error; 78 | } 79 | }; 80 | 81 | public updateSession: IWalletKit["updateSession"] = async (params) => { 82 | try { 83 | return await this.engine.updateSession(params); 84 | } catch (error: any) { 85 | this.logger.error(error.message); 86 | throw error; 87 | } 88 | }; 89 | 90 | public extendSession: IWalletKit["extendSession"] = async (params) => { 91 | try { 92 | return await this.engine.extendSession(params); 93 | } catch (error: any) { 94 | this.logger.error(error.message); 95 | throw error; 96 | } 97 | }; 98 | 99 | public respondSessionRequest: IWalletKit["respondSessionRequest"] = async (params) => { 100 | try { 101 | return await this.engine.respondSessionRequest(params); 102 | } catch (error: any) { 103 | this.logger.error(error.message); 104 | throw error; 105 | } 106 | }; 107 | 108 | public disconnectSession: IWalletKit["disconnectSession"] = async (params) => { 109 | try { 110 | return await this.engine.disconnectSession(params); 111 | } catch (error: any) { 112 | this.logger.error(error.message); 113 | throw error; 114 | } 115 | }; 116 | 117 | public emitSessionEvent: IWalletKit["emitSessionEvent"] = async (params) => { 118 | try { 119 | return await this.engine.emitSessionEvent(params); 120 | } catch (error: any) { 121 | this.logger.error(error.message); 122 | throw error; 123 | } 124 | }; 125 | 126 | public getActiveSessions: IWalletKit["getActiveSessions"] = () => { 127 | try { 128 | return this.engine.getActiveSessions(); 129 | } catch (error: any) { 130 | this.logger.error(error.message); 131 | throw error; 132 | } 133 | }; 134 | 135 | public getPendingSessionProposals: IWalletKit["getPendingSessionProposals"] = () => { 136 | try { 137 | return this.engine.getPendingSessionProposals(); 138 | } catch (error: any) { 139 | this.logger.error(error.message); 140 | throw error; 141 | } 142 | }; 143 | 144 | public getPendingSessionRequests: IWalletKit["getPendingSessionRequests"] = () => { 145 | try { 146 | return this.engine.getPendingSessionRequests(); 147 | } catch (error: any) { 148 | this.logger.error(error.message); 149 | throw error; 150 | } 151 | }; 152 | 153 | public registerDeviceToken: IWalletKit["registerDeviceToken"] = (params) => { 154 | try { 155 | return this.engine.registerDeviceToken(params); 156 | } catch (error: any) { 157 | this.logger.error(error.message); 158 | throw error; 159 | } 160 | }; 161 | 162 | public approveSessionAuthenticate: IWalletKit["approveSessionAuthenticate"] = (params) => { 163 | try { 164 | return this.engine.approveSessionAuthenticate(params); 165 | } catch (error: any) { 166 | this.logger.error(error.message); 167 | throw error; 168 | } 169 | }; 170 | 171 | public rejectSessionAuthenticate: IWalletKit["rejectSessionAuthenticate"] = (params) => { 172 | try { 173 | return this.engine.rejectSessionAuthenticate(params); 174 | } catch (error: any) { 175 | this.logger.error(error.message); 176 | throw error; 177 | } 178 | }; 179 | 180 | public formatAuthMessage: IWalletKit["formatAuthMessage"] = (params) => { 181 | try { 182 | return this.engine.formatAuthMessage(params); 183 | } catch (error: any) { 184 | this.logger.error(error.message); 185 | throw error; 186 | } 187 | }; 188 | 189 | // ---------- Private ----------------------------------------------- // 190 | 191 | private async initialize() { 192 | this.logger.trace(`Initialized`); 193 | try { 194 | await this.engine.init(); 195 | this.logger.info(`WalletKit Initialization Success`); 196 | } catch (error: any) { 197 | this.logger.info(`WalletKit Initialization Failure`); 198 | this.logger.error(error.message); 199 | throw error; 200 | } 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /packages/walletkit/src/controllers/engine.ts: -------------------------------------------------------------------------------- 1 | import { SignClient } from "@walletconnect/sign-client"; 2 | import { ISignClient, SessionTypes } from "@walletconnect/types"; 3 | import { IWalletKitEngine, WalletKitTypes } from "../types/index.js"; 4 | 5 | export class Engine extends IWalletKitEngine { 6 | public signClient: ISignClient; 7 | 8 | constructor(client: IWalletKitEngine["client"]) { 9 | super(client); 10 | // initialized in init() 11 | this.signClient = {} as any; 12 | } 13 | 14 | public init = async () => { 15 | this.signClient = await SignClient.init({ 16 | core: this.client.core, 17 | metadata: this.client.metadata, 18 | signConfig: this.client.signConfig, 19 | }); 20 | this.signClient.core.eventClient.init().catch((error) => { 21 | this.client.logger.warn(error); 22 | }); 23 | }; 24 | 25 | public pair: IWalletKitEngine["pair"] = async (params) => { 26 | await this.client.core.pairing.pair(params); 27 | }; 28 | 29 | // Sign // 30 | public approveSession: IWalletKitEngine["approveSession"] = async (sessionProposal) => { 31 | const { topic, acknowledged } = await this.signClient.approve({ 32 | ...sessionProposal, 33 | id: sessionProposal.id, 34 | namespaces: sessionProposal.namespaces, 35 | sessionProperties: sessionProposal.sessionProperties, 36 | scopedProperties: sessionProposal.scopedProperties, 37 | sessionConfig: sessionProposal.sessionConfig, 38 | proposalRequestsResponses: sessionProposal?.proposalRequestsResponses, 39 | }); 40 | await acknowledged(); 41 | return this.signClient.session.get(topic); 42 | }; 43 | 44 | public rejectSession: IWalletKitEngine["rejectSession"] = async (params) => { 45 | return await this.signClient.reject(params); 46 | }; 47 | 48 | public updateSession: IWalletKitEngine["updateSession"] = async (params) => { 49 | return await this.signClient.update(params); 50 | }; 51 | 52 | public extendSession: IWalletKitEngine["extendSession"] = async (params) => { 53 | return await this.signClient.extend(params); 54 | }; 55 | 56 | public respondSessionRequest: IWalletKitEngine["respondSessionRequest"] = async (params) => { 57 | const result = await this.signClient.respond(params); 58 | return result; 59 | }; 60 | 61 | public disconnectSession: IWalletKitEngine["disconnectSession"] = async (params) => { 62 | return await this.signClient.disconnect(params); 63 | }; 64 | 65 | public emitSessionEvent: IWalletKitEngine["emitSessionEvent"] = async (params) => { 66 | return await this.signClient.emit(params); 67 | }; 68 | 69 | public getActiveSessions: IWalletKitEngine["getActiveSessions"] = () => { 70 | const sessions = this.signClient.session.getAll(); 71 | return sessions.reduce((sessions: Record, session) => { 72 | sessions[session.topic] = session; 73 | return sessions; 74 | }, {}); 75 | }; 76 | 77 | public getPendingSessionProposals: IWalletKitEngine["getPendingSessionProposals"] = () => { 78 | return this.signClient.proposal.getAll(); 79 | }; 80 | 81 | public getPendingSessionRequests: IWalletKitEngine["getPendingSessionRequests"] = () => { 82 | return this.signClient.getPendingSessionRequests(); 83 | }; 84 | 85 | // Multi chain Auth // 86 | public approveSessionAuthenticate: IWalletKitEngine["approveSessionAuthenticate"] = async ( 87 | params, 88 | ) => { 89 | return await this.signClient.approveSessionAuthenticate(params); 90 | }; 91 | 92 | public rejectSessionAuthenticate: IWalletKitEngine["rejectSessionAuthenticate"] = async ( 93 | params, 94 | ) => { 95 | return await this.signClient.rejectSessionAuthenticate(params); 96 | }; 97 | 98 | public formatAuthMessage: IWalletKitEngine["formatAuthMessage"] = (params) => { 99 | return this.signClient.formatAuthMessage(params); 100 | }; 101 | 102 | // Push // 103 | public registerDeviceToken: IWalletKitEngine["registerDeviceToken"] = (params) => { 104 | return this.client.core.echoClient.registerDeviceToken(params); 105 | }; 106 | 107 | // ---------- public events ----------------------------------------------- // 108 | public on: IWalletKitEngine["on"] = (name, listener) => { 109 | this.setEvent(name, "off"); 110 | this.setEvent(name, "on"); 111 | return this.client.events.on(name, listener); 112 | }; 113 | 114 | public once: IWalletKitEngine["once"] = (name, listener) => { 115 | this.setEvent(name, "off"); 116 | this.setEvent(name, "once"); 117 | return this.client.events.once(name, listener); 118 | }; 119 | 120 | public off: IWalletKitEngine["off"] = (name, listener) => { 121 | this.setEvent(name, "off"); 122 | return this.client.events.off(name, listener); 123 | }; 124 | 125 | public removeListener: IWalletKitEngine["removeListener"] = (name, listener) => { 126 | this.setEvent(name, "removeListener"); 127 | return this.client.events.removeListener(name, listener); 128 | }; 129 | 130 | // ---------- Private ----------------------------------------------- // 131 | 132 | private onSessionRequest = (event: WalletKitTypes.SessionRequest) => { 133 | this.client.events.emit("session_request", event); 134 | }; 135 | 136 | private onSessionProposal = (event: WalletKitTypes.SessionProposal) => { 137 | this.client.events.emit("session_proposal", event); 138 | }; 139 | 140 | private onSessionDelete = (event: WalletKitTypes.SessionDelete) => { 141 | this.client.events.emit("session_delete", event); 142 | }; 143 | 144 | private onProposalExpire = (event: WalletKitTypes.ProposalExpire) => { 145 | this.client.events.emit("proposal_expire", event); 146 | }; 147 | 148 | private onSessionRequestExpire = (event: WalletKitTypes.SessionRequestExpire) => { 149 | this.client.events.emit("session_request_expire", event); 150 | }; 151 | 152 | private onSessionRequestAuthenticate = (event: WalletKitTypes.SessionAuthenticate) => { 153 | this.client.events.emit("session_authenticate", event); 154 | }; 155 | 156 | private setEvent = ( 157 | event: WalletKitTypes.Event, 158 | action: "on" | "off" | "once" | "removeListener", 159 | ) => { 160 | switch (event) { 161 | case "session_request": 162 | this.signClient.events[action]("session_request", this.onSessionRequest); 163 | break; 164 | case "session_proposal": 165 | this.signClient.events[action]("session_proposal", this.onSessionProposal); 166 | break; 167 | case "session_delete": 168 | this.signClient.events[action]("session_delete", this.onSessionDelete); 169 | break; 170 | case "proposal_expire": 171 | this.signClient.events[action]("proposal_expire", this.onProposalExpire); 172 | break; 173 | case "session_request_expire": 174 | this.signClient.events[action]("session_request_expire", this.onSessionRequestExpire); 175 | break; 176 | case "session_authenticate": 177 | this.signClient.events[action]("session_authenticate", this.onSessionRequestAuthenticate); 178 | break; 179 | } 180 | }; 181 | } 182 | -------------------------------------------------------------------------------- /packages/walletkit/test/multitenancy.spec.ts: -------------------------------------------------------------------------------- 1 | import { TEST_METADATA } from "./shared/values"; 2 | import { Core } from "@walletconnect/core"; 3 | import { formatJsonRpcResult } from "@walletconnect/jsonrpc-utils"; 4 | import { SignClient } from "@walletconnect/sign-client"; 5 | import { ICore, ISignClient, SessionTypes } from "@walletconnect/types"; 6 | import { parseUri } from "@walletconnect/utils"; 7 | import { KeyValueStorage } from "@walletconnect/keyvaluestorage"; 8 | import { Wallet as CryptoWallet } from "@ethersproject/wallet"; 9 | 10 | import { expect, describe, it, beforeAll } from "vitest"; 11 | import { WalletKit, IWalletKit } from "../src"; 12 | import { 13 | TEST_CORE_OPTIONS, 14 | TEST_ETHEREUM_CHAIN, 15 | TEST_NAMESPACES, 16 | TEST_REQUIRED_NAMESPACES, 17 | } from "./shared"; 18 | 19 | interface IClientInstance { 20 | clientId: number; 21 | core: ICore; 22 | wallet: IWalletKit; 23 | cryptoWallet: CryptoWallet; 24 | sessionTopic: string; 25 | } 26 | 27 | describe("Multitenancy", () => { 28 | // enable global core - its disabled in tests by default 29 | process.env.DISABLE_GLOBAL_CORE = "false"; 30 | // create 5 walletkit clients 31 | const walletKitClientsToCreate = process.env.WALLETKIT_CLIENTS_TO_CREATE 32 | ? parseInt(process.env.WALLETKIT_CLIENTS_TO_CREATE, 10) 33 | : 5; 34 | 35 | expect(walletKitClientsToCreate).to.be.a("number"); 36 | expect(walletKitClientsToCreate).to.be.greaterThan(0); 37 | 38 | // send 5 session requests to the clients 39 | const sessionRequestsToSend = process.env.SESSION_REQUESTS_TO_SEND 40 | ? parseInt(process.env.SESSION_REQUESTS_TO_SEND, 10) 41 | : 5; 42 | 43 | expect(sessionRequestsToSend).to.be.a("number"); 44 | expect(sessionRequestsToSend).to.be.greaterThan(0); 45 | 46 | // force all clients to use the same storage 47 | const walletKitStorage = new KeyValueStorage({ 48 | database: "./test/tmp/walletkit-core-db", 49 | }); 50 | 51 | // map of the number of proposals received by each WalletKit client 52 | const proposalsReceived = new Map(); 53 | // map of the number of proposals responded by each WalletKit client 54 | const proposalsResponded = new Map(); 55 | 56 | // map of the number of session requests received by each WalletKit client 57 | const sessionRequestsReceived = new Map< 58 | number, 59 | { 60 | instanceId: number; 61 | count: number; 62 | payload: string; 63 | } 64 | >(); 65 | // map of the number of session requests responded by each WalletKit client 66 | const sessionRequestsResponded = new Map< 67 | number, 68 | { 69 | instanceId: number; 70 | count: number; 71 | payload: string; 72 | } 73 | >(); 74 | 75 | let core: ICore; 76 | let dapp: ISignClient; 77 | 78 | // map of walletkit clients, one client for each session 79 | const walletKitClients = new Map(); 80 | const walletKitIdToSessionTopic = new Map(); 81 | // map of dapp sessions, one session with each client 82 | const dappSessions = new Map(); 83 | 84 | const createWalletKitClient = async (id: number, isRestarting = false) => { 85 | const core = new Core({ 86 | ...TEST_CORE_OPTIONS, 87 | storage: walletKitStorage, 88 | }); 89 | const wallet = await WalletKit.init({ 90 | core, 91 | name: "wallet", 92 | metadata: TEST_METADATA, 93 | signConfig: { 94 | disableRequestQueue: true, 95 | }, 96 | }); 97 | if (isRestarting) { 98 | // each walletKit client will load the sessions of the previous client instances because of the same storage they all use 99 | expect(Object.keys(wallet.getActiveSessions()).length).to.be.eq(walletKitClientsToCreate); 100 | } 101 | 102 | const clientInstance: IClientInstance = { 103 | clientId: id, 104 | wallet, 105 | core: wallet.core, 106 | cryptoWallet: CryptoWallet.createRandom(), 107 | sessionTopic: "", // will be set after session is approved or after restarting the wallet 108 | }; 109 | return clientInstance; 110 | }; 111 | 112 | const pairWalletKitClient = async (walletInstance: IClientInstance, uri: string) => { 113 | const wallet = walletInstance.wallet; 114 | const { topic: pairingTopic } = parseUri(uri); 115 | 116 | await Promise.all([ 117 | new Promise((resolve) => { 118 | wallet.on("session_proposal", async (sessionProposal) => { 119 | // count the number of proposals received for each client 120 | proposalsReceived.set( 121 | walletInstance.clientId, 122 | (proposalsReceived.get(walletInstance.clientId) || 0) + 1, 123 | ); 124 | 125 | if (sessionProposal.params.pairingTopic !== pairingTopic) { 126 | console.warn("Session proposal not intended for this client, skipping..."); 127 | return; 128 | } 129 | 130 | proposalsResponded.set( 131 | walletInstance.clientId, 132 | (proposalsResponded.get(walletInstance.clientId) || 0) + 1, 133 | ); 134 | 135 | const session = await wallet.approveSession({ 136 | id: sessionProposal.id, 137 | namespaces: TEST_NAMESPACES, 138 | }); 139 | walletInstance.sessionTopic = session.topic; 140 | resolve(session); 141 | }); 142 | }), 143 | wallet.pair({ uri }), 144 | ]); 145 | 146 | // map the walletkit instance id to the session topic 147 | walletKitIdToSessionTopic.set(walletInstance.clientId, walletInstance.sessionTopic); 148 | 149 | expect(walletInstance.sessionTopic).to.be.exist; 150 | expect(walletInstance.sessionTopic).to.be.not.empty; 151 | expect(walletInstance.sessionTopic).to.be.a("string"); 152 | expect(walletInstance.sessionTopic).to.not.eql(""); 153 | 154 | expect(walletInstance.core).to.be.exist; 155 | expect(walletInstance.wallet).to.be.exist; 156 | expect(walletInstance.cryptoWallet).to.be.exist; 157 | 158 | return walletInstance.sessionTopic; 159 | }; 160 | 161 | beforeAll(async () => { 162 | core = new Core({ 163 | ...TEST_CORE_OPTIONS, 164 | // isolate dapp storage from walletkit clients 165 | customStoragePrefix: "dapp-storage", 166 | }); 167 | dapp = await SignClient.init({ 168 | core, 169 | ...TEST_CORE_OPTIONS, 170 | name: "Dapp", 171 | metadata: TEST_METADATA, 172 | }); 173 | }); 174 | 175 | it("should establish sessions to multiple WalletKit clients", async () => { 176 | for (let i = 0; i < walletKitClientsToCreate; i++) { 177 | const { uri, approval } = await dapp.connect({ 178 | requiredNamespaces: TEST_REQUIRED_NAMESPACES, 179 | }); 180 | if (!uri) { 181 | throw new Error(`URI is not defined for client ${i}`); 182 | } 183 | const walletKitInstanceId = i + 1; 184 | // 1. create new WalletKit client for each session 185 | // 2. pair the WalletKit client with the dapp 186 | // 3. store the paired WalletKit client in the walletKitClients map 187 | // 4. store the session in the dappSessions map 188 | const walletInstance = await createWalletKitClient(walletKitInstanceId); 189 | await Promise.all([ 190 | new Promise(async (resolve) => { 191 | const session = await approval(); 192 | dappSessions.set(session.topic, session); 193 | resolve(); 194 | }), 195 | new Promise(async (resolve) => { 196 | const sessionTopic = await pairWalletKitClient(walletInstance, uri); 197 | walletKitClients.set(sessionTopic, walletInstance); 198 | resolve(); 199 | }), 200 | ]); 201 | } 202 | // verify that the number of sessions in the dappSessions map is equal to the number of WalletKit clients 203 | expect(dappSessions.size).to.be.eq(walletKitClientsToCreate); 204 | // verify that the number of WalletKit clients is equal to the number of sessions in the dappSessions map 205 | expect(walletKitClients.size).to.be.eq(walletKitClientsToCreate); 206 | 207 | // verify that each WalletKit client has a session that matches the session in the dappSessions map 208 | for (const [topic] of dappSessions.entries()) { 209 | const walletKitClient = walletKitClients.get(topic); 210 | 211 | if (!walletKitClient) { 212 | throw new Error(`WalletKit client not found for session topic ${topic}`); 213 | } 214 | expect(walletKitClient).to.be.exist; 215 | expect(walletKitClient.sessionTopic).to.be.eq(topic); 216 | } 217 | }); 218 | 219 | it("should restart walletkit clients", async () => { 220 | // close all transports 221 | for (const client of walletKitClients.values()) { 222 | expect(client).to.be.exist; 223 | await client.core.relayer.transportClose(); 224 | await new Promise((resolve) => setTimeout(resolve, 500)); 225 | } 226 | // clear the global Core - as if the instance was restarted 227 | globalThis._walletConnectCore_ = undefined; 228 | // clear the walletkit clients map 229 | walletKitClients.clear(); 230 | 231 | expect(walletKitClients.size).to.be.eq(0); 232 | 233 | // reinitialize the walletkit clients 234 | for (let i = 0; i < walletKitClientsToCreate; i++) { 235 | const walletInstance = await createWalletKitClient(i + 1, true); 236 | 237 | const sessionTopic = walletKitIdToSessionTopic.get(walletInstance.clientId); 238 | if (!sessionTopic) { 239 | throw new Error(`Session topic not found for wallet instance ${walletInstance.clientId}`); 240 | } 241 | walletInstance.sessionTopic = sessionTopic; 242 | walletKitClients.set(sessionTopic, walletInstance); 243 | } 244 | 245 | // verify that the number of WalletKit clients is equal to the number of sessions in the dappSessions map 246 | expect(walletKitClients.size).to.be.eq(dappSessions.size); 247 | expect(walletKitClients.size).to.be.eq(walletKitClientsToCreate); 248 | 249 | // verify that each WalletKit client has a session that matches the session in the dappSessions map 250 | for (const session of dappSessions.values()) { 251 | const walletKitClient = walletKitClients.get(session.topic); 252 | if (!walletKitClient) { 253 | throw new Error(`WalletKit client not found for session topic ${session.topic}`); 254 | } 255 | expect(walletKitClient).to.be.exist; 256 | expect(walletKitClient.sessionTopic).to.be.eq(session.topic); 257 | } 258 | 259 | // verify that each WalletKit client has correctly loaded its session 260 | for (const walletKitClient of walletKitClients.values()) { 261 | const sessionTopic = walletKitClient.sessionTopic; 262 | const session = walletKitClient.wallet.getActiveSessions()[sessionTopic]; 263 | expect(session).to.be.exist; 264 | expect(session.topic).to.be.eq(sessionTopic); 265 | } 266 | }); 267 | 268 | it("should receive session requests", async () => { 269 | const onSessionRequest = (walletKitClient: IClientInstance) => { 270 | walletKitClient.wallet.on("session_request", async (sessionRequest) => { 271 | const { id, topic: requestTopic } = sessionRequest; 272 | // count the number of session requests received by this client 273 | sessionRequestsReceived.set(walletKitClient.clientId, { 274 | instanceId: walletKitClient.clientId, 275 | count: (sessionRequestsReceived.get(walletKitClient.clientId)?.count || 0) + 1, 276 | payload: sessionRequest.params.request.params[0], 277 | }); 278 | if (requestTopic !== walletKitClient.sessionTopic) { 279 | console.warn("session_request not intended for this client, skipping..."); 280 | return; 281 | } 282 | 283 | // count the number of session requests responded by this client 284 | sessionRequestsResponded.set(walletKitClient.clientId, { 285 | instanceId: walletKitClient.clientId, 286 | count: (sessionRequestsResponded.get(walletKitClient.clientId)?.count || 0) + 1, 287 | payload: sessionRequest.params.request.params[0], 288 | }); 289 | 290 | await walletKitClient.wallet.respondSessionRequest({ 291 | topic: requestTopic, 292 | response: formatJsonRpcResult(id, `0x_${walletKitClient.clientId}`), 293 | }); 294 | }); 295 | }; 296 | 297 | for (const walletKitClient of walletKitClients.values()) { 298 | onSessionRequest(walletKitClient); 299 | } 300 | 301 | const dappSessionsArray = [...dappSessions.values()]; 302 | 303 | for (let i = 0; i < sessionRequestsToSend; i++) { 304 | const sessionToSendRequestTo = dappSessionsArray[i]; 305 | 306 | if (!sessionToSendRequestTo) { 307 | throw new Error(`Dapp session not found for random request: ${i + 1}`); 308 | } 309 | // i+1 is the intended clientId for this request 310 | const requestPayload = `request_for_client_${i + 1}`; 311 | const result = await dapp.request({ 312 | topic: sessionToSendRequestTo.topic, 313 | request: { 314 | method: "eth_signTransaction", 315 | params: [requestPayload, "0x"], 316 | }, 317 | chainId: TEST_ETHEREUM_CHAIN, 318 | }); 319 | expect(result).to.be.exist; 320 | expect(result).to.be.a("string"); 321 | // verify that this request was responded by the correct WalletKit client - clientId = i+1 322 | expect(result).to.be.eq(`0x_${i + 1}`); 323 | } 324 | await new Promise((resolve) => setTimeout(resolve, 1000)); 325 | 326 | console.log("sessionRequestsReceived", sessionRequestsReceived.values()); 327 | console.log("sessionRequestsResponded", sessionRequestsResponded.values()); 328 | // verify that each WalletKit client received every session request at least once 329 | for (const requestReceived of sessionRequestsReceived.values()) { 330 | expect(requestReceived.count).to.be.greaterThan(1); 331 | } 332 | 333 | // verify that each WalletKit client responded to its intended request 334 | for (const requestResponded of sessionRequestsResponded.values()) { 335 | expect(requestResponded.count).to.be.eq(1); 336 | expect(requestResponded.payload).to.be.eq( 337 | `request_for_client_${requestResponded.instanceId}`, 338 | ); 339 | } 340 | 341 | await new Promise((resolve) => setTimeout(resolve, 1000)); 342 | }); 343 | }); 344 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | **WALLETCONNECT COMMUNITY LICENSE AGREEMENT** 2 | 3 | **Release Date: 20 August 2025** 4 | 5 | You, or the company or organization of which you are an employee, agent, or contractor (“**You**”, “**Your**”), desire a license from Reown, Inc. (“**We**”, “**Us**”, “**Our**”) to use the WalletKit software development kit, documentation and related materials (collectively, “**WalletKit Items**”). Before You access the WalletKit Items, We need to establish what you can expect from Us, and what We expect from You. By accessing and using the WalletKit Items, You confirm that You reviewed and You agree to this License Agreement (“**License**”), and that You are authorized to bind the company or organization you represent to this License. **_By downloading, installing, integrating and/or using the_** \*WalletKit **Items, you or the organization you represent, as applicable, agree to be bound by and become a party to this License as a "Licensee". If you do not agree to all of the terms of this License, you are not a Licensee and you may not download, install, or use (or must cease using) the WalletKit Items.\*** 6 | 7 | **1\. LICENSE** 8 | 9 | **a. Grant of Rights.** Subject to the terms and conditions of this License We grant You (and not Your affiliates or related persons), and You accept, a perpetual (subject to Section 8(b) Termination), non-exclusive, non-transferable (subject to Section 11(c) Assignment), non-sublicensable, and worldwide license under Our intellectual property rights to use, display, copy, transmit, distribute, modify, adapt, and create derivative works of the WalletKit Items. 10 | 11 | **b. Educational, Research and New Application Uses.** If You are using the WalletKit Items for educational or non-commercial research purposes, the License We grant You is royalty-free. In addition, Your use of WalletKit Items for development, maintenance and enhancement of Your applications and wallets is royalty-free, subject to Section 3\. 12 | 13 | **c. Modification of WalletKit Items.** We own all right, title and interest to the intellectual property rights (including, but not limited to patent, copyright, and trademark rights) to any modification to or derivative work of the WalletKit Items that You may develop, and you hereby assign any and all such rights to such modifications and derivative works to Us, subject to the following: 14 | 15 | (i) We hereby grant back to You a perpetual, non-exclusive, non-transferable, royalty-free, worldwide right and license to use these modifications and derivative works solely in conjunction with the WalletKit Items and only while this License remains in effect. You must cause any modified files to carry prominent notices stating that You changed the files. We and our assignees and Our and their licensees may use such modifications and derivative works without any approval from You and without compensation or attribution to You. You also agree to provide the source and binary code of any such modifications and derivative works to Us at the email address We may designate from time to time. 16 | 17 | (ii) For the avoidance of doubt, Your applications and wallets that you develop using the WalletKit Items are not modifications or derivative works of the WalletKit Items. You retain all rights to your applications and wallets, and you have no obligation to share or license Your applications or wallets with Us or with any third parties; provided, however, We retain all rights to the WalletKit Items and any modifications or derivative works thereof that may be incorporated into your applications and wallets. 18 | 19 | **2\. REDISTRIBUTION AND USE** 20 | 21 | **a. Distribution Requirements.** If You distribute or make available the WalletKit Items (including any modifications to or derivative works thereof) or any product or service that uses them (including Your applications and wallets), You must (i) include the following copyright notice in the notices or readme.txt of the product, or the about section of the website for the services: “Portions © 2025 Reown, Inc. All Rights Reserved” ; (ii) provide a copy of this License Agreement with any applications or wallets containing such WalletKit Items or that are required for the use thereof; and (iii) applicable logo and branding. 22 | 23 | **b. Network Connection.** As a material condition of this License, all use, distribution or derivatives works of the WalletKit Items must connect to the proprietary Reown gateway infrastructure, or its successor, and its related WalletConnect messaging protocol (collectively, the “**Network**”) unless we explicitly approve you doing otherwise. You further agree not to circumvent this requirement, or instruct or suggest to Your end users to do so. 24 | 25 | **c. Compliance with Law.** We are not in a position to monitor and assess the legality and regulatory treatment of your business and operations. Your use of the WalletKit Items must comply with all applicable laws and regulations and Our Terms of Service and Acceptable Use Policy, as amended from time to time, the terms of which are incorporated herein by reference. 26 | 27 | **d. Use of Marks.** This License does not grant permission to use Our trade names, trademarks, service marks, or product names (collectively, the “**Marks**”), except as permitted by Our Marks usage guidelines or as required for disclosure of the development of Your applications or wallets, reproducing the attribution notice in the readme.txt file, or Your redistribution of WalletKit Items. We reserve all rights not expressly granted to you under this License. Unless explicitly approved by Us, in no event will You use Our name, trademarks, or logos to endorse or imply Our endorsement of products derived from WalletKit Items or any products or services that use WalletKit Items. Where You are permitted to use the Marks, You must comply with Our then-current trademark and logo usage guidelines, as may be updated from time to time without notice from Us. All goodwill arising out of Your use of any Mark will inure solely to Us. 28 | 29 | **e. Export Controls.** You and We acknowledge that the WalletKit Items and all related technical information, documents and materials are subject to export controls under the U.S. Export Administration Regulations. You and We will (i) comply strictly with all legal requirements established under these controls, (ii) cooperate fully with each other in any official or unofficial audit or inspection that relates to these controls and (iii) not export, re-export, divert or transfer, directly or indirectly, any such item or direct products thereof to Cuba, Iran, North Korea, Sudan, and the Crimea, Donetsk, Luhansk, Kherson and Zaporizhzhia region not controlled by the government of Ukraine, or any national thereof or to any country or national thereof that is embargoed by Executive order, unless such party has obtained the prior written authorization of the other party and U.S. Commerce Department. We may modify this list to conform to changes in the U.S. Export Administration Regulations. 30 | 31 | **3\. LIMITED COMMERCIAL USE** 32 | 33 | If the number of remote processing calls (RPCs) made by, or monthly active users of (MAUs), associated with Your application or wallet developed using WalletKit Items, or your product or service that uses WalletKit Items exceed the thresholds set forth below, determined in our sole discretion, You must enter into and pay applicable fees and charges for a commercial license. You may contact Reown for these purposes using [this form](https://share.hsforms.com/1_oWa8QkwRXi6oR0nZ_SIxQnxw6s). 34 | 35 | - Monthly RPC limit of 2,500,000 36 | 37 | - Monthly MAU limit of 500 38 | 39 | MAU means, as applicable: (1) a unique externally owned account that authenticates and connects to your app in a 30 day period; or (2) a unique embedded wallet that is created on, and authenticates and connects, to your app in a 30 day period 40 | 41 | We reserve the right to change the RPC and/or MAU limit thresholds and/or set different or additional triggers or thresholds for requiring a commercial license. The current thresholds or triggers for a commercial license may be published on Our [Terms of Service](https://reown.com/terms-of-service). 42 | 43 | **4\. DISCLAIMER OF WARRANTY** 44 | 45 | TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THE WALLETKIT ITEMS ARE PROVIDED “AS IS” WITHOUT WARRANTIES OF ANY KIND. ALL WARRANTIES, CONDITIONS, REPRESENTATIONS, INDEMNITIES AND GUARANTEES WITH RESPECT TO THE WALLETKIT ITEMS, WHETHER EXPRESS OR IMPLIED, ARISING BY LAW, CUSTOM, PRIOR ORAL OR WRITTEN STATEMENTS BY US OR OTHERWISE (INCLUDING, BUT NOT LIMITED TO ANY WARRANTY OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR PARTICULAR PURPOSE AND NON-INFRINGEMENT) ARE HEREBY OVERRIDDEN, EXCLUDED AND DISCLAIMED. SOME JURISDICTIONS DO NOT PERMIT THE DISCLAIMER OF ALL WARRANTIES SO YOU MAY HAVE ADDITIONAL RIGHTS PROVIDED BY LAW. 46 | 47 | **5\. LIMITATION OF LIABILITY** 48 | 49 | IN NO EVENT AND UNDER NO LEGAL THEORY, WHETHER IN TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, UNLESS REQUIRED BY APPLICABLE LAW DESPITE THIS LIMITATION OR AGREED TO IN WRITING BY US, WILL WE BE LIABLE TO YOU OR YOUR LICENSEES OR SUBLICENSEES FOR ANY DAMAGES, INCLUDING ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER ARISING AS A RESULT OF THIS LICENSE OR OUT OF THE USE OR INABILITY TO USE THE WALLETKIT ITEMS OR ANY PORTION THEREOF (INCLUDING BUT NOT LIMITED TO DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES), EVEN IF WE OR OUR AFFILIATES HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. SOME JURISDICTIONS DO NOT PERMIT THE EXCLUSION OF ALL DAMAGES SO YOU MAY HAVE ADDITIONAL RIGHTS PROVIDED BY LAW. 50 | 51 | **6\. INTELLECTUAL PROPERTY** 52 | 53 | All trademarks, service marks, patents, copyrights, trade secrets, or other proprietary rights in or related to the WalletKit Items are and will remain Our exclusive property (or that of Our licensors). You will not take any action that jeopardizes those rights, nor will you acquire any right in the WalletKit Items, except the limited use rights specified in this License. You acknowledge that We (or Our licensors) will own all rights in any copy, translation, modification, adaptation or derivative works of the WalletKit Items (whether or not authorized by Us), and You will provide any instrument We reasonably request that you obtain to give full legal effect to Our rights under this Section. 54 | 55 | **7\. CONFIDENTIALITY** 56 | 57 | You acknowledge that the WalletKit Items incorporate nonpublic confidential and proprietary information We have developed or acquired or licensed ("**Confidential Information**"). You will keep the Confidential Information in the strictest confidence and will not use or copy any Confidential Information, or disclose it to any other person. You will not permit removal or defacing of any confidentiality or proprietary notice placed on or in the WalletKit Items, and any copyright notices on or in these items does not constitute publication or make them non-confidential. 58 | 59 | **8\. TERM AND TERMINATION** 60 | 61 | **a. Term.** The term of this Agreement will commence upon Your accessing the WalletKit Items and will continue in full force and effect until terminated in accordance with the terms and conditions of this License. As this License is updated for future releases of the WalletKit Items, You must abide by and meet all requirements of future updates of this License for those future releases of WalletKit Items, and such future updates of this License will apply to such future applications and wallets that may be developed with that future release of WalletKit Items or any portion thereof. You may not use older versions of the SDK to circumvent the terms applicable to new versions you choose to download or use. 62 | 63 | **b. Termination.** This License terminates (i) 30 days after either We notify you in writing that You have breached a material term of this License, if that breach is not cured before then, or (ii) immediately and automatically if You are declared bankrupt or become insolvent. Your right to access the Network ceases, if this License terminates, and We may disable Your access to the Network upon such termination. You may terminate this License at any time by disintegrating WalletKit Items from your products and services and disabling access to the Network. If this License terminates, You must immediately cease all use of and delete the WalletKit Items, in whole or in part, as well as any Confidential Information, in Your possession or control. At Our request, You will certify Your compliance with these obligations in writing. Sections 2, 4 through 7, 8(b), and 9 through 11 will survive any termination of this Agreement. 64 | 65 | **9\. GOVERNING LAW** 66 | 67 | This Agreement will be governed and construed under the laws of the State of Delaware, U.S.A., without regard to choice of law principles, and the UN Convention on Contracts for the International Sale of Goods does not apply to this Agreement. 68 | 69 | **10\. DISPUTE RESOLUTION** 70 | 71 | PLEASE READ THE FOLLOWING SECTION CAREFULLY BECAUSE IT REQUIRES YOU TO SUBMIT TO BINDING ARBITRATION (JURY TRIAL WAIVER) OF ANY AND ALL DISPUTES (OTHER THAN SPECIFIED SMALL CLAIMS) WITH REOWN, INC. AND LIMITS THE MANNER IN WHICH YOU CAN SEEK RELIEF FROM REOWN, INC. (NO CLASS ARBITRATIONS, CLASS ACTIONS OR REPRESENTATIVE ACTIONS OR ARBITRATIONS). 72 | 73 | **a. Binding Arbitration; Disputes; Small Claims.** **You and We waive any right to a jury trial, or the right to have any Dispute resolved in any court, and instead accept the use of binding arbitration (which is the referral of a Dispute to one or more impartial persons for a final and binding determination); provided, however, that you have the right to litigate any Dispute in small claims court, if all the requirements of the small claims court, including any limitations on jurisdiction and the amount at issue in the Dispute, are satisfied. You agree to bring a Dispute in small claims court in the State of Delaware**. “**Dispute**” **as used in this Section 10 means any dispute, cause of action, claim, or controversy arising out of or in any way related to Us, this License, the subject matter of this License, or access to and use of the WalletKit Items, in whole or in part, or the Network, including but not limited to personal injury disputes and disputes that involve third parties (such as developers of applications or wallets).** 74 | 75 | **b. No Class Arbitrations, Class Actions or Representative Actions.** **You and We agree that any Dispute is personal to You and Us, and that any Dispute must only be resolved by an individual arbitration and may not be brought as a class arbitration, a class action, or any other representative proceeding. Neither party agrees to class arbitration, or an arbitration where a person brings a Dispute as a representative of any other person or persons. Neither You nor We agree that a Dispute can be brought as a class or representative action whether inside or outside of arbitration, or on behalf of any other person or persons**. 76 | 77 | **c. Federal Arbitration Act.** You and We agree that the terms of this License affect interstate commerce and that the enforceability of Section 10 is governed by, construed, and enforced, both substantively and procedurally, by the Federal Arbitration Act, 9 U.S.C. § 1 et seq. (“**FAA**”) to the maximum extent permitted by applicable law. 78 | 79 | **d. Confidentiality.** The arbitrator, You and We must maintain the confidentiality of any proceedings, including but not limited to, any and all information gathered, prepared, and presented for purposes of the arbitration or related to the Dispute(s) therein. The arbitrator has the authority to make appropriate rulings to safeguard that confidentiality, unless the law provides to the contrary. 80 | 81 | **e. Process.** Our goal is to resolve claims fairly and quickly. Accordingly, for any Dispute that you have against Us, you must first contact Us and attempt to resolve the claim informally by sending a written notice of your claim (“**Notice**”) to Us. The Notice to Us must be sent by certified mail addressed to: Reown Inc., 200 Continental Drive, 401, Newark, DE 19713 USA and a copy must be sent on the same day via email to Legal@walletconnect.com. The Notice must (a) include Your name, residence address, projectID and the email address and/or mobile telephone number associated with your account; (b) describe the nature and basis of the claim; and (c) set forth the specific relief sought. If You and We cannot reach an agreement to resolve the claim within 30 days after such Notice is received, then either party may, as appropriate in accordance with this Section 10, commence an arbitration proceeding or file a claim in small claims court. 82 | 83 | **f. Binding Arbitration.** In the event that You and We cannot resolve a Dispute and do not pursue the matter through small claims court, You or We must promptly submit the Dispute to binding arbitration before the American Arbitration Association (“**AAA**”). In the event AAA declines or is unable to administer the arbitration, You and We agree to use an arbitration forum or arbitrator that You and We mutually agree upon. If, after making a reasonable effort, You and We are not able to agree upon an arbitration forum or arbitrator, AAA or a court having proper jurisdiction will appoint an arbitration forum or arbitrator. The arbitration will be conducted in accordance with the AAA Consumer Arbitration Rules (“**AAA Rules**”) then in effect. The AAA Rules and other information about AAA and arbitration are readily available at [http://www.adr.org](http://www.adr.org/), by calling 1-800-778-7879, or by mail at 120 Broadway, Floor 21, New York, NY 10271\. By entering into this License, you either (1) acknowledge that you have read and understand the AAA Rules or (2) waive reading the AAA Rules and waive any claim that the AAA Rules are unfair in any way. You and We agree that the terms of this License govern the arbitration, and that the applicable AAA Rules are subject to changes in procedures that AAA may make from time to time. 84 | 85 | **g. Arbitrator Empowered.** As limited by the FAA, the terms of this License, and the applicable AAA Rules, the arbitrator will have the exclusive power and jurisdiction to make all procedural and substantive decisions concerning the Dispute; provided, however, that this power does not include: (i) the power to determine the question of arbitrability, which power You and We agree is vested solely in a court of competent jurisdiction; or (ii) the power to conduct a class arbitration or a representative action, which is prohibited by the terms of this License (as stated above). The arbitrator may only conduct an individual arbitration, and may not consolidate more than one person’s claims and may not preside over any form of representative or class proceeding, or any proceeding on behalf of or involving more than one person or persons. 86 | 87 | **11\. MISCELLANEOUS** 88 | 89 | **a. Amendment.** This License may be amended by Us on a prospective basis, and your usage of the License after such amendments or changes signifies your consent to and acceptance of any such amendments or changes on a going forward basis. 90 | 91 | **b. Severability.** In the event any provision of this License is determined to be invalid, prohibited or unenforceable by a court or other body of competent jurisdiction, this License must be construed as if such invalid, prohibited or unenforceable provision has been more narrowly drawn so as not to be invalid, prohibited or unenforceable. 92 | 93 | **c. Assignment.** You may not assign any rights or obligations under this License, including by operation of law, without Our prior written consent, which may be withheld in its sole discretion. We may assign Our rights or obligations under this License in Our sole discretion. 94 | 95 | **d. Waiver.** Failure of either party at any time to enforce any of the provisions of this License will not be construed as a waiver of such provisions or in any way affect the validity of this License or parts thereof. 96 | -------------------------------------------------------------------------------- /packages/walletkit/LICENSE.md: -------------------------------------------------------------------------------- 1 | **WALLETCONNECT COMMUNITY LICENSE AGREEMENT** 2 | 3 | **Release Date: 20 August 2025** 4 | 5 | You, or the company or organization of which you are an employee, agent, or contractor (“**You**”, “**Your**”), desire a license from Reown, Inc. (“**We**”, “**Us**”, “**Our**”) to use the WalletKit software development kit, documentation and related materials (collectively, “**WalletKit Items**”). Before You access the WalletKit Items, We need to establish what you can expect from Us, and what We expect from You. By accessing and using the WalletKit Items, You confirm that You reviewed and You agree to this License Agreement (“**License**”), and that You are authorized to bind the company or organization you represent to this License. **_By downloading, installing, integrating and/or using the_** \*WalletKit **Items, you or the organization you represent, as applicable, agree to be bound by and become a party to this License as a "Licensee". If you do not agree to all of the terms of this License, you are not a Licensee and you may not download, install, or use (or must cease using) the WalletKit Items.\*** 6 | 7 | **1\. LICENSE** 8 | 9 | **a. Grant of Rights.** Subject to the terms and conditions of this License We grant You (and not Your affiliates or related persons), and You accept, a perpetual (subject to Section 8(b) Termination), non-exclusive, non-transferable (subject to Section 11(c) Assignment), non-sublicensable, and worldwide license under Our intellectual property rights to use, display, copy, transmit, distribute, modify, adapt, and create derivative works of the WalletKit Items. 10 | 11 | **b. Educational, Research and New Application Uses.** If You are using the WalletKit Items for educational or non-commercial research purposes, the License We grant You is royalty-free. In addition, Your use of WalletKit Items for development, maintenance and enhancement of Your applications and wallets is royalty-free, subject to Section 3\. 12 | 13 | **c. Modification of WalletKit Items.** We own all right, title and interest to the intellectual property rights (including, but not limited to patent, copyright, and trademark rights) to any modification to or derivative work of the WalletKit Items that You may develop, and you hereby assign any and all such rights to such modifications and derivative works to Us, subject to the following: 14 | 15 | (i) We hereby grant back to You a perpetual, non-exclusive, non-transferable, royalty-free, worldwide right and license to use these modifications and derivative works solely in conjunction with the WalletKit Items and only while this License remains in effect. You must cause any modified files to carry prominent notices stating that You changed the files. We and our assignees and Our and their licensees may use such modifications and derivative works without any approval from You and without compensation or attribution to You. You also agree to provide the source and binary code of any such modifications and derivative works to Us at the email address We may designate from time to time. 16 | 17 | (ii) For the avoidance of doubt, Your applications and wallets that you develop using the WalletKit Items are not modifications or derivative works of the WalletKit Items. You retain all rights to your applications and wallets, and you have no obligation to share or license Your applications or wallets with Us or with any third parties; provided, however, We retain all rights to the WalletKit Items and any modifications or derivative works thereof that may be incorporated into your applications and wallets. 18 | 19 | **2\. REDISTRIBUTION AND USE** 20 | 21 | **a. Distribution Requirements.** If You distribute or make available the WalletKit Items (including any modifications to or derivative works thereof) or any product or service that uses them (including Your applications and wallets), You must (i) include the following copyright notice in the notices or readme.txt of the product, or the about section of the website for the services: “Portions © 2025 Reown, Inc. All Rights Reserved” ; (ii) provide a copy of this License Agreement with any applications or wallets containing such WalletKit Items or that are required for the use thereof; and (iii) applicable logo and branding. 22 | 23 | **b. Network Connection.** As a material condition of this License, all use, distribution or derivatives works of the WalletKit Items must connect to the proprietary Reown gateway infrastructure, or its successor, and its related WalletConnect messaging protocol (collectively, the “**Network**”) unless we explicitly approve you doing otherwise. You further agree not to circumvent this requirement, or instruct or suggest to Your end users to do so. 24 | 25 | **c. Compliance with Law.** We are not in a position to monitor and assess the legality and regulatory treatment of your business and operations. Your use of the WalletKit Items must comply with all applicable laws and regulations and Our Terms of Service and Acceptable Use Policy, as amended from time to time, the terms of which are incorporated herein by reference. 26 | 27 | **d. Use of Marks.** This License does not grant permission to use Our trade names, trademarks, service marks, or product names (collectively, the “**Marks**”), except as permitted by Our Marks usage guidelines or as required for disclosure of the development of Your applications or wallets, reproducing the attribution notice in the readme.txt file, or Your redistribution of WalletKit Items. We reserve all rights not expressly granted to you under this License. Unless explicitly approved by Us, in no event will You use Our name, trademarks, or logos to endorse or imply Our endorsement of products derived from WalletKit Items or any products or services that use WalletKit Items. Where You are permitted to use the Marks, You must comply with Our then-current trademark and logo usage guidelines, as may be updated from time to time without notice from Us. All goodwill arising out of Your use of any Mark will inure solely to Us. 28 | 29 | **e. Export Controls.** You and We acknowledge that the WalletKit Items and all related technical information, documents and materials are subject to export controls under the U.S. Export Administration Regulations. You and We will (i) comply strictly with all legal requirements established under these controls, (ii) cooperate fully with each other in any official or unofficial audit or inspection that relates to these controls and (iii) not export, re-export, divert or transfer, directly or indirectly, any such item or direct products thereof to Cuba, Iran, North Korea, Sudan, and the Crimea, Donetsk, Luhansk, Kherson and Zaporizhzhia region not controlled by the government of Ukraine, or any national thereof or to any country or national thereof that is embargoed by Executive order, unless such party has obtained the prior written authorization of the other party and U.S. Commerce Department. We may modify this list to conform to changes in the U.S. Export Administration Regulations. 30 | 31 | **3\. LIMITED COMMERCIAL USE** 32 | 33 | If the number of remote processing calls (RPCs) made by, or monthly active users of (MAUs), associated with Your application or wallet developed using WalletKit Items, or your product or service that uses WalletKit Items exceed the thresholds set forth below, determined in our sole discretion, You must enter into and pay applicable fees and charges for a commercial license. You may contact Reown for these purposes using [this form](https://share.hsforms.com/1_oWa8QkwRXi6oR0nZ_SIxQnxw6s). 34 | 35 | - Monthly RPC limit of 2,500,000 36 | 37 | - Monthly MAU limit of 500 38 | 39 | MAU means, as applicable: (1) a unique externally owned account that authenticates and connects to your app in a 30 day period; or (2) a unique embedded wallet that is created on, and authenticates and connects, to your app in a 30 day period 40 | 41 | We reserve the right to change the RPC and/or MAU limit thresholds and/or set different or additional triggers or thresholds for requiring a commercial license. The current thresholds or triggers for a commercial license may be published on Our [Terms of Service](https://reown.com/terms-of-service). 42 | 43 | **4\. DISCLAIMER OF WARRANTY** 44 | 45 | TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THE WALLETKIT ITEMS ARE PROVIDED “AS IS” WITHOUT WARRANTIES OF ANY KIND. ALL WARRANTIES, CONDITIONS, REPRESENTATIONS, INDEMNITIES AND GUARANTEES WITH RESPECT TO THE WALLETKIT ITEMS, WHETHER EXPRESS OR IMPLIED, ARISING BY LAW, CUSTOM, PRIOR ORAL OR WRITTEN STATEMENTS BY US OR OTHERWISE (INCLUDING, BUT NOT LIMITED TO ANY WARRANTY OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR PARTICULAR PURPOSE AND NON-INFRINGEMENT) ARE HEREBY OVERRIDDEN, EXCLUDED AND DISCLAIMED. SOME JURISDICTIONS DO NOT PERMIT THE DISCLAIMER OF ALL WARRANTIES SO YOU MAY HAVE ADDITIONAL RIGHTS PROVIDED BY LAW. 46 | 47 | **5\. LIMITATION OF LIABILITY** 48 | 49 | IN NO EVENT AND UNDER NO LEGAL THEORY, WHETHER IN TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, UNLESS REQUIRED BY APPLICABLE LAW DESPITE THIS LIMITATION OR AGREED TO IN WRITING BY US, WILL WE BE LIABLE TO YOU OR YOUR LICENSEES OR SUBLICENSEES FOR ANY DAMAGES, INCLUDING ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER ARISING AS A RESULT OF THIS LICENSE OR OUT OF THE USE OR INABILITY TO USE THE WALLETKIT ITEMS OR ANY PORTION THEREOF (INCLUDING BUT NOT LIMITED TO DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES), EVEN IF WE OR OUR AFFILIATES HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. SOME JURISDICTIONS DO NOT PERMIT THE EXCLUSION OF ALL DAMAGES SO YOU MAY HAVE ADDITIONAL RIGHTS PROVIDED BY LAW. 50 | 51 | **6\. INTELLECTUAL PROPERTY** 52 | 53 | All trademarks, service marks, patents, copyrights, trade secrets, or other proprietary rights in or related to the WalletKit Items are and will remain Our exclusive property (or that of Our licensors). You will not take any action that jeopardizes those rights, nor will you acquire any right in the WalletKit Items, except the limited use rights specified in this License. You acknowledge that We (or Our licensors) will own all rights in any copy, translation, modification, adaptation or derivative works of the WalletKit Items (whether or not authorized by Us), and You will provide any instrument We reasonably request that you obtain to give full legal effect to Our rights under this Section. 54 | 55 | **7\. CONFIDENTIALITY** 56 | 57 | You acknowledge that the WalletKit Items incorporate nonpublic confidential and proprietary information We have developed or acquired or licensed ("**Confidential Information**"). You will keep the Confidential Information in the strictest confidence and will not use or copy any Confidential Information, or disclose it to any other person. You will not permit removal or defacing of any confidentiality or proprietary notice placed on or in the WalletKit Items, and any copyright notices on or in these items does not constitute publication or make them non-confidential. 58 | 59 | **8\. TERM AND TERMINATION** 60 | 61 | **a. Term.** The term of this Agreement will commence upon Your accessing the WalletKit Items and will continue in full force and effect until terminated in accordance with the terms and conditions of this License. As this License is updated for future releases of the WalletKit Items, You must abide by and meet all requirements of future updates of this License for those future releases of WalletKit Items, and such future updates of this License will apply to such future applications and wallets that may be developed with that future release of WalletKit Items or any portion thereof. You may not use older versions of the SDK to circumvent the terms applicable to new versions you choose to download or use. 62 | 63 | **b. Termination.** This License terminates (i) 30 days after either We notify you in writing that You have breached a material term of this License, if that breach is not cured before then, or (ii) immediately and automatically if You are declared bankrupt or become insolvent. Your right to access the Network ceases, if this License terminates, and We may disable Your access to the Network upon such termination. You may terminate this License at any time by disintegrating WalletKit Items from your products and services and disabling access to the Network. If this License terminates, You must immediately cease all use of and delete the WalletKit Items, in whole or in part, as well as any Confidential Information, in Your possession or control. At Our request, You will certify Your compliance with these obligations in writing. Sections 2, 4 through 7, 8(b), and 9 through 11 will survive any termination of this Agreement. 64 | 65 | **9\. GOVERNING LAW** 66 | 67 | This Agreement will be governed and construed under the laws of the State of Delaware, U.S.A., without regard to choice of law principles, and the UN Convention on Contracts for the International Sale of Goods does not apply to this Agreement. 68 | 69 | **10\. DISPUTE RESOLUTION** 70 | 71 | PLEASE READ THE FOLLOWING SECTION CAREFULLY BECAUSE IT REQUIRES YOU TO SUBMIT TO BINDING ARBITRATION (JURY TRIAL WAIVER) OF ANY AND ALL DISPUTES (OTHER THAN SPECIFIED SMALL CLAIMS) WITH REOWN, INC. AND LIMITS THE MANNER IN WHICH YOU CAN SEEK RELIEF FROM REOWN, INC. (NO CLASS ARBITRATIONS, CLASS ACTIONS OR REPRESENTATIVE ACTIONS OR ARBITRATIONS). 72 | 73 | **a. Binding Arbitration; Disputes; Small Claims.** **You and We waive any right to a jury trial, or the right to have any Dispute resolved in any court, and instead accept the use of binding arbitration (which is the referral of a Dispute to one or more impartial persons for a final and binding determination); provided, however, that you have the right to litigate any Dispute in small claims court, if all the requirements of the small claims court, including any limitations on jurisdiction and the amount at issue in the Dispute, are satisfied. You agree to bring a Dispute in small claims court in the State of Delaware**. “**Dispute**” **as used in this Section 10 means any dispute, cause of action, claim, or controversy arising out of or in any way related to Us, this License, the subject matter of this License, or access to and use of the WalletKit Items, in whole or in part, or the Network, including but not limited to personal injury disputes and disputes that involve third parties (such as developers of applications or wallets).** 74 | 75 | **b. No Class Arbitrations, Class Actions or Representative Actions.** **You and We agree that any Dispute is personal to You and Us, and that any Dispute must only be resolved by an individual arbitration and may not be brought as a class arbitration, a class action, or any other representative proceeding. Neither party agrees to class arbitration, or an arbitration where a person brings a Dispute as a representative of any other person or persons. Neither You nor We agree that a Dispute can be brought as a class or representative action whether inside or outside of arbitration, or on behalf of any other person or persons**. 76 | 77 | **c. Federal Arbitration Act.** You and We agree that the terms of this License affect interstate commerce and that the enforceability of Section 10 is governed by, construed, and enforced, both substantively and procedurally, by the Federal Arbitration Act, 9 U.S.C. § 1 et seq. (“**FAA**”) to the maximum extent permitted by applicable law. 78 | 79 | **d. Confidentiality.** The arbitrator, You and We must maintain the confidentiality of any proceedings, including but not limited to, any and all information gathered, prepared, and presented for purposes of the arbitration or related to the Dispute(s) therein. The arbitrator has the authority to make appropriate rulings to safeguard that confidentiality, unless the law provides to the contrary. 80 | 81 | **e. Process.** Our goal is to resolve claims fairly and quickly. Accordingly, for any Dispute that you have against Us, you must first contact Us and attempt to resolve the claim informally by sending a written notice of your claim (“**Notice**”) to Us. The Notice to Us must be sent by certified mail addressed to: Reown Inc., 200 Continental Drive, 401, Newark, DE 19713 USA and a copy must be sent on the same day via email to Legal@walletconnect.com. The Notice must (a) include Your name, residence address, projectID and the email address and/or mobile telephone number associated with your account; (b) describe the nature and basis of the claim; and (c) set forth the specific relief sought. If You and We cannot reach an agreement to resolve the claim within 30 days after such Notice is received, then either party may, as appropriate in accordance with this Section 10, commence an arbitration proceeding or file a claim in small claims court. 82 | 83 | **f. Binding Arbitration.** In the event that You and We cannot resolve a Dispute and do not pursue the matter through small claims court, You or We must promptly submit the Dispute to binding arbitration before the American Arbitration Association (“**AAA**”). In the event AAA declines or is unable to administer the arbitration, You and We agree to use an arbitration forum or arbitrator that You and We mutually agree upon. If, after making a reasonable effort, You and We are not able to agree upon an arbitration forum or arbitrator, AAA or a court having proper jurisdiction will appoint an arbitration forum or arbitrator. The arbitration will be conducted in accordance with the AAA Consumer Arbitration Rules (“**AAA Rules**”) then in effect. The AAA Rules and other information about AAA and arbitration are readily available at [http://www.adr.org](http://www.adr.org/), by calling 1-800-778-7879, or by mail at 120 Broadway, Floor 21, New York, NY 10271\. By entering into this License, you either (1) acknowledge that you have read and understand the AAA Rules or (2) waive reading the AAA Rules and waive any claim that the AAA Rules are unfair in any way. You and We agree that the terms of this License govern the arbitration, and that the applicable AAA Rules are subject to changes in procedures that AAA may make from time to time. 84 | 85 | **g. Arbitrator Empowered.** As limited by the FAA, the terms of this License, and the applicable AAA Rules, the arbitrator will have the exclusive power and jurisdiction to make all procedural and substantive decisions concerning the Dispute; provided, however, that this power does not include: (i) the power to determine the question of arbitrability, which power You and We agree is vested solely in a court of competent jurisdiction; or (ii) the power to conduct a class arbitration or a representative action, which is prohibited by the terms of this License (as stated above). The arbitrator may only conduct an individual arbitration, and may not consolidate more than one person’s claims and may not preside over any form of representative or class proceeding, or any proceeding on behalf of or involving more than one person or persons. 86 | 87 | **11\. MISCELLANEOUS** 88 | 89 | **a. Amendment.** This License may be amended by Us on a prospective basis, and your usage of the License after such amendments or changes signifies your consent to and acceptance of any such amendments or changes on a going forward basis. 90 | 91 | **b. Severability.** In the event any provision of this License is determined to be invalid, prohibited or unenforceable by a court or other body of competent jurisdiction, this License must be construed as if such invalid, prohibited or unenforceable provision has been more narrowly drawn so as not to be invalid, prohibited or unenforceable. 92 | 93 | **c. Assignment.** You may not assign any rights or obligations under this License, including by operation of law, without Our prior written consent, which may be withheld in its sole discretion. We may assign Our rights or obligations under this License in Our sole discretion. 94 | 95 | **d. Waiver.** Failure of either party at any time to enforce any of the provisions of this License will not be construed as a waiver of such provisions or in any way affect the validity of this License or parts thereof. 96 | -------------------------------------------------------------------------------- /packages/walletkit/test/sign.spec.ts: -------------------------------------------------------------------------------- 1 | import { TEST_METADATA } from "./shared/values"; 2 | import { Core, RELAYER_EVENTS } from "@walletconnect/core"; 3 | import { 4 | JsonRpcPayload, 5 | formatJsonRpcResult, 6 | isJsonRpcRequest, 7 | } from "@walletconnect/jsonrpc-utils"; 8 | import { SignClient, ENGINE_RPC_OPTS } from "@walletconnect/sign-client"; 9 | import { AuthTypes, CoreTypes, ICore, ISignClient, SessionTypes } from "@walletconnect/types"; 10 | import { buildApprovedNamespaces, buildAuthObject, getSdkError } from "@walletconnect/utils"; 11 | import { toMiliseconds } from "@walletconnect/time"; 12 | import { Wallet as CryptoWallet } from "@ethersproject/wallet"; 13 | 14 | import { expect, describe, it, beforeEach, vi, beforeAll, afterEach } from "vitest"; 15 | import { WalletKit, IWalletKit, WalletKitTypes } from "../src"; 16 | import { 17 | disconnect, 18 | TEST_CORE_OPTIONS, 19 | TEST_ETHEREUM_CHAIN, 20 | TEST_NAMESPACES, 21 | TEST_REQUIRED_NAMESPACES, 22 | TEST_UPDATED_NAMESPACES, 23 | } from "./shared"; 24 | 25 | describe("Sign Integration", () => { 26 | let core: ICore; 27 | let wallet: IWalletKit; 28 | let dapp: ISignClient; 29 | let uriString: string; 30 | let sessionApproval: () => Promise; 31 | let session: SessionTypes.Struct; 32 | let cryptoWallet: CryptoWallet; 33 | 34 | beforeAll(() => { 35 | cryptoWallet = CryptoWallet.createRandom(); 36 | }); 37 | 38 | afterEach(async () => { 39 | await disconnect(wallet.core); 40 | await disconnect(dapp.core); 41 | }); 42 | 43 | beforeEach(async () => { 44 | core = new Core({ 45 | ...TEST_CORE_OPTIONS, 46 | customStoragePrefix: Math.random().toString(36).substring(2, 15), 47 | }); 48 | dapp = await SignClient.init({ 49 | ...TEST_CORE_OPTIONS, 50 | name: "Dapp", 51 | metadata: TEST_METADATA, 52 | customStoragePrefix: Math.random().toString(36).substring(2, 15), 53 | }); 54 | const { uri, approval } = await dapp.connect({ 55 | requiredNamespaces: TEST_REQUIRED_NAMESPACES, 56 | }); 57 | uriString = uri || ""; 58 | sessionApproval = approval; 59 | const signConfig = { disableRequestQueue: true }; 60 | wallet = await WalletKit.init({ 61 | core, 62 | name: "wallet", 63 | metadata: TEST_METADATA, 64 | signConfig, 65 | }); 66 | expect(wallet).to.be.exist; 67 | expect(dapp).to.be.exist; 68 | expect(core).to.be.exist; 69 | expect(wallet.metadata.redirect).to.not.exist; 70 | expect(dapp.metadata.redirect).to.not.exist; 71 | expect(wallet.engine.signClient.signConfig).to.toMatchObject(signConfig); 72 | }); 73 | 74 | it("should approve session proposal", async () => { 75 | const sessionConfig = { disableDeepLink: false }; 76 | await Promise.all([ 77 | new Promise((resolve) => { 78 | wallet.on("session_proposal", async (sessionProposal) => { 79 | const { id, params, verifyContext } = sessionProposal; 80 | expect(verifyContext.verified.validation).to.eq("UNKNOWN"); 81 | expect(verifyContext.verified.isScam).to.eq(undefined); 82 | session = await wallet.approveSession({ 83 | id, 84 | namespaces: TEST_NAMESPACES, 85 | sessionConfig, 86 | }); 87 | expect(params.optionalNamespaces).to.toMatchObject(TEST_REQUIRED_NAMESPACES); 88 | resolve(session); 89 | }); 90 | }), 91 | new Promise(async (resolve) => { 92 | resolve(await sessionApproval()); 93 | }), 94 | wallet.pair({ uri: uriString }), 95 | ]); 96 | expect(session).to.be.exist; 97 | expect(session.topic).to.be.exist; 98 | expect(session.sessionConfig).to.eql(sessionConfig); 99 | }); 100 | it("should reject session proposal", async () => { 101 | const rejectionError = getSdkError("USER_REJECTED"); 102 | await Promise.all([ 103 | new Promise((resolve) => { 104 | wallet.on("session_proposal", async (sessionProposal) => { 105 | const { params } = sessionProposal; 106 | expect(params.optionalNamespaces).to.toMatchObject(TEST_REQUIRED_NAMESPACES); 107 | await wallet.rejectSession({ 108 | id: params.id, 109 | reason: rejectionError, 110 | }); 111 | resolve(); 112 | }); 113 | }), 114 | new Promise(async (resolve) => { 115 | // catch the rejection and compare 116 | try { 117 | await sessionApproval(); 118 | } catch (err) { 119 | expect(err).to.toMatchObject(rejectionError); 120 | } 121 | resolve(); 122 | }), 123 | wallet.pair({ uri: uriString }), 124 | ]); 125 | }); 126 | it("should update session", async () => { 127 | // first pair and approve session 128 | await Promise.all([ 129 | new Promise((resolve) => { 130 | wallet.on("session_proposal", async (sessionProposal) => { 131 | const { id, params, verifyContext } = sessionProposal; 132 | expect(verifyContext.verified.validation).to.eq("UNKNOWN"); 133 | session = await wallet.approveSession({ 134 | id, 135 | namespaces: TEST_NAMESPACES, 136 | }); 137 | expect(params.optionalNamespaces).to.toMatchObject(TEST_REQUIRED_NAMESPACES); 138 | resolve(session); 139 | }); 140 | }), 141 | sessionApproval(), 142 | wallet.pair({ uri: uriString }), 143 | ]); 144 | 145 | expect(TEST_NAMESPACES).not.toMatchObject(TEST_UPDATED_NAMESPACES); 146 | // update the session 147 | await Promise.all([ 148 | new Promise((resolve) => { 149 | dapp.events.on("session_update", (session) => { 150 | const { params } = session; 151 | expect(params.namespaces).to.toMatchObject(TEST_UPDATED_NAMESPACES); 152 | resolve(session); 153 | }); 154 | }), 155 | wallet.updateSession({ topic: session.topic, namespaces: TEST_UPDATED_NAMESPACES }), 156 | ]); 157 | }); 158 | 159 | it("should update session while peer is offline", async () => { 160 | // first pair and approve session 161 | await Promise.all([ 162 | new Promise((resolve) => { 163 | wallet.on("session_proposal", async (sessionProposal) => { 164 | const { id, params, verifyContext } = sessionProposal; 165 | expect(verifyContext.verified.validation).to.eq("UNKNOWN"); 166 | session = await wallet.approveSession({ 167 | id, 168 | namespaces: TEST_NAMESPACES, 169 | }); 170 | expect(params.optionalNamespaces).to.toMatchObject(TEST_REQUIRED_NAMESPACES); 171 | resolve(session); 172 | }); 173 | }), 174 | sessionApproval(), 175 | wallet.pair({ uri: uriString }), 176 | ]); 177 | expect(TEST_NAMESPACES).not.toMatchObject(TEST_UPDATED_NAMESPACES); 178 | // close the transport to simulate peer being offline 179 | await dapp.core.relayer.transportClose(); 180 | const updatedChain = "eip155:55"; 181 | const updatedAddress = `${updatedChain}:${cryptoWallet.address}`; 182 | 183 | await new Promise((resolve) => setTimeout(resolve, 1000)); 184 | // update the session 185 | await new Promise(async (resolve) => { 186 | await wallet.updateSession({ 187 | topic: session.topic, 188 | namespaces: { 189 | eip155: { 190 | ...TEST_UPDATED_NAMESPACES.eip155, 191 | accounts: [...TEST_UPDATED_NAMESPACES.eip155.accounts, updatedAddress], 192 | }, 193 | }, 194 | }); 195 | await wallet.emitSessionEvent({ 196 | topic: session.topic, 197 | event: { 198 | name: "chainChanged", 199 | data: updatedChain, 200 | }, 201 | chainId: updatedChain, 202 | }); 203 | await wallet.emitSessionEvent({ 204 | topic: session.topic, 205 | event: { 206 | name: "accountsChanged", 207 | data: [updatedAddress], 208 | }, 209 | chainId: updatedChain, 210 | }); 211 | resolve(); 212 | }); 213 | await Promise.all([ 214 | new Promise((resolve) => { 215 | dapp.events.on("session_update", (session) => { 216 | resolve(session); 217 | }); 218 | }), 219 | new Promise((resolve) => { 220 | dapp.events.on("session_event", (event) => { 221 | const { params } = event; 222 | if (params.event.name === "chainChanged") { 223 | expect(params.event.data).to.equal(updatedChain); 224 | resolve(event); 225 | } 226 | }); 227 | }), 228 | new Promise((resolve) => { 229 | dapp.events.on("session_event", (event) => { 230 | const { params } = event; 231 | if (params.event.name === "accountsChanged") { 232 | console; 233 | expect(params.event.data[0]).to.equal(updatedAddress); 234 | resolve(event); 235 | } 236 | }); 237 | }), 238 | dapp.core.relayer.transportOpen(), 239 | ]); 240 | }); 241 | 242 | it("should extend session", async () => { 243 | // first pair and approve session 244 | await Promise.all([ 245 | new Promise((resolve) => { 246 | wallet.on("session_proposal", async (sessionProposal) => { 247 | const { id, params } = sessionProposal; 248 | session = await wallet.approveSession({ 249 | id, 250 | namespaces: TEST_NAMESPACES, 251 | }); 252 | expect(params.optionalNamespaces).to.toMatchObject(TEST_REQUIRED_NAMESPACES); 253 | resolve(session); 254 | }); 255 | }), 256 | sessionApproval(), 257 | wallet.pair({ uri: uriString }), 258 | ]); 259 | 260 | const prevExpiry = session.expiry; 261 | const topic = session.topic; 262 | vi.useFakeTimers(); 263 | // Fast-forward system time by 60 seconds after expiry was first set. 264 | vi.setSystemTime(Date.now() + 60_000); 265 | await wallet.extendSession({ topic }); 266 | const updatedExpiry = wallet.engine.signClient.session.get(topic).expiry; 267 | expect(updatedExpiry).to.be.greaterThan(prevExpiry); 268 | vi.useRealTimers(); 269 | }); 270 | 271 | it("should respond to session request", async () => { 272 | // first pair and approve session 273 | await Promise.all([ 274 | new Promise((resolve) => { 275 | wallet.on("session_proposal", async (sessionProposal) => { 276 | const { id, params } = sessionProposal; 277 | session = await wallet.approveSession({ 278 | id, 279 | namespaces: { 280 | eip155: { 281 | ...TEST_NAMESPACES.eip155, 282 | accounts: [`${TEST_ETHEREUM_CHAIN}:${cryptoWallet.address}`], 283 | }, 284 | }, 285 | }); 286 | expect(params.optionalNamespaces).to.toMatchObject(TEST_REQUIRED_NAMESPACES); 287 | resolve(session); 288 | }); 289 | }), 290 | sessionApproval(), 291 | wallet.pair({ uri: uriString }), 292 | ]); 293 | 294 | await Promise.all([ 295 | new Promise((resolve) => { 296 | wallet.on("session_request", async (sessionRequest) => { 297 | const { id, params, verifyContext } = sessionRequest; 298 | expect(verifyContext.verified.validation).to.eq("UNKNOWN"); 299 | const signTransaction = params.request.params[0]; 300 | const signature = await cryptoWallet.signTransaction(signTransaction); 301 | const response = await wallet.respondSessionRequest({ 302 | topic: session.topic, 303 | response: formatJsonRpcResult(id, signature), 304 | }); 305 | resolve(response); 306 | }); 307 | }), 308 | new Promise(async (resolve) => { 309 | const result = await dapp.request({ 310 | topic: session.topic, 311 | request: { 312 | method: "eth_signTransaction", 313 | params: [ 314 | { 315 | from: cryptoWallet.address, 316 | to: cryptoWallet.address, 317 | data: "0x", 318 | nonce: "0x01", 319 | gasPrice: "0x020a7ac094", 320 | gasLimit: "0x5208", 321 | value: "0x00", 322 | }, 323 | ], 324 | }, 325 | chainId: TEST_ETHEREUM_CHAIN, 326 | }); 327 | expect(result).to.be.exist; 328 | expect(result).to.be.a("string"); 329 | resolve(); 330 | }), 331 | ]); 332 | }); 333 | 334 | it("should disconnect from session", async () => { 335 | // first pair and approve session 336 | await Promise.all([ 337 | new Promise((resolve) => { 338 | wallet.on("session_proposal", async (sessionProposal) => { 339 | const { id, params } = sessionProposal; 340 | session = await wallet.approveSession({ 341 | id, 342 | namespaces: { 343 | eip155: { 344 | ...TEST_NAMESPACES.eip155, 345 | accounts: [`${TEST_ETHEREUM_CHAIN}:${cryptoWallet.address}`], 346 | }, 347 | }, 348 | }); 349 | expect(params.optionalNamespaces).to.toMatchObject(TEST_REQUIRED_NAMESPACES); 350 | resolve(session); 351 | }); 352 | }), 353 | sessionApproval(), 354 | wallet.pair({ uri: uriString }), 355 | ]); 356 | 357 | const reason = getSdkError("USER_DISCONNECTED"); 358 | await Promise.all([ 359 | new Promise((resolve) => { 360 | dapp.events.on("session_delete", (sessionDelete) => { 361 | const { topic } = sessionDelete; 362 | expect(topic).to.be.eq(session.topic); 363 | resolve(); 364 | }); 365 | }), 366 | wallet.disconnectSession({ topic: session.topic, reason }), 367 | ]); 368 | }); 369 | 370 | it("should receive session_disconnect", async () => { 371 | // first pair and approve session 372 | await Promise.all([ 373 | new Promise((resolve) => { 374 | wallet.on("session_proposal", async (sessionProposal) => { 375 | const { id, params } = sessionProposal; 376 | session = await wallet.approveSession({ 377 | id, 378 | namespaces: { 379 | eip155: { 380 | ...TEST_NAMESPACES.eip155, 381 | accounts: [`${TEST_ETHEREUM_CHAIN}:${cryptoWallet.address}`], 382 | }, 383 | }, 384 | }); 385 | expect(params.optionalNamespaces).to.toMatchObject(TEST_REQUIRED_NAMESPACES); 386 | resolve(session); 387 | }); 388 | }), 389 | sessionApproval(), 390 | wallet.pair({ uri: uriString }), 391 | ]); 392 | 393 | const reason = getSdkError("USER_DISCONNECTED"); 394 | await Promise.all([ 395 | new Promise((resolve) => { 396 | wallet.on("session_delete", (sessionDelete) => { 397 | const { topic } = sessionDelete; 398 | expect(topic).to.be.eq(session.topic); 399 | resolve(); 400 | }); 401 | }), 402 | dapp.disconnect({ topic: session.topic, reason }), 403 | ]); 404 | }); 405 | 406 | it("should emit session event", async () => { 407 | // first pair and approve session 408 | await Promise.all([ 409 | new Promise((resolve) => { 410 | wallet.on("session_proposal", async (sessionProposal) => { 411 | const { id, params } = sessionProposal; 412 | session = await wallet.approveSession({ 413 | id, 414 | namespaces: { 415 | eip155: { 416 | ...TEST_NAMESPACES.eip155, 417 | accounts: [`${TEST_ETHEREUM_CHAIN}:${cryptoWallet.address}`], 418 | }, 419 | }, 420 | }); 421 | expect(params.optionalNamespaces).to.toMatchObject(TEST_REQUIRED_NAMESPACES); 422 | resolve(session); 423 | }); 424 | }), 425 | sessionApproval(), 426 | wallet.pair({ uri: uriString }), 427 | ]); 428 | const sessionEvent = { 429 | topic: session.topic, 430 | event: { 431 | name: "chainChanged", 432 | }, 433 | chainId: TEST_REQUIRED_NAMESPACES.eip155.chains[0], 434 | }; 435 | await Promise.all([ 436 | new Promise((resolve) => { 437 | dapp.events.on("session_event", (eventPayload) => { 438 | const { topic, params } = eventPayload; 439 | expect(topic).to.be.eq(sessionEvent.topic); 440 | expect(params.event).to.toMatchObject(sessionEvent.event); 441 | resolve(); 442 | }); 443 | }), 444 | wallet.emitSessionEvent(sessionEvent), 445 | ]); 446 | }); 447 | 448 | it("should get active sessions", async () => { 449 | // first pair and approve session 450 | await Promise.all([ 451 | new Promise((resolve) => { 452 | wallet.on("session_proposal", async (sessionProposal) => { 453 | const { id, params } = sessionProposal; 454 | session = await wallet.approveSession({ 455 | id, 456 | namespaces: { 457 | eip155: { 458 | ...TEST_NAMESPACES.eip155, 459 | accounts: [`${TEST_ETHEREUM_CHAIN}:${cryptoWallet.address}`], 460 | }, 461 | }, 462 | }); 463 | expect(params.optionalNamespaces).to.toMatchObject(TEST_REQUIRED_NAMESPACES); 464 | resolve(session); 465 | }); 466 | }), 467 | sessionApproval(), 468 | wallet.pair({ uri: uriString }), 469 | ]); 470 | 471 | const sessions = wallet.getActiveSessions(); 472 | expect(sessions).to.be.exist; 473 | expect(Object.values(sessions).length).to.be.eq(1); 474 | expect(Object.keys(sessions)[0]).to.be.eq(session.topic); 475 | }); 476 | 477 | it("should handle multiple session proposal listeners correctly", async () => { 478 | const firstHandler = vi.fn(); 479 | const secondHandler = vi.fn(); 480 | 481 | await Promise.all([ 482 | new Promise((resolve) => { 483 | wallet.on("session_proposal", firstHandler); 484 | wallet.on("session_proposal", secondHandler); 485 | wallet.on("session_proposal", () => { 486 | resolve(); 487 | }); 488 | }), 489 | wallet.pair({ uri: uriString }), 490 | ]); 491 | 492 | expect(firstHandler.mock.calls).toHaveLength(1); 493 | expect(secondHandler.mock.calls).toHaveLength(1); 494 | }); 495 | 496 | it("should get pending session proposals", async () => { 497 | // first pair and approve session 498 | await Promise.all([ 499 | new Promise((resolve) => { 500 | wallet.on("session_proposal", () => { 501 | const proposals = wallet.getPendingSessionProposals(); 502 | expect(proposals).to.be.exist; 503 | expect(Object.values(proposals).length).to.be.eq(1); 504 | expect(proposals[0].optionalNamespaces).to.toMatchObject(TEST_REQUIRED_NAMESPACES); 505 | resolve(); 506 | }); 507 | }), 508 | wallet.pair({ uri: uriString }), 509 | ]); 510 | }); 511 | 512 | it.skip("receive proposal_expire event", async () => { 513 | const { uri: uriString } = await dapp.connect({ requiredNamespaces: TEST_REQUIRED_NAMESPACES }); 514 | 515 | // first pair and approve session 516 | await Promise.all([ 517 | new Promise((resolve) => { 518 | wallet.once("session_proposal", () => { 519 | vi.useFakeTimers({ 520 | shouldAdvanceTime: true, 521 | }); 522 | // Fast-forward system time by 4 min 58 seconds after expiry was first set. 523 | vi.setSystemTime( 524 | Date.now() + toMiliseconds(ENGINE_RPC_OPTS.wc_sessionPropose.req.ttl - 2), 525 | ); 526 | }); 527 | wallet.on("session_proposal", async (event) => { 528 | const { id } = event; 529 | const startTimer = Date.now(); 530 | await new Promise((resolve) => { 531 | wallet.on("proposal_expire", (event) => { 532 | const { id: expiredId } = event; 533 | if (id === expiredId) { 534 | expect(startTimer).to.be.approximately(Date.now(), 5000); // 5 seconds delta for heartbeat 535 | resolve(); 536 | } 537 | }); 538 | }); 539 | resolve(); 540 | }); 541 | }), 542 | wallet.pair({ uri: uriString! }), 543 | ]); 544 | vi.useRealTimers(); 545 | }); 546 | 547 | it("should get pending session requests", async () => { 548 | // first pair and approve session 549 | await Promise.all([ 550 | new Promise((resolve) => { 551 | wallet.on("session_proposal", async (sessionProposal) => { 552 | const { id, params } = sessionProposal; 553 | session = await wallet.approveSession({ 554 | id, 555 | namespaces: { 556 | eip155: { 557 | ...TEST_NAMESPACES.eip155, 558 | accounts: [`${TEST_ETHEREUM_CHAIN}:${cryptoWallet.address}`], 559 | }, 560 | }, 561 | }); 562 | expect(params.optionalNamespaces).to.toMatchObject(TEST_REQUIRED_NAMESPACES); 563 | resolve(session); 564 | }); 565 | }), 566 | sessionApproval(), 567 | wallet.pair({ uri: uriString }), 568 | ]); 569 | 570 | const requestParams = { 571 | method: "eth_signTransaction", 572 | params: [ 573 | { 574 | from: cryptoWallet.address, 575 | to: cryptoWallet.address, 576 | data: "0x", 577 | nonce: "0x01", 578 | gasPrice: "0x020a7ac094", 579 | gasLimit: "0x5208", 580 | value: "0x00", 581 | }, 582 | ], 583 | }; 584 | 585 | await Promise.all([ 586 | new Promise((resolve) => { 587 | wallet.on("session_request", async () => { 588 | const pendingRequests = wallet.getPendingSessionRequests(); 589 | const request = pendingRequests[0]; 590 | const signTransaction = request.params.request.params[0]; 591 | const signature = await cryptoWallet.signTransaction(signTransaction); 592 | const response = await wallet.respondSessionRequest({ 593 | topic: session.topic, 594 | response: formatJsonRpcResult(request.id, signature), 595 | }); 596 | resolve(response); 597 | resolve(pendingRequests); 598 | }); 599 | }), 600 | new Promise(async (resolve) => { 601 | const result = await dapp.request({ 602 | topic: session.topic, 603 | request: requestParams, 604 | chainId: TEST_ETHEREUM_CHAIN, 605 | }); 606 | expect(result).to.be.exist; 607 | expect(result).to.be.a("string"); 608 | resolve(); 609 | resolve(); 610 | }), 611 | ]); 612 | }); 613 | 614 | describe("Decrypted notifications", () => { 615 | it("should get session metadata", async () => { 616 | const initMetadata: CoreTypes.Metadata = { 617 | name: "Test Dapp", 618 | description: "Test Dapp Description", 619 | url: "https://walletconnect.com", 620 | icons: ["https://walletconnect.com/walletconnect-logo.png"], 621 | }; 622 | const dappTable = "./test/tmp/dapp"; 623 | const walletTable = "./test/tmp/wallet"; 624 | const dapp = await SignClient.init({ 625 | ...TEST_CORE_OPTIONS, 626 | name: "Dapp", 627 | metadata: initMetadata, 628 | storageOptions: { 629 | database: dappTable, 630 | }, 631 | }); 632 | const wallet = await WalletKit.init({ 633 | core: new Core({ 634 | ...TEST_CORE_OPTIONS, 635 | storageOptions: { database: walletTable }, 636 | }), 637 | name: "wallet", 638 | metadata: initMetadata, 639 | }); 640 | 641 | const { uri: uriString, approval } = await dapp.connect({}); 642 | let session: SessionTypes.Struct; 643 | await Promise.all([ 644 | new Promise((resolve) => { 645 | wallet.on("session_proposal", async (sessionProposal) => { 646 | const { id, params, verifyContext } = sessionProposal; 647 | expect(verifyContext.verified.validation).to.eq("UNKNOWN"); 648 | expect(verifyContext.verified.isScam).to.eq(undefined); 649 | session = await wallet.approveSession({ 650 | id, 651 | namespaces: TEST_NAMESPACES, 652 | }); 653 | resolve(session); 654 | }); 655 | }), 656 | new Promise(async (resolve) => { 657 | resolve(await approval()); 658 | }), 659 | wallet.pair({ uri: uriString! }), 660 | ]); 661 | 662 | const metadata = await WalletKit.notifications.getMetadata({ 663 | topic: session?.topic, 664 | storageOptions: { database: walletTable }, 665 | }); 666 | 667 | expect(metadata).to.be.exist; 668 | expect(metadata).to.be.a("object"); 669 | expect(metadata).to.toMatchObject(initMetadata); 670 | await disconnect(wallet.core); 671 | await disconnect(dapp.core); 672 | }); 673 | 674 | it("should decrypt payload with pairing topic", async () => { 675 | const initMetadata: CoreTypes.Metadata = { 676 | name: "Test Dapp", 677 | description: "Test Dapp Description", 678 | url: "https://walletconnect.com", 679 | icons: ["https://walletconnect.com/walletconnect-logo.png"], 680 | }; 681 | const dappTable = "./test/tmp/dapp"; 682 | const walletTable = "./test/tmp/wallet"; 683 | const dapp = await SignClient.init({ 684 | ...TEST_CORE_OPTIONS, 685 | name: "Dapp", 686 | metadata: initMetadata, 687 | storageOptions: { 688 | database: dappTable, 689 | }, 690 | }); 691 | const wallet = await WalletKit.init({ 692 | core: new Core({ 693 | ...TEST_CORE_OPTIONS, 694 | storageOptions: { database: walletTable }, 695 | }), 696 | name: "wallet", 697 | metadata: initMetadata, 698 | }); 699 | 700 | const { uri: uriString = "", approval } = await dapp.connect({}); 701 | let encryptedMessage = ""; 702 | let decryptedMessage: JsonRpcPayload = {} as any; 703 | let pairingTopic = ""; 704 | await Promise.all([ 705 | new Promise((resolve) => { 706 | wallet.core.relayer.on(RELAYER_EVENTS.message, async (payload) => { 707 | const { topic, message } = payload; 708 | const decrypted = await wallet.core.crypto.decode(topic, message); 709 | expect(decrypted).to.be.exist; 710 | if (decrypted?.method === "wc_sessionPropose" && isJsonRpcRequest(decrypted)) { 711 | encryptedMessage = message; 712 | decryptedMessage = decrypted; 713 | pairingTopic = topic; 714 | resolve(); 715 | } 716 | }); 717 | }), 718 | new Promise((resolve) => { 719 | wallet.on("session_proposal", async (sessionProposal) => { 720 | const { id, params, verifyContext } = sessionProposal; 721 | expect(verifyContext.verified.validation).to.eq("UNKNOWN"); 722 | expect(verifyContext.verified.isScam).to.eq(undefined); 723 | await wallet.approveSession({ 724 | id, 725 | namespaces: TEST_NAMESPACES, 726 | }); 727 | resolve(); 728 | }); 729 | }), 730 | new Promise(async (resolve) => { 731 | resolve(await approval()); 732 | }), 733 | wallet.pair({ uri: uriString }), 734 | ]); 735 | 736 | const decrypted = await WalletKit.notifications.decryptMessage({ 737 | topic: pairingTopic, 738 | encryptedMessage, 739 | storageOptions: { database: walletTable }, 740 | }); 741 | expect(decrypted).to.be.exist; 742 | expect(decrypted).to.be.a("object"); 743 | expect(decrypted).to.toMatchObject(decryptedMessage); 744 | await disconnect(wallet.core); 745 | await disconnect(dapp.core); 746 | }); 747 | it("should decrypt payload with session topic", async () => { 748 | const initMetadata: CoreTypes.Metadata = { 749 | name: "Test Dapp", 750 | description: "Test Dapp Description", 751 | url: "https://walletconnect.com", 752 | icons: ["https://walletconnect.com/walletconnect-logo.png"], 753 | }; 754 | const dappTable = "./test/tmp/dapp"; 755 | const walletTable = "./test/tmp/wallet"; 756 | const dapp = await SignClient.init({ 757 | ...TEST_CORE_OPTIONS, 758 | name: "Dapp", 759 | metadata: initMetadata, 760 | storageOptions: { 761 | database: dappTable, 762 | }, 763 | }); 764 | const wallet = await WalletKit.init({ 765 | core: new Core({ 766 | ...TEST_CORE_OPTIONS, 767 | storageOptions: { database: walletTable }, 768 | }), 769 | name: "wallet", 770 | metadata: initMetadata, 771 | }); 772 | 773 | const { uri: uriString = "", approval } = await dapp.connect({}); 774 | 775 | let session: SessionTypes.Struct = {} as any; 776 | // pair and approve session 777 | await Promise.all([ 778 | new Promise((resolve) => { 779 | wallet.on("session_proposal", async (sessionProposal) => { 780 | const { id, params, verifyContext } = sessionProposal; 781 | expect(verifyContext.verified.validation).to.eq("UNKNOWN"); 782 | expect(verifyContext.verified.isScam).to.eq(undefined); 783 | session = await wallet.approveSession({ 784 | id, 785 | namespaces: TEST_NAMESPACES, 786 | }); 787 | resolve(); 788 | }); 789 | }), 790 | new Promise(async (resolve) => { 791 | resolve(await approval()); 792 | }), 793 | wallet.pair({ uri: uriString }), 794 | ]); 795 | 796 | let encryptedMessage = ""; 797 | let decryptedMessage: JsonRpcPayload = {} as any; 798 | await Promise.all([ 799 | new Promise((resolve) => { 800 | wallet.core.relayer.on(RELAYER_EVENTS.message, async (payload) => { 801 | const { topic, message } = payload; 802 | const decrypted = await wallet.core.crypto.decode(topic, message); 803 | expect(decrypted).to.be.exist; 804 | if (decrypted?.method === "wc_sessionRequest" && isJsonRpcRequest(decrypted)) { 805 | encryptedMessage = message; 806 | decryptedMessage = decrypted; 807 | resolve(); 808 | } 809 | }); 810 | }), 811 | new Promise((resolve) => { 812 | wallet.on("session_request", async (payload) => { 813 | const { id, params, topic, verifyContext } = payload; 814 | await wallet.respondSessionRequest({ 815 | topic, 816 | response: formatJsonRpcResult(id, "0x"), 817 | }); 818 | resolve(); 819 | }); 820 | }), 821 | dapp.request({ 822 | topic: session.topic, 823 | request: { 824 | method: "eth_signTransaction", 825 | params: [ 826 | { 827 | from: cryptoWallet.address, 828 | to: cryptoWallet.address, 829 | data: "0x", 830 | nonce: "0x01", 831 | gasPrice: "0x020a7ac094", 832 | gasLimit: "0x5208", 833 | value: "0x00", 834 | }, 835 | ], 836 | }, 837 | chainId: TEST_ETHEREUM_CHAIN, 838 | }), 839 | ]); 840 | const decrypted = await WalletKit.notifications.decryptMessage({ 841 | topic: session.topic, 842 | encryptedMessage, 843 | storageOptions: { database: walletTable }, 844 | }); 845 | expect(decrypted).to.be.exist; 846 | expect(decrypted).to.be.a("object"); 847 | expect(decrypted).to.toMatchObject(decryptedMessage); 848 | await disconnect(wallet.core); 849 | await disconnect(dapp.core); 850 | }); 851 | }); 852 | 853 | describe("Sign 2.5", () => { 854 | it("should establish authenticated session", async () => { 855 | const dapp = await SignClient.init({ 856 | ...TEST_CORE_OPTIONS, 857 | name: "Dapp", 858 | metadata: TEST_METADATA, 859 | }); 860 | expect(dapp).to.be.exist; 861 | const { uri, response } = await dapp.authenticate({ 862 | chains: ["eip155:1", "eip155:2"], 863 | domain: "localhost", 864 | nonce: "1", 865 | uri: "aud", 866 | methods: ["personal_sign"], 867 | resources: [], 868 | }); 869 | const walletkit = await WalletKit.init({ 870 | name: "wallet", 871 | core: new Core(TEST_CORE_OPTIONS), 872 | metadata: TEST_METADATA, 873 | }); 874 | await Promise.all([ 875 | new Promise((resolve) => { 876 | walletkit.on("session_authenticate", async (payload) => { 877 | const verifyContext = payload.verifyContext; 878 | expect(verifyContext).to.exist; 879 | expect(verifyContext.verified.validation).to.eq("UNKNOWN"); 880 | 881 | const auths: any[] = []; 882 | for (const chain of payload.params.authPayload.chains) { 883 | const message = walletkit.formatAuthMessage({ 884 | request: payload.params.authPayload, 885 | iss: `${chain}:${cryptoWallet.address}`, 886 | }); 887 | const sig = await cryptoWallet.signMessage(message); 888 | const auth = buildAuthObject( 889 | payload.params.authPayload, 890 | { 891 | t: "eip191", 892 | s: sig, 893 | }, 894 | `${chain}:${cryptoWallet.address}`, 895 | ); 896 | auths.push(auth); 897 | } 898 | 899 | const result = await walletkit.approveSessionAuthenticate({ 900 | id: payload.id, 901 | auths, 902 | }); 903 | const { session } = result; 904 | expect(session).to.exist; 905 | resolve(); 906 | }); 907 | }), 908 | new Promise(async (resolve) => { 909 | await walletkit.pair({ uri }); 910 | resolve(); 911 | }), 912 | ]); 913 | const { session, auths } = await response(); 914 | expect(auths).to.exist; 915 | expect(auths).to.be.an("array"); 916 | const walletSessions = walletkit.getActiveSessions(); 917 | expect(walletSessions).to.exist; 918 | expect(walletSessions).to.be.an("object"); 919 | const walletSession = walletSessions[session.topic]; 920 | // approved namespaces on both sides must be equal 921 | expect(JSON.stringify(session.namespaces)).to.eq(JSON.stringify(walletSession.namespaces)); 922 | expect(session.topic).to.eq(walletSession.topic); 923 | await Promise.all([ 924 | new Promise((resolve) => { 925 | walletkit.on("session_request", async (payload) => { 926 | const { id, topic } = payload; 927 | await walletkit.respondSessionRequest({ 928 | topic, 929 | response: formatJsonRpcResult( 930 | id, 931 | await cryptoWallet.signMessage(payload.params.request.params[0]), 932 | ), 933 | }); 934 | resolve(); 935 | }); 936 | }), 937 | new Promise(async (resolve) => { 938 | await dapp.request({ 939 | chainId: "eip155:1", 940 | topic: session.topic, 941 | request: { 942 | method: "personal_sign", 943 | params: ["hey, sup"], 944 | }, 945 | }); 946 | resolve(); 947 | }), 948 | ]); 949 | await disconnect(walletkit.core); 950 | await disconnect(dapp.core); 951 | }); 952 | it("should fallback to session_proposal when no listener for `session_authenticate` exists", async () => { 953 | const dapp = await SignClient.init({ 954 | ...TEST_CORE_OPTIONS, 955 | name: "Dapp", 956 | metadata: TEST_METADATA, 957 | }); 958 | expect(dapp).to.be.exist; 959 | const { uri, response } = await dapp.authenticate({ 960 | chains: ["eip155:1", "eip155:2"], 961 | domain: "localhost", 962 | nonce: "1", 963 | uri: "aud", 964 | methods: ["personal_sign"], 965 | resources: [], 966 | }); 967 | const walletkit = await WalletKit.init({ 968 | name: "wallet", 969 | core: new Core(TEST_CORE_OPTIONS), 970 | metadata: TEST_METADATA, 971 | }); 972 | await Promise.all([ 973 | new Promise((resolve) => { 974 | walletkit.on("session_proposal", (payload) => { 975 | const approved = buildApprovedNamespaces({ 976 | supportedNamespaces: { 977 | eip155: { 978 | methods: ["personal_sign", "eth_signTransaction", "eth_signTypedData_v4"], 979 | chains: ["eip155:1", "eip155:2", "eip155:3"], 980 | accounts: [ 981 | "eip155:1:" + cryptoWallet.address, 982 | "eip155:2:" + cryptoWallet.address, 983 | "eip155:3:" + cryptoWallet.address, 984 | ], 985 | events: [], 986 | }, 987 | }, 988 | proposal: payload.params, 989 | }); 990 | walletkit.approveSession({ 991 | id: payload.id, 992 | namespaces: approved, 993 | }); 994 | resolve(); 995 | }); 996 | }), 997 | new Promise(async (resolve) => { 998 | await walletkit.pair({ uri }); 999 | resolve(); 1000 | }), 1001 | ]); 1002 | const { session, auths } = await response(); 1003 | expect(auths).to.be.undefined; 1004 | const walletSessions = walletkit.getActiveSessions(); 1005 | expect(walletSessions).to.exist; 1006 | expect(walletSessions).to.be.an("object"); 1007 | const walletSession = walletSessions[session.topic]; 1008 | // approved namespaces on both sides must be equal 1009 | expect(JSON.stringify(session.namespaces)).to.eq(JSON.stringify(walletSession.namespaces)); 1010 | expect(session.topic).to.eq(walletSession.topic); 1011 | await Promise.all([ 1012 | new Promise((resolve) => { 1013 | walletkit.on("session_request", async (payload) => { 1014 | const { id, topic } = payload; 1015 | await walletkit.respondSessionRequest({ 1016 | topic, 1017 | response: formatJsonRpcResult( 1018 | id, 1019 | await cryptoWallet.signMessage(payload.params.request.params[0]), 1020 | ), 1021 | }); 1022 | resolve(); 1023 | }); 1024 | }), 1025 | new Promise(async (resolve) => { 1026 | await dapp.request({ 1027 | chainId: "eip155:1", 1028 | topic: session.topic, 1029 | request: { 1030 | method: "personal_sign", 1031 | params: ["hey, sup"], 1032 | }, 1033 | }); 1034 | resolve(); 1035 | }), 1036 | ]); 1037 | await disconnect(walletkit.core); 1038 | await disconnect(dapp.core); 1039 | }); 1040 | }); 1041 | 1042 | it("should send core init event when walletkit initializes", async () => { 1043 | process.env.IS_VITEST = false as any; 1044 | const core = new Core({ ...TEST_CORE_OPTIONS, telemetryEnabled: false }); 1045 | let initCalled = false; 1046 | expect(initCalled).to.be.false; 1047 | // @ts-expect-error - accessing private properties 1048 | core.eventClient.sendEvent = async (payload: any) => { 1049 | initCalled = true; 1050 | expect(payload).toBeDefined(); 1051 | expect(payload.length).to.eql(1); 1052 | expect(payload[0].props.event).to.eql("INIT"); 1053 | expect(payload[0].props.properties.client_id).to.eql(await core.crypto.getClientId()); 1054 | }; 1055 | await WalletKit.init({ 1056 | core, 1057 | name: "wallet", 1058 | metadata: TEST_METADATA, 1059 | }); 1060 | await new Promise((resolve) => setTimeout(resolve, 500)); 1061 | expect(initCalled).to.be.true; 1062 | process.env.IS_VITEST = true as any; 1063 | }); 1064 | 1065 | describe.concurrent("1CA", () => { 1066 | const approveAuthentication = async ( 1067 | event: WalletKitTypes.EventArguments["session_proposal"], 1068 | ) => { 1069 | const authenticationRequests = event.params.requests?.authentication; 1070 | if (!authenticationRequests) return []; 1071 | const auths: AuthTypes.Cacao[] = []; 1072 | for (const authenticationRequest of authenticationRequests) { 1073 | for (const chain of authenticationRequest.chains) { 1074 | const message = wallet.formatAuthMessage({ 1075 | request: authenticationRequest, 1076 | iss: `did:pkh:${chain}:${cryptoWallet.address}`, 1077 | }); 1078 | const sig = await cryptoWallet.signMessage(message!); 1079 | const authObject = buildAuthObject( 1080 | authenticationRequest, 1081 | { 1082 | t: "eip191", 1083 | s: sig, 1084 | }, 1085 | `did:pkh:${chain}:${cryptoWallet.address}`, 1086 | ); 1087 | auths.push(authObject); 1088 | } 1089 | } 1090 | return auths; 1091 | }; 1092 | 1093 | it("should approve session proposal with 1CA requests", async () => { 1094 | const core = new Core({ 1095 | ...TEST_CORE_OPTIONS, 1096 | customStoragePrefix: Math.random().toString(36).substring(2, 15), 1097 | }); 1098 | const dapp = await SignClient.init({ 1099 | ...TEST_CORE_OPTIONS, 1100 | name: "Dapp", 1101 | metadata: TEST_METADATA, 1102 | customStoragePrefix: Math.random().toString(36).substring(2, 15), 1103 | }); 1104 | 1105 | const authentication = { 1106 | uri: "https://walletconnect.com", 1107 | domain: "walletconnect.com", 1108 | chains: ["eip155:1", "eip155:2"], 1109 | nonce: "1", 1110 | ttl: 1000, 1111 | }; 1112 | const { uri, approval } = await dapp.connect({ 1113 | requiredNamespaces: TEST_REQUIRED_NAMESPACES, 1114 | authentication: [authentication], 1115 | }); 1116 | uriString = uri || ""; 1117 | sessionApproval = approval; 1118 | const signConfig = { disableRequestQueue: true }; 1119 | const wallet = await WalletKit.init({ 1120 | core, 1121 | name: "wallet", 1122 | metadata: TEST_METADATA, 1123 | signConfig, 1124 | }); 1125 | 1126 | const sessionConfig = { disableDeepLink: false }; 1127 | await Promise.all([ 1128 | new Promise((resolve) => { 1129 | wallet.on("session_proposal", async (sessionProposal) => { 1130 | const { id, params, verifyContext } = sessionProposal; 1131 | expect(verifyContext.verified.validation).to.eq("UNKNOWN"); 1132 | expect(verifyContext.verified.isScam).to.eq(undefined); 1133 | const auths = await approveAuthentication(sessionProposal); 1134 | session = await wallet.approveSession({ 1135 | id, 1136 | namespaces: TEST_NAMESPACES, 1137 | sessionConfig, 1138 | proposalRequestsResponses: { 1139 | authentication: auths, 1140 | }, 1141 | }); 1142 | expect(params.optionalNamespaces).to.toMatchObject(TEST_REQUIRED_NAMESPACES); 1143 | resolve(session); 1144 | }); 1145 | }), 1146 | new Promise(async (resolve) => { 1147 | resolve(await sessionApproval()); 1148 | }), 1149 | wallet.pair({ uri: uriString }), 1150 | ]); 1151 | 1152 | expect(session).to.be.exist; 1153 | expect(session.topic).to.be.exist; 1154 | expect(session.sessionConfig).to.eql(sessionConfig); 1155 | const auths = session.authentication; 1156 | expect(auths).to.be.exist; 1157 | expect(auths).to.be.an("array"); 1158 | expect(auths).to.have.length(2); 1159 | expect(auths?.[0]?.p.iss).to.eq(`did:pkh:eip155:1:${cryptoWallet.address}`); 1160 | expect(auths?.[1]?.p.iss).to.eq(`did:pkh:eip155:2:${cryptoWallet.address}`); 1161 | const dappSession = dapp.session.get(session.topic); 1162 | expect(dappSession).to.be.exist; 1163 | expect(dappSession?.authentication).to.be.exist; 1164 | expect(dappSession?.authentication).to.be.an("array"); 1165 | expect(dappSession?.authentication).to.have.length(2); 1166 | expect(dappSession?.authentication?.[0]?.p.iss).to.eq( 1167 | `did:pkh:eip155:1:${cryptoWallet.address}`, 1168 | ); 1169 | expect(dappSession?.authentication?.[1]?.p.iss).to.eq( 1170 | `did:pkh:eip155:2:${cryptoWallet.address}`, 1171 | ); 1172 | await disconnect(wallet.core); 1173 | await disconnect(dapp.core); 1174 | }); 1175 | }); 1176 | }); 1177 | --------------------------------------------------------------------------------