├── .nojekyll ├── packages ├── core │ ├── .gitignore │ ├── .prettierignore │ ├── src │ │ ├── index.ts │ │ ├── errors.ts │ │ ├── localStorage.ts │ │ └── walletStore.ts │ ├── tsconfig.json │ ├── tsconfig.cjs.json │ ├── package.json │ ├── README.md │ └── LICENSE ├── anchor │ ├── .gitignore │ ├── .prettierrc │ ├── src │ │ ├── lib │ │ │ ├── index.ts │ │ │ ├── workSpace.ts │ │ │ └── AnchorConnectionProvider.svelte │ │ └── app.html │ ├── svelte.config.js │ ├── .eslintrc.cjs │ ├── tsconfig.json │ ├── package.json │ └── README.md └── ui │ ├── .gitignore │ ├── .prettierrc │ ├── src │ ├── lib │ │ ├── workSpace.ts │ │ ├── ConnectionProvider.svelte │ │ ├── index.ts │ │ ├── WalletButton.svelte │ │ ├── WalletProvider.svelte │ │ ├── WalletConnectButton.svelte │ │ ├── WalletMultiButton.svelte │ │ ├── styles.css │ │ └── WalletModal.svelte │ └── app.html │ ├── .eslintrc.cjs │ ├── svelte.config.js │ ├── tsconfig.json │ ├── package.json │ └── README.md ├── .eslintignore ├── .prettierignore ├── wallets-adapter.png ├── .gitignore ├── .prettierrc ├── lerna.json ├── .editorconfig ├── .eslintrc.json ├── .github └── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── tsconfig.json ├── README.md ├── package.json └── LICENSE /.nojekyll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/core/.gitignore: -------------------------------------------------------------------------------- 1 | lib 2 | -------------------------------------------------------------------------------- /packages/core/.prettierignore: -------------------------------------------------------------------------------- 1 | lib 2 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | 3 | docs 4 | lib 5 | out 6 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | docs 2 | lib 3 | build 4 | out 5 | .next 6 | *.svg 7 | 8 | -------------------------------------------------------------------------------- /packages/anchor/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | -------------------------------------------------------------------------------- /packages/ui/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | -------------------------------------------------------------------------------- /wallets-adapter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svelte-on-solana/wallet-adapter/HEAD/wallets-adapter.png -------------------------------------------------------------------------------- /packages/core/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './errors'; 2 | export * from './localStorage'; 3 | export * from './walletStore'; 4 | -------------------------------------------------------------------------------- /packages/ui/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "useTabs": true, 3 | "singleQuote": true, 4 | "trailingComma": "none", 5 | "printWidth": 100 6 | } 7 | -------------------------------------------------------------------------------- /packages/anchor/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "useTabs": true, 3 | "singleQuote": true, 4 | "trailingComma": "none", 5 | "printWidth": 100 6 | } 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .vscode 3 | package-lock.json 4 | 5 | .DS_Store 6 | 7 | node_modules 8 | 9 | docs 10 | 11 | .parcel-cache 12 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "trailingComma": "es5", 4 | "tabWidth": 4, 5 | "semi": true, 6 | "singleQuote": true 7 | } -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": ["packages/*"], 3 | "version": "independent", 4 | "npmClient": "yarn", 5 | "useWorkspaces": true 6 | } 7 | -------------------------------------------------------------------------------- /packages/anchor/src/lib/index.ts: -------------------------------------------------------------------------------- 1 | export { default as AnchorConnectionProvider } from './AnchorConnectionProvider.svelte'; 2 | export * from './workSpace'; 3 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 4 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true -------------------------------------------------------------------------------- /packages/core/src/errors.ts: -------------------------------------------------------------------------------- 1 | import { WalletError } from '@solana/wallet-adapter-base'; 2 | 3 | export class WalletNotSelectedError extends WalletError { 4 | name = 'WalletNotSelectedError'; 5 | } 6 | -------------------------------------------------------------------------------- /packages/core/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": ["src"], 4 | "compilerOptions": { 5 | "noEmit": false, 6 | "outDir": "lib/esm" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/core/tsconfig.cjs.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "target": "es6", 5 | "module": "commonjs", 6 | "outDir": "lib/cjs" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/ui/src/lib/workSpace.ts: -------------------------------------------------------------------------------- 1 | import { writable } from 'svelte/store'; 2 | import type { Connection } from '@solana/web3.js'; 3 | 4 | type WorkSpace = { 5 | connection: Connection; 6 | }; 7 | 8 | export const workSpace = writable(undefined); 9 | -------------------------------------------------------------------------------- /packages/ui/src/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | %svelte.head% 8 | 9 | 10 |
%svelte.body%
11 | 12 | 13 | -------------------------------------------------------------------------------- /packages/anchor/src/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | %svelte.head% 8 | 9 | 10 |
%svelte.body%
11 | 12 | 13 | -------------------------------------------------------------------------------- /packages/ui/src/lib/ConnectionProvider.svelte: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /packages/ui/src/lib/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ConnectionProvider } from './ConnectionProvider.svelte'; 2 | export { default as WalletButton } from './WalletButton.svelte'; 3 | export { default as WalletConnectButton } from './WalletConnectButton.svelte'; 4 | export { default as WalletModal } from './WalletModal.svelte'; 5 | export { default as WalletMultiButton } from './WalletMultiButton.svelte'; 6 | export { default as WalletProvider } from './WalletProvider.svelte'; 7 | export * from './workSpace'; 8 | -------------------------------------------------------------------------------- /packages/anchor/src/lib/workSpace.ts: -------------------------------------------------------------------------------- 1 | import { writable } from 'svelte/store'; 2 | import type { Program, Provider, web3 } from '@project-serum/anchor'; 3 | import type { Connection, Keypair } from '@solana/web3.js'; 4 | 5 | export type WorkSpace = { 6 | baseAccount?: Keypair; 7 | connection: Connection; 8 | provider?: Provider; 9 | program?: Program; 10 | systemProgram?: typeof web3.SystemProgram; 11 | network: string; 12 | }; 13 | 14 | export const workSpace = writable(undefined); 15 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "env": { 4 | "browser": true 5 | }, 6 | "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"], 7 | "parser": "@typescript-eslint/parser", 8 | "plugins": ["@typescript-eslint", "prettier"], 9 | "rules": { 10 | "@typescript-eslint/ban-ts-comment": "off", 11 | "@typescript-eslint/no-explicit-any": "off", 12 | "@typescript-eslint/no-unused-vars": "off", 13 | "@typescript-eslint/no-empty-interface": "off" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/anchor/svelte.config.js: -------------------------------------------------------------------------------- 1 | import preprocess from 'svelte-preprocess'; 2 | import adapter from '@sveltejs/adapter-static'; 3 | import path from 'path'; 4 | 5 | const config = { 6 | preprocess: preprocess(), 7 | 8 | kit: { 9 | adapter: adapter(), 10 | vite: { 11 | adapter: adapter(), 12 | optimizeDeps: { 13 | include: ['@project-serum/anchor', '@solana/web3.js'], 14 | }, 15 | resolve: { 16 | alias: { 17 | $lib: path.resolve('src/lib/') 18 | } 19 | } 20 | } 21 | } 22 | }; 23 | 24 | export default config; 25 | -------------------------------------------------------------------------------- /packages/ui/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parser: '@typescript-eslint/parser', 4 | extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'prettier'], 5 | plugins: ['svelte3', '@typescript-eslint'], 6 | ignorePatterns: ['*.cjs'], 7 | overrides: [{ files: ['*.svelte'], processor: 'svelte3/svelte3' }], 8 | settings: { 9 | 'svelte3/typescript': () => require('typescript') 10 | }, 11 | parserOptions: { 12 | sourceType: 'module', 13 | ecmaVersion: 2019 14 | }, 15 | env: { 16 | browser: true, 17 | es2017: true, 18 | node: true 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /packages/anchor/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parser: '@typescript-eslint/parser', 4 | extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'prettier'], 5 | plugins: ['svelte3', '@typescript-eslint'], 6 | ignorePatterns: ['*.cjs'], 7 | overrides: [{ files: ['*.svelte'], processor: 'svelte3/svelte3' }], 8 | settings: { 9 | 'svelte3/typescript': () => require('typescript') 10 | }, 11 | parserOptions: { 12 | sourceType: 'module', 13 | ecmaVersion: 2019 14 | }, 15 | env: { 16 | browser: true, 17 | es2017: true, 18 | node: true 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /packages/ui/src/lib/WalletButton.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 | 24 | -------------------------------------------------------------------------------- /packages/ui/src/lib/WalletProvider.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /packages/ui/svelte.config.js: -------------------------------------------------------------------------------- 1 | import preprocess from 'svelte-preprocess'; 2 | import adapter from '@sveltejs/adapter-static'; 3 | import path from 'path'; 4 | 5 | /** @type {import('@sveltejs/kit').Config} */ 6 | const config = { 7 | // Consult https://github.com/sveltejs/svelte-preprocess 8 | // for more information about preprocessors 9 | preprocess: preprocess(), 10 | 11 | kit: { 12 | // hydrate the
element in src/app.html 13 | adapter: adapter(), 14 | vite: { 15 | adapter: adapter(), 16 | resolve: { 17 | alias: { 18 | $lib: path.resolve('src/lib/') 19 | } 20 | } 21 | } 22 | } 23 | }; 24 | 25 | export default config; 26 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["packages/*/src"], 3 | "exclude": ["node_modules", "packages/*/node_modules"], 4 | "compilerOptions": { 5 | "target": "es2019", 6 | "module": "esnext", 7 | "noEmit": true, 8 | "declaration": true, 9 | "sourceMap": true, 10 | "noEmitOnError": true, 11 | "stripInternal": true, 12 | "moduleResolution": "node", 13 | "jsx": "react", 14 | "strict": true, 15 | "esModuleInterop": true, 16 | "resolveJsonModule": true, 17 | "isolatedModules": true, 18 | "typeRoots": ["node_modules/@types"] 19 | }, 20 | "typedocOptions": { 21 | "entryPoints": ".", 22 | "entryPointStrategy": "packages", 23 | "out": "docs", 24 | "readme": "README.md" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/core/src/localStorage.ts: -------------------------------------------------------------------------------- 1 | export function getLocalStorage(key: string, defaultValue: T | null = null): T | null { 2 | try { 3 | const value = localStorage.getItem(key); 4 | if (value) return JSON.parse(value) as T; 5 | } catch (error) { 6 | if (typeof window !== 'undefined') { 7 | console.error(error); 8 | } 9 | } 10 | 11 | return defaultValue; 12 | } 13 | 14 | export function setLocalStorage(key: string, value: T | null = null): void { 15 | try { 16 | if (value === null) { 17 | localStorage.removeItem(key); 18 | } else { 19 | localStorage.setItem(key, JSON.stringify(value)); 20 | } 21 | } catch (error) { 22 | if (typeof window !== 'undefined') { 23 | console.error(error); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/ui/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "node", 4 | "module": "es2020", 5 | "lib": ["es2020", "DOM"], 6 | "target": "es2019", 7 | /** 8 | svelte-preprocess cannot figure out whether you have a value or a type, so tell TypeScript 9 | to enforce using \`import type\` instead of \`import\` for Types. 10 | */ 11 | "importsNotUsedAsValues": "error", 12 | "isolatedModules": true, 13 | "resolveJsonModule": true, 14 | /** 15 | To have warnings/errors of the Svelte compiler at the correct position, 16 | enable source maps by default. 17 | */ 18 | "sourceMap": true, 19 | "esModuleInterop": true, 20 | "skipLibCheck": true, 21 | "forceConsistentCasingInFileNames": true, 22 | "baseUrl": ".", 23 | "allowJs": true, 24 | "checkJs": true, 25 | "paths": { 26 | "$lib": ["src/lib"], 27 | "$lib/*": ["src/lib/*"] 28 | } 29 | }, 30 | "include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.ts", "src/**/*.svelte"] 31 | } 32 | -------------------------------------------------------------------------------- /packages/anchor/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "node", 4 | "module": "es2020", 5 | "lib": ["es2020", "DOM"], 6 | "target": "es2019", 7 | /** 8 | svelte-preprocess cannot figure out whether you have a value or a type, so tell TypeScript 9 | to enforce using \`import type\` instead of \`import\` for Types. 10 | */ 11 | "importsNotUsedAsValues": "error", 12 | "isolatedModules": true, 13 | "resolveJsonModule": true, 14 | /** 15 | To have warnings/errors of the Svelte compiler at the correct position, 16 | enable source maps by default. 17 | */ 18 | "sourceMap": true, 19 | "esModuleInterop": true, 20 | "skipLibCheck": true, 21 | "forceConsistentCasingInFileNames": true, 22 | "baseUrl": ".", 23 | "allowJs": true, 24 | "checkJs": true, 25 | "paths": { 26 | "$lib": ["src/lib"], 27 | "$lib/*": ["src/lib/*"] 28 | } 29 | }, 30 | "include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.ts", "src/**/*.svelte"] 31 | } 32 | -------------------------------------------------------------------------------- /packages/ui/src/lib/WalletConnectButton.svelte: -------------------------------------------------------------------------------- 1 | 22 | 23 | 28 | 29 | {#if wallet} 30 | {`${wallet.name} 31 | {/if} 32 | 33 | {content} 34 | 35 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /packages/ui/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@svelte-on-solana/wallet-adapter-ui", 3 | "version": "1.0.21-alpha.0", 4 | "author": "Svelte on Solana", 5 | "repository": "https://github.com/svelte-on-solana/wallet-adapter", 6 | "type": "module", 7 | "publishConfig": { 8 | "access": "public", 9 | "directory": "package" 10 | }, 11 | "scripts": { 12 | "build": "svelte-kit package", 13 | "lint": "prettier --ignore-path .gitignore --check --plugin-search-dir=. . && eslint --ignore-path .gitignore .", 14 | "format": "prettier --ignore-path .gitignore --write --plugin-search-dir=. ." 15 | }, 16 | "devDependencies": { 17 | "@sveltejs/adapter-static": "^1.0.0-next.21", 18 | "@sveltejs/kit": "next", 19 | "@typescript-eslint/eslint-plugin": "^4.31.1", 20 | "@typescript-eslint/parser": "^4.31.1", 21 | "eslint": "^7.32.0", 22 | "eslint-config-prettier": "^8.3.0", 23 | "eslint-plugin-svelte3": "^3.2.1", 24 | "prettier": "^2.4.1", 25 | "prettier-plugin-svelte": "^2.4.0", 26 | "svelte": "^3.46.2", 27 | "svelte-preprocess": "^4.9.4", 28 | "svelte2tsx": "^0.4.8", 29 | "tslib": "^2.3.1", 30 | "typescript": "^4.4.3" 31 | }, 32 | "dependencies": { 33 | "eventemitter3": "^4.0.7" 34 | }, 35 | "keywords": [ 36 | "svelte", 37 | "wallet", 38 | "adapter", 39 | "wallet-adapter", 40 | "solana" 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /packages/anchor/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@svelte-on-solana/wallet-adapter-anchor", 3 | "version": "1.0.16-alpha.0", 4 | "author": "Svelte on Solana", 5 | "repository": "https://github.com/svelte-on-solana/wallet-adapter", 6 | "type": "module", 7 | "publishConfig": { 8 | "access": "public", 9 | "directory": "package" 10 | }, 11 | "scripts": { 12 | "build": "svelte-kit package", 13 | "lint": "prettier --ignore-path .gitignore --check --plugin-search-dir=. . && eslint --ignore-path .gitignore .", 14 | "format": "prettier --ignore-path .gitignore --write --plugin-search-dir=. ." 15 | }, 16 | "devDependencies": { 17 | "@sveltejs/kit": "next", 18 | "@typescript-eslint/eslint-plugin": "^4.31.1", 19 | "@typescript-eslint/parser": "^4.31.1", 20 | "eslint": "^7.32.0", 21 | "eslint-config-prettier": "^8.3.0", 22 | "eslint-plugin-svelte3": "^3.2.1", 23 | "prettier": "^2.4.1", 24 | "prettier-plugin-svelte": "^2.4.0", 25 | "svelte": "^3.46.2", 26 | "svelte-preprocess": "^4.9.4", 27 | "svelte2tsx": "^0.4.8", 28 | "tslib": "^2.3.1", 29 | "typescript": "^4.4.3" 30 | }, 31 | "dependencies": { 32 | "eventemitter3": "^4.0.7" 33 | }, 34 | "peerDependencies": { 35 | "@project-serum/anchor": "^0.24.2" 36 | }, 37 | "keywords": [ 38 | "svelte", 39 | "wallet", 40 | "adapter", 41 | "anchor", 42 | "wallet-adapter", 43 | "solana" 44 | ] 45 | } 46 | -------------------------------------------------------------------------------- /packages/core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@svelte-on-solana/wallet-adapter-core", 3 | "version": "1.0.11-alpha.0", 4 | "author": "Svelte on Solana", 5 | "repository": "https://github.com/svelte-on-solana/wallet-adapter", 6 | "license": "Apache-2.0", 7 | "type": "module", 8 | "sideEffects": false, 9 | "main": "lib/cjs/index.js", 10 | "module": "lib/esm/index.mjs", 11 | "types": "lib/esm/index.d.ts", 12 | "exports": { 13 | "import": "./lib/esm/index.mjs", 14 | "require": "./lib/cjs/index.js" 15 | }, 16 | "files": [ 17 | "lib", 18 | "src", 19 | "LICENSE" 20 | ], 21 | "publishConfig": { 22 | "access": "public" 23 | }, 24 | "scripts": { 25 | "clean": "shx rm -rf lib/*", 26 | "build": "yarn clean && tsc -p tsconfig.json; tsc-esm -p tsconfig.json && tsc -p tsconfig.cjs.json", 27 | "postbuild": "echo '{\"type\":\"commonjs\"}' | npx json > lib/cjs/package.json && echo '{\"type\":\"module\"} ' | npx json > lib/esm/package.json" 28 | }, 29 | "dependencies": { 30 | "@types/node-fetch": "^2.6.2", 31 | "@solana/wallet-adapter-base": "^0.9.19", 32 | "@solana/web3.js": "^1.70.0", 33 | "eventemitter3": "^4.0.7", 34 | "svelte": "^3.0.0" 35 | }, 36 | "keywords": [ 37 | "svelte", 38 | "wallet", 39 | "adapter", 40 | "wallet-adapter", 41 | "solana" 42 | ] 43 | } 44 | -------------------------------------------------------------------------------- /packages/core/README.md: -------------------------------------------------------------------------------- 1 | # `@svelte-on-solana/wallet-adapter-core` 2 | 3 | The core of the wallet adapter is a Svelte Store which exposes methods and properties to run the wallet in your application. This allows to share this data among all components in your application. 4 | 5 | ## Install 6 | 7 | ```shell 8 | npm install @solana/wallet-adapter-base \ 9 | @solana/wallet-adapter-wallets \ 10 | @solana/web3.js \ 11 | @svelte-on-solana/wallet-adapter-core 12 | ``` 13 | 14 | ## Use 15 | 16 | Once it is installed, you can use it and subscribe to its methods as an usual Svelte Store: 17 | 18 | ```html 19 | 23 | 24 | {#if $walletStore?.connected} 25 |
My wallet is connected
26 | {/if} 27 | ``` 28 | 29 | ## UI 30 | 31 | To complete the setup you will need to add some UI components which will provide the full experience to connect, disconnect and visualize address. You can choose between the [Svelte Template](https://github.com/sveltejs/template) or the [SvelteKit](https://kit.svelte.dev/) implementation. Both are using the same packages but setup is different. The UI packages offer different implementations for **Solana** and **Anchor**. 32 | 33 | - [Using Svelte Template](https://github.com/svelte-on-solana/wallet-adapter/blob/master/packages/ui/README.md#svelte-template) 34 | - [Using SvelteKit](https://github.com/svelte-on-solana/wallet-adapter/blob/master/packages/ui/README.md#sveltekit) 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `@svelte-on-solana/wallet-adapter` 2 | 3 | ![Wallets](wallets-adapter.png) 4 | 5 | Modular TypeScript wallet adapter and UI components for Solana/Anchor applications using [SvelteJS](https://svelte.dev/) as framework. This package contains a solution for [Svelte Template](https://github.com/sveltejs/template) and [SvelteKit](https://kit.svelte.dev/), making possible to build Solana Dapps in SPA or SSR mode. 6 | 7 | [View demo][4] / [Browse demo code][5] 8 | 9 | ## Packages 10 | 11 | - [Core][1] - Svelte Store which exposes methods and properties to run the wallet in your application 12 | - [UI][2] - Pre-built components for integrating with Solana wallets using Svelte 13 | - [Anchor][3] - Helper components for working with Anchor 14 | 15 | ## Build from Source 16 | 17 | 1. Clone the project: 18 | ```shell 19 | git clone https://github.com/svelte-on-solana/wallet-adapter.git 20 | ``` 21 | 22 | 2. Install dependencies: 23 | ```shell 24 | cd wallet-adapter 25 | yarn install 26 | ``` 27 | 28 | 3. Build all packages: 29 | ```shell 30 | yarn build 31 | ``` 32 | 33 | 4. Run locally: 34 | ```shell 35 | cd packages/ui/ 36 | yarn start 37 | ``` 38 | 39 | [1]: https://github.com/svelte-on-solana/wallet-adapter/blob/master/packages/core/README.md/ 40 | [2]: https://github.com/svelte-on-solana/wallet-adapter/blob/master/packages/ui/README.md 41 | [3]: https://github.com/svelte-on-solana/wallet-adapter/blob/master/packages/anchor/README.md 42 | [4]: https://github.com/silvestrevivo/solana-svelte-counter/ 43 | [5]: https://solana-svelte-counter.netlify.app/ 44 | -------------------------------------------------------------------------------- /packages/anchor/src/lib/AnchorConnectionProvider.svelte: -------------------------------------------------------------------------------- 1 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "@svelte-on-solana/wallet-adapter", 4 | "author": "Svelte on Solana", 5 | "repository": "https://github.com/svelte-on-solana/wallet-adapter", 6 | "license": "Apache-2.0", 7 | "type": "module", 8 | "workspaces": { 9 | "packages": [ 10 | "packages/*" 11 | ], 12 | "nohoist": [ 13 | "**/@babel/preset-env", 14 | "**/@babel/preset-env/**", 15 | "**/babel-loader", 16 | "**/babel-loader/**", 17 | "**/webpack", 18 | "**/webpack/**" 19 | ] 20 | }, 21 | "engines": { 22 | "node": ">= 14" 23 | }, 24 | "publishConfig": { 25 | "access": "public" 26 | }, 27 | "scripts": { 28 | "clean": "lerna run clean", 29 | "build": "lerna run build", 30 | "test": "lerna run test", 31 | "publish": "lerna publish from-package", 32 | "deploy": "yarn deploy:docs && yarn deploy:example", 33 | "deploy:docs": "yarn docs && gh-pages --dist docs --dotfiles", 34 | "deploy:example": "gh-pages --dist packages/starter/example/out --dest example --dotfiles", 35 | "docs": "shx rm -rf docs && NODE_OPTIONS=--max_old_space_size=8192 typedoc && shx cp ./{.nojekyll,wallets.png} docs/", 36 | "fmt": "prettier --write '{*,**/*}.{js,ts,jsx,tsx,json}'", 37 | "lint": "eslint --ext .ts . && prettier --check '{*,**/*}.{js,ts,jsx,tsx,json}'", 38 | "lint:fix": "eslint --fix --ext .ts . && yarn fmt", 39 | "nuke": "shx rm -rf {.,packages/*}/{node_modules,yarn.lock}" 40 | }, 41 | "devDependencies": { 42 | "@babel/eslint-parser": "^7.16.5", 43 | "@types/eslint": "^8.2.1", 44 | "@types/eslint-plugin-prettier": "^3.1.0", 45 | "@types/node": "^16.11.12", 46 | "@types/prettier": "^2.4.2", 47 | "@typescript-eslint/eslint-plugin": "^5.6.0", 48 | "@typescript-eslint/parser": "^5.6.0", 49 | "babel-eslint": "^10.1.0", 50 | "eslint": "^7.32.0", 51 | "eslint-config-prettier": "^8.3.0", 52 | "eslint-config-react-app": "^7.0.0", 53 | "eslint-plugin-prettier": "^4.0.0", 54 | "gh-pages": "^3.2.3", 55 | "lerna": "^4.0.0", 56 | "prettier": "^2.5.1", 57 | "shx": "^0.3.3", 58 | "tslib": "^2.3.0", 59 | "typedoc": "^0.22.10", 60 | "typescript": "~4.4.4", 61 | "typescript-esm": "^2.0.0" 62 | }, 63 | "dependencies": { 64 | "svelte2tsx": "^0.5.5" 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /packages/anchor/README.md: -------------------------------------------------------------------------------- 1 | # `@svelte-on-solana/wallet-adapter-anchor` 2 | 3 | `AnchorConnectionProvider` component and `workSpace` for Solana wallets using Svelte 4 | 5 | ## Installing 6 | 7 | You have already installed the core package to run the wallet Svelte Store [@svelte-on-solana/wallet-adapter-core](https://github.com/svelte-on-solana/wallet-adapter/blob/master/packages/core/README.md) and the UI components to use the wallet [@svelte-on-solana/wallet-adapter-ui](https://github.com/svelte-on-solana/wallet-adapter/blob/master/packages/ui/README.md/). Then install the `AnchorConnectionProvider` component and `workSpace` file contained in this package. 8 | 9 | ```shell 10 | npm i @svelte-on-solana/wallet-adapter-anchor 11 | ``` 12 | 13 | ## Set Up 14 | 15 | Add `@project-serum/anchor` to `optimizeDeps` inside `vite.config.js`. This pre-bundles the `@project-serum/anchor` package. This steps converts CommonJS dependencies into ESM ( Vite's dev server serves all code as native ESM ). 16 | 17 | ```javascript 18 | import { sveltekit } from '@sveltejs/kit/vite' 19 | 20 | /** @type {import('vite').UserConfig} */ 21 | const config = { 22 | plugins: [sveltekit()], 23 | // ... use the same implementation from the SvelteKit ui 24 | optimizeDeps: { 25 | include: ['@project-serum/anchor', '@solana/web3.js', 'buffer'], 26 | // ... use the same implementation from the SvelteKit ui 27 | } 28 | // ... use the same implementation from the SvelteKit ui 29 | } 30 | 31 | export default config 32 | ``` 33 | 34 | The `AnchorConnectionProvider` for Anchor Dapps accepts the next props. 35 | 36 | | prop | type | default | 37 | | ------- | -------- | ------- | 38 | | network | `string` | | 39 | | idl | `Idl` | | 40 | 41 | It is automatically connected to the `workSpace` defining all the parameters to share among the components in your Anchor Dapp **(baseAccount, connection, provider, program, systemProgram and network)**. 42 | 43 | ## SvelteKit 44 | 45 | In the **\_\_layout.svelte** component you can import the wallets and setup the UI components. 46 | 47 | ```html 48 | 80 | 81 | 82 | 83 |
84 | 85 |
86 | 87 | ``` 88 | 89 | ## Svelte Template 90 | 91 | In `App.svelte` or the entry point of your SPA, you can setup the wallet and components like this. 92 | 93 | ```html 94 | 107 | 108 | 109 | 110 | 111 | 112 | {#if $walletStore?.connected} 113 |
My wallet is connected
114 | {/if} 115 | ``` 116 | 117 | ## Example Implementation 118 | 119 | See example implementations of the `@svelte-on-solana/wallet-adapter-ui` library. 120 | 121 | - [Demo site][1] 122 | 123 | [1]: https://github.com/silvestrevivo/solana-svelte-counter/tree/master/app 124 | -------------------------------------------------------------------------------- /packages/ui/src/lib/WalletMultiButton.svelte: -------------------------------------------------------------------------------- 1 | 82 | 83 | {#if !wallet} 84 | 85 | Select Wallet 86 | 87 | {:else if !base58} 88 | 89 | {:else} 90 |
91 | 95 | 96 | {`${wallet.name} 97 | 98 | {content} 99 | 100 | {#if dropDrownVisible} 101 | 133 | {/if} 134 |
135 | {/if} 136 | 137 | {#if modalVisible} 138 | 143 | {/if} 144 | -------------------------------------------------------------------------------- /packages/ui/README.md: -------------------------------------------------------------------------------- 1 | # `@svelte-on-solana/wallet-adapter-ui` 2 | 3 | Pre-built components for integrating with Solana wallets using Svelte 4 | 5 | ## Getting Started 6 | 7 | The UI components need to be installed into a project that is already set up with `@solana/web3.js` and the base wallet adapters. Therefore, it cannot work standalone. 8 | 9 | During this process, you will: 10 | 11 | - 📦 Install the base wallet adapters 12 | - 📦 Install the svelte adapter and svelte UI 13 | - 🔨 Add the `ConnectionProvider` ([`AnchorConnectionProvider`](https://github.com/svelte-on-solana/wallet-adapter/blob/master/packages/anchor/README.md) if you're using Anchor) 14 | - 🔨 Add the `WalletProvider` component 15 | - 🔨 Add the `WalletMultiButton` component 16 | 17 | ## Installing 18 | 19 | You have already installed the core package to run the wallet Svelte Store [@svelte-on-solana/wallet-adapter-core](github.com/svelte-on-solana/wallet-adapter/blob/master/packages/core/README.md). Then install the UI components contained in this package 20 | 21 | ```shell 22 | npm i @svelte-on-solana/wallet-adapter-ui 23 | ``` 24 | 25 | ## Set Up 26 | 27 | There are three components that you need to get set up: 28 | 29 | - `WalletProvider` 30 | - `ConnectionProvider` (`[AnchorConnectionProvider](https://github.com/svelte-on-solana/wallet-adapter/blob/master/packages/anchor/README.md)` if you're using Anchor) 31 | - `WalletMultiButton` 32 | 33 | `WalletProvider` is a component used to initialize the wallet stores and add event listeners 34 | 35 | | prop | type | default | 36 | | ---------------- | ----------- | ----------------- | 37 | | localStorageKey? | `string` | `'walletAdapter'` | 38 | | wallets | `Wallets[]` | | 39 | | autoConnect? | `boolean` | `false` | 40 | 41 | `ConnectionProvider` is a component used to establish a connection with the network. 42 | 43 | | prop | type | default | 44 | | ------- | -------- | ------- | 45 | | network | `string` | | 46 | 47 | Alternatively you can use `AnchorConnectionProvider` for Anchor Dapps. 48 | 49 | | prop | type | default | 50 | | ------- | -------- | ------- | 51 | | network | `string` | | 52 | | idl | `Idl` | | 53 | 54 | `WalletMultiButton` is a component used as the entry point to connect/disconnect a wallet. 55 | 56 | | prop | type | default | 57 | | ------------------ | -------- | ------- | 58 | | maxNumberOfWallets | `number` | `3` | 59 | 60 | ## SvelteKit 61 | 62 | First you need to install some additional packages to make the Torus implementation compatible with SvelteKit. 63 | 64 | ```shell 65 | npm install -D @esbuild-plugins/node-globals-polyfill @rollup/plugin-inject rollup-plugin-node-polyfills 66 | ``` 67 | 68 | Then you have to adjust the **vite.config.js** file to prepare the project for all the Solana packages previously installed. 69 | 70 | ```javascript 71 | import { sveltekit } from '@sveltejs/kit/vite' 72 | 73 | const config = { 74 | plugins: [sveltekit()], 75 | optimizeDeps: { 76 | include: ['@solana/web3.js', 'buffer'], 77 | esbuildOptions: { 78 | target: 'esnext', 79 | plugins: [NodeGlobalsPolyfillPlugin({ buffer: true })], 80 | }, 81 | }, 82 | resolve: { 83 | alias: { 84 | $utils: path.resolve('src/utils/'), 85 | stream: 'rollup-plugin-node-polyfills/polyfills/stream', 86 | }, 87 | }, 88 | define: { 89 | 'process.env.BROWSER': true, 90 | 'process.env.NODE_DEBUG': JSON.stringify(''), 91 | }, 92 | build: { 93 | target: 'esnext', 94 | commonjsOptions: { 95 | transformMixedEsModules: true 96 | }, 97 | rollupOptions: { 98 | plugins: [inject({ Buffer: ['buffer', 'Buffer'] }), nodePolyfills({ crypto: true })], 99 | }, 100 | } 101 | } 102 | 103 | export default config 104 | ``` 105 | 106 | And then in the **\_\_layout.svelte** component you can import the wallets and setup the UI components. 107 | 108 | ```html 109 | 144 | 145 | 146 | 147 |
148 | 149 |
150 | 151 | ``` 152 | 153 | ## Svelte Template 154 | 155 | You have to adjust some stuff in the configuration in your project. 156 | 157 | > Enable JSON module resolving in `app/tsconfig.json` 158 | 159 | ```json 160 | { 161 | "extends": "@tsconfig/svelte/tsconfig.json", 162 | "compilerOptions": { 163 | "resolveJsonModule": true 164 | }, 165 | 166 | "include": ["src/**/*"], 167 | "exclude": ["node_modules/*", "__sapper__/*", "public/*"] 168 | } 169 | ``` 170 | 171 | > Install a few plugins to take care about JSON imports and built-on Node.js modules not available in the browser. 172 | 173 | ```shell 174 | npm install -D @rollup/plugin-json rollup-plugin-node-builtins rollup-plugin-node-globals 175 | ``` 176 | 177 | > Adjust `rollup.config.js` to import those plugins 178 | 179 | ```javascript 180 | // ... other imports 181 | import json from '@rollup/plugin-json'; 182 | import builtins from 'rollup-plugin-node-builtins'; 183 | import globals from 'rollup-plugin-node-globals'; 184 | 185 | export default { 186 | // ... other configs 187 | plugins: [ 188 | // ... other rollup plugins 189 | resolve({ 190 | browser: true, 191 | dedupe: ['svelte'], 192 | preferBuiltins: false 193 | }), 194 | // ... more rollup plugins 195 | json(), 196 | globals(), 197 | builtins() 198 | ] 199 | }; 200 | ``` 201 | 202 | > Then in `App.svelte` or the entry point of your SPA, you can setup the wallet and components like this. 203 | 204 | ```html 205 | 221 | 222 | 223 | 224 | 225 | 226 | {#if $walletStore?.connected} 227 |
My wallet is connected
228 | {/if} 229 | ``` 230 | 231 | ## Working with Anchor 232 | 233 | If you work with Anchor you will need the `AnchorConnectionProvider` component and its workSpace [@svelte-on-solana/wallet-adapter-anchor](https://github.com/svelte-on-solana/wallet-adapter/blob/master/packages/anchor/README.md) 234 | 235 | ## Example Implementation 236 | 237 | See example implementations of the `@svelte-on-solana/wallet-adapter-ui` library. 238 | 239 | - [Demo site][1] 240 | 241 | [1]: https://github.com/silvestrevivo/solana-svelte-counter/tree/master/app 242 | -------------------------------------------------------------------------------- /packages/ui/src/lib/styles.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;700&display=swap'); 2 | 3 | .wallet-adapter-button { 4 | background-color: transparent; 5 | border: none; 6 | color: #fff; 7 | cursor: pointer; 8 | display: flex; 9 | align-items: center; 10 | font-family: 'DM Sans', 'Roboto', 'Helvetica Neue', Helvetica, Arial, sans-serif; 11 | font-size: 16px; 12 | font-weight: 600; 13 | height: 48px; 14 | line-height: 48px; 15 | padding: 0 24px; 16 | border-radius: 4px; 17 | } 18 | 19 | .wallet-adapter-button-trigger { 20 | background-color: #512da8; 21 | } 22 | 23 | .wallet-adapter-button:not([disabled]):focus-visible { 24 | outline-color: white; 25 | } 26 | 27 | .wallet-adapter-button:not([disabled]):hover { 28 | background-color: #1a1f2e; 29 | } 30 | 31 | .wallet-adapter-button[disabled] { 32 | background: #404144; 33 | color: #999; 34 | cursor: not-allowed; 35 | } 36 | 37 | .wallet-adapter-button-end-icon, 38 | .wallet-adapter-button-start-icon, 39 | .wallet-adapter-button-end-icon img, 40 | .wallet-adapter-button-start-icon img { 41 | display: flex; 42 | align-items: center; 43 | justify-content: center; 44 | width: 24px; 45 | height: 24px; 46 | } 47 | 48 | .wallet-adapter-button-end-icon { 49 | margin-left: 12px; 50 | } 51 | 52 | .wallet-adapter-button-start-icon { 53 | margin-right: 12px; 54 | } 55 | 56 | .wallet-adapter-collapse { 57 | width: 100%; 58 | } 59 | 60 | .wallet-adapter-dropdown { 61 | position: relative; 62 | display: inline-block; 63 | } 64 | 65 | .wallet-adapter-dropdown-list { 66 | position: absolute; 67 | z-index: 99; 68 | display: grid; 69 | grid-template-rows: 1fr; 70 | grid-row-gap: 10px; 71 | padding: 10px; 72 | top: 100%; 73 | right: 0; 74 | margin: 0; 75 | list-style: none; 76 | background: #2c2d30; 77 | border-radius: 10px; 78 | box-shadow: 0px 8px 20px rgba(0, 0, 0, 0.6); 79 | opacity: 0; 80 | visibility: hidden; 81 | transition: opacity 200ms ease, transform 200ms ease, visibility 200ms; 82 | font-family: 'DM Sans', 'Roboto', 'Helvetica Neue', Helvetica, Arial, sans-serif; 83 | } 84 | 85 | .wallet-adapter-dropdown-list-active { 86 | opacity: 1; 87 | visibility: visible; 88 | transform: translateY(10px); 89 | } 90 | 91 | .wallet-adapter-dropdown-list-item { 92 | display: flex; 93 | flex-direction: row; 94 | justify-content: center; 95 | align-items: center; 96 | border: none; 97 | outline: none; 98 | cursor: pointer; 99 | white-space: nowrap; 100 | box-sizing: border-box; 101 | padding: 0 20px; 102 | width: 100%; 103 | border-radius: 6px; 104 | font-size: 14px; 105 | font-weight: 600; 106 | height: 37px; 107 | color: #fff; 108 | } 109 | 110 | .wallet-adapter-dropdown-list-item:not([disabled]):hover { 111 | background-color: #1a1f2e; 112 | } 113 | 114 | .wallet-adapter-modal-collapse-button svg { 115 | align-self: center; 116 | fill: #999; 117 | } 118 | 119 | .wallet-adapter-modal-collapse-button.wallet-adapter-modal-collapse-button-active svg { 120 | transform: rotate(180deg); 121 | transition: transform ease-in 150ms; 122 | } 123 | 124 | .wallet-adapter-modal { 125 | position: fixed; 126 | top: 0; 127 | left: 0; 128 | right: 0; 129 | bottom: 0; 130 | opacity: 0; 131 | transition: opacity linear 150ms; 132 | background: rgba(0, 0, 0, 0.5); 133 | z-index: 1040; 134 | overflow-y: auto; 135 | } 136 | 137 | .wallet-adapter-modal.wallet-adapter-modal-fade-in { 138 | opacity: 1; 139 | } 140 | 141 | .wallet-adapter-modal-button-close { 142 | display: flex; 143 | align-items: center; 144 | justify-content: center; 145 | position: absolute; 146 | top: 18px; 147 | right: 18px; 148 | padding: 12px; 149 | cursor: pointer; 150 | background: #1a1f2e; 151 | border: none; 152 | border-radius: 50%; 153 | } 154 | 155 | .wallet-adapter-modal-button-close:focus-visible { 156 | outline-color: white; 157 | } 158 | 159 | .wallet-adapter-modal-button-close svg { 160 | fill: #777; 161 | transition: fill 200ms ease 0s; 162 | } 163 | 164 | .wallet-adapter-modal-button-close:hover svg { 165 | fill: #fff; 166 | } 167 | 168 | .wallet-adapter-modal-overlay { 169 | background: rgba(0, 0, 0, 0.5); 170 | position: fixed; 171 | top: 0; 172 | left: 0; 173 | bottom: 0; 174 | right: 0; 175 | } 176 | 177 | .wallet-adapter-modal-container { 178 | display: flex; 179 | margin: 3rem; 180 | min-height: calc(100vh - 6rem); /* 100vh - 2 * margin */ 181 | align-items: center; 182 | justify-content: center; 183 | } 184 | 185 | @media (max-width: 480px) { 186 | .wallet-adapter-modal-container { 187 | margin: 1rem; 188 | min-height: calc(100vh - 2rem); /* 100vh - 2 * margin */ 189 | } 190 | } 191 | 192 | .wallet-adapter-modal-wrapper { 193 | box-sizing: border-box; 194 | position: relative; 195 | display: flex; 196 | align-items: center; 197 | flex-direction: column; 198 | z-index: 1050; 199 | max-width: 400px; 200 | border-radius: 10px; 201 | background: #10141f; 202 | box-shadow: 0px 8px 20px rgba(0, 0, 0, 0.6); 203 | font-family: 'DM Sans', 'Roboto', 'Helvetica Neue', Helvetica, Arial, sans-serif; 204 | flex: 1; 205 | } 206 | 207 | .wallet-adapter-modal-wrapper .wallet-adapter-button { 208 | width: 100%; 209 | } 210 | 211 | .wallet-adapter-modal-title { 212 | font-weight: 500; 213 | font-size: 24px; 214 | line-height: 36px; 215 | margin: 0; 216 | padding: 64px 48px 48px 48px; 217 | text-align: center; 218 | color: #fff; 219 | } 220 | 221 | @media (max-width: 374px) { 222 | .wallet-adapter-modal-title { 223 | font-size: 18px; 224 | } 225 | } 226 | 227 | .wallet-adapter-modal-list { 228 | margin: 0 0 12px 0; 229 | padding: 0; 230 | width: 100%; 231 | list-style: none; 232 | } 233 | 234 | .wallet-adapter-modal-list .wallet-adapter-button { 235 | font-weight: 400; 236 | border-radius: 0; 237 | font-size: 18px; 238 | } 239 | 240 | .wallet-adapter-modal-list .wallet-adapter-button-end-icon, 241 | .wallet-adapter-modal-list .wallet-adapter-button-start-icon, 242 | .wallet-adapter-modal-list .wallet-adapter-button-end-icon img, 243 | .wallet-adapter-modal-list .wallet-adapter-button-start-icon img { 244 | width: 28px; 245 | height: 28px; 246 | } 247 | 248 | .wallet-adapter-modal-list .wallet-adapter-button span { 249 | margin-left: auto; 250 | font-size: 14px; 251 | opacity: .6; 252 | } 253 | 254 | .wallet-adapter-modal-list-more { 255 | cursor: pointer; 256 | border: none; 257 | padding: 12px 24px 24px 12px; 258 | align-self: flex-end; 259 | display: flex; 260 | align-items: center; 261 | background-color: transparent; 262 | color: #fff; 263 | font-family: 'DM Sans', 'Roboto', 'Helvetica Neue', Helvetica, Arial, sans-serif; 264 | } 265 | 266 | .wallet-adapter-modal-list-more svg { 267 | transition: all 0.1s ease; 268 | fill: rgba(255, 255, 255, 1); 269 | margin-left: 0.5rem; 270 | } 271 | 272 | .wallet-adapter-modal-list-more span { 273 | font-size: 16px; 274 | } 275 | 276 | .wallet-adapter-modal-list-more-icon-rotate { 277 | transform: rotate(180deg); 278 | } 279 | 280 | .wallet-adapter-modal-middle { 281 | width: 100%; 282 | display: flex; 283 | flex-direction: column; 284 | align-items: center; 285 | padding: 0 24px 24px 24px; 286 | box-sizing: border-box; 287 | } 288 | 289 | .wallet-adapter-modal-middle-button { 290 | display: block; 291 | cursor: pointer; 292 | margin-top: 48px; 293 | width: 100%; 294 | background-color: #512da8; 295 | padding: 12px; 296 | font-size: 18px; 297 | border: none; 298 | border-radius: 8px; 299 | color: #fff; 300 | } 301 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /packages/core/LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /packages/core/src/walletStore.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | Adapter, 3 | MessageSignerWalletAdapter, 4 | MessageSignerWalletAdapterProps, 5 | SendTransactionOptions, 6 | SignerWalletAdapter, 7 | SignerWalletAdapterProps, 8 | WalletError, 9 | WalletName, 10 | } from '@solana/wallet-adapter-base'; 11 | import { WalletNotConnectedError, WalletNotReadyError, WalletReadyState } from '@solana/wallet-adapter-base'; 12 | import type { Connection, PublicKey, Transaction, TransactionSignature, VersionedTransaction } from '@solana/web3.js'; 13 | import { get, writable } from 'svelte/store'; 14 | import { WalletNotSelectedError } from './errors'; 15 | import { getLocalStorage, setLocalStorage } from './localStorage'; 16 | 17 | interface Wallet { 18 | adapter: Adapter; 19 | readyState: WalletReadyState; 20 | } 21 | 22 | type ErrorHandler = (error: WalletError) => void; 23 | type WalletPropsConfig = Pick & { wallets: Adapter[]}; 24 | type WalletReturnConfig = Pick; 25 | 26 | type WalletStatus = Pick; 27 | 28 | export interface WalletStore { 29 | // props 30 | autoConnect: boolean; 31 | wallets: Wallet[]; 32 | 33 | // wallet state 34 | adapter: Adapter | null; 35 | connected: boolean; 36 | connecting: boolean; 37 | disconnecting: boolean; 38 | localStorageKey: string; 39 | onError: ErrorHandler; 40 | publicKey: PublicKey | null; 41 | ready: WalletReadyState; 42 | wallet: Adapter | null; 43 | walletsByName: Record; 44 | name: WalletName | null; 45 | 46 | // wallet methods 47 | connect(): Promise; 48 | disconnect(): Promise; 49 | select(walletName: WalletName): void; 50 | sendTransaction( 51 | transaction: Transaction | VersionedTransaction, 52 | connection: Connection, 53 | options?: SendTransactionOptions 54 | ): Promise; 55 | signAllTransactions: SignerWalletAdapterProps['signAllTransactions'] | undefined; 56 | signMessage: MessageSignerWalletAdapterProps['signMessage'] | undefined; 57 | signTransaction: SignerWalletAdapterProps['signTransaction'] | undefined; 58 | } 59 | 60 | export const walletStore = createWalletStore(); 61 | 62 | function addAdapterEventListeners(adapter: Adapter) { 63 | const { onError, wallets } = get(walletStore); 64 | 65 | wallets.forEach(({adapter}) => { 66 | adapter.on('readyStateChange', onReadyStateChange, adapter); 67 | }) 68 | adapter.on('connect', onConnect); 69 | adapter.on('disconnect', onDisconnect); 70 | adapter.on('error', onError); 71 | } 72 | 73 | async function autoConnect() { 74 | const { adapter } = get(walletStore); 75 | 76 | try { 77 | walletStore.setConnecting(true); 78 | await adapter?.connect(); 79 | } catch (error: unknown) { 80 | // Clear the selected wallet 81 | walletStore.resetWallet(); 82 | // Don't throw error, but onError will still be called 83 | } finally { 84 | walletStore.setConnecting(false); 85 | } 86 | } 87 | 88 | async function connect(): Promise { 89 | const { connected, connecting, disconnecting, ready, adapter } = get(walletStore); 90 | if (connected || connecting || disconnecting) return; 91 | 92 | if (!adapter) throw newError(new WalletNotSelectedError()); 93 | 94 | if (!(ready === WalletReadyState.Installed || ready === WalletReadyState.Loadable)) { 95 | walletStore.resetWallet(); 96 | 97 | if (typeof window !== 'undefined') { 98 | window.open(adapter.url, '_blank'); 99 | } 100 | 101 | throw newError(new WalletNotReadyError()); 102 | } 103 | 104 | try { 105 | walletStore.setConnecting(true); 106 | await adapter.connect(); 107 | } catch (error: unknown) { 108 | walletStore.resetWallet(); 109 | throw error; 110 | } finally { 111 | walletStore.setConnecting(false); 112 | } 113 | } 114 | 115 | function createWalletStore() { 116 | const { subscribe, update } = writable({ 117 | autoConnect: false, 118 | wallets: [], 119 | adapter: null, 120 | connected: false, 121 | connecting: false, 122 | disconnecting: false, 123 | localStorageKey: 'walletAdapter', 124 | onError: (error: WalletError) => console.error(error), 125 | publicKey: null, 126 | ready: 'Unsupported' as WalletReadyState, 127 | wallet: null, 128 | name: null, 129 | walletsByName: {}, 130 | connect, 131 | disconnect, 132 | select, 133 | sendTransaction, 134 | signTransaction: undefined, 135 | signAllTransactions: undefined, 136 | signMessage: undefined, 137 | }); 138 | 139 | function updateWalletState(adapter: Adapter | null) { 140 | updateAdapter(adapter); 141 | update((store: WalletStore) => ({ 142 | ...store, 143 | name: adapter?.name || null, 144 | wallet: adapter, 145 | ready: adapter?.readyState || 'Unsupported' as WalletReadyState, 146 | publicKey: adapter?.publicKey || null, 147 | connected: adapter?.connected || false, 148 | })); 149 | 150 | if (!adapter) return; 151 | 152 | if (shouldAutoConnect()) { 153 | autoConnect(); 154 | } 155 | } 156 | 157 | function updateWalletName(name: WalletName | null) { 158 | const { localStorageKey, walletsByName } = get(walletStore); 159 | 160 | const adapter = walletsByName?.[name as WalletName] ?? null; 161 | 162 | setLocalStorage(localStorageKey, name); 163 | updateWalletState(adapter); 164 | } 165 | 166 | function updateAdapter(adapter: Adapter | null) { 167 | removeAdapterEventListeners(); 168 | 169 | let signTransaction: SignerWalletAdapter['signTransaction'] | undefined = undefined; 170 | let signAllTransactions: SignerWalletAdapter['signAllTransactions'] | undefined = undefined; 171 | let signMessage: MessageSignerWalletAdapter['signMessage'] | undefined = undefined; 172 | 173 | if (adapter) { 174 | // Sign a transaction if the wallet supports it 175 | if ('signTransaction' in adapter) { 176 | signTransaction = async function (transaction: T) { 177 | const { connected } = get(walletStore); 178 | if (!connected) throw newError(new WalletNotConnectedError()); 179 | return await adapter.signTransaction(transaction); 180 | }; 181 | } 182 | 183 | // Sign multiple transactions if the wallet supports it 184 | if ('signAllTransactions' in adapter) { 185 | signAllTransactions = async function (transactions: T[]) { 186 | const { connected } = get(walletStore); 187 | if (!connected) throw newError(new WalletNotConnectedError()); 188 | return await adapter.signAllTransactions(transactions); 189 | }; 190 | } 191 | 192 | // Sign an arbitrary message if the wallet supports it 193 | if ('signMessage' in adapter) { 194 | signMessage = async function (message: Uint8Array) { 195 | const { connected } = get(walletStore); 196 | if (!connected) throw newError(new WalletNotConnectedError()); 197 | return await adapter.signMessage(message); 198 | }; 199 | } 200 | 201 | addAdapterEventListeners(adapter); 202 | } 203 | 204 | update((store: WalletStore) => ({ ...store, adapter, signTransaction, signAllTransactions, signMessage })); 205 | } 206 | 207 | return { 208 | resetWallet: () => updateWalletName(null), 209 | setConnecting: (connecting: boolean) => update((store: WalletStore) => ({ ...store, connecting })), 210 | setDisconnecting: (disconnecting: boolean) => update((store: WalletStore) => ({ ...store, disconnecting })), 211 | setReady: (ready: WalletReadyState) => update((store: WalletStore) => ({ ...store, ready })), 212 | subscribe, 213 | updateConfig: (walletConfig: WalletReturnConfig & { walletsByName: Record }) => 214 | update((store: WalletStore) => ({ 215 | ...store, 216 | ...walletConfig, 217 | })), 218 | updateWallets: (wallets: Wallet[]) => update((store: WalletStore) => ({ ...store, ...wallets })), 219 | updateStatus: (walletStatus: WalletStatus) => update((store: WalletStore) => ({ ...store, ...walletStatus })), 220 | updateWallet: (walletName: WalletName) => updateWalletName(walletName), 221 | }; 222 | } 223 | 224 | async function disconnect(): Promise { 225 | const { disconnecting, adapter } = get(walletStore); 226 | if (disconnecting) return; 227 | 228 | if (!adapter) return walletStore.resetWallet(); 229 | 230 | try { 231 | walletStore.setDisconnecting(true); 232 | await adapter.disconnect(); 233 | } finally { 234 | walletStore.resetWallet(); 235 | walletStore.setDisconnecting(false); 236 | } 237 | } 238 | 239 | export async function initialize({ 240 | wallets, 241 | autoConnect = false, 242 | localStorageKey = 'walletAdapter', 243 | onError = (error: WalletError) => console.error(error), 244 | }: WalletPropsConfig): Promise { 245 | const walletsByName = wallets.reduce>((walletsByName, wallet) => { 246 | walletsByName[wallet.name] = wallet; 247 | return walletsByName; 248 | }, {}); 249 | 250 | // Wrap adapters to conform to the `Wallet` interface 251 | const mapWallets = wallets.map((adapter) => ({ 252 | adapter, 253 | readyState: adapter.readyState, 254 | })) 255 | 256 | walletStore.updateConfig({ 257 | wallets: mapWallets, 258 | walletsByName, 259 | autoConnect, 260 | localStorageKey, 261 | onError, 262 | }); 263 | 264 | const walletName = getLocalStorage(localStorageKey); 265 | 266 | if (walletName) { 267 | walletStore.updateWallet(walletName); 268 | } 269 | } 270 | 271 | function newError(error: WalletError): WalletError { 272 | const { onError } = get(walletStore); 273 | onError(error); 274 | return error; 275 | } 276 | 277 | function onConnect() { 278 | const { adapter } = get(walletStore); 279 | if (!adapter) return; 280 | 281 | walletStore.updateStatus({ 282 | publicKey: adapter.publicKey, 283 | connected: adapter.connected, 284 | }); 285 | } 286 | 287 | function onDisconnect() { 288 | walletStore.resetWallet(); 289 | } 290 | 291 | function onReadyStateChange(this: Adapter, readyState: WalletReadyState) { 292 | const { adapter, wallets } = get(walletStore); 293 | if (!adapter) return; 294 | 295 | walletStore.setReady(adapter.readyState); 296 | 297 | // When the wallets change, start to listen for changes to their `readyState` 298 | const walletIndex = wallets.findIndex(({ adapter }) => adapter.name === this.name); 299 | if (walletIndex === -1) { 300 | return; 301 | } else { 302 | walletStore.updateWallets([ 303 | ...wallets.slice(0, walletIndex), 304 | { ...wallets[walletIndex], readyState }, 305 | ...wallets.slice(walletIndex + 1), 306 | ]) 307 | } 308 | } 309 | 310 | function removeAdapterEventListeners(): void { 311 | const { adapter, onError, wallets } = get(walletStore); 312 | if (!adapter) return; 313 | 314 | wallets.forEach(({adapter}) => { 315 | adapter.off('readyStateChange', onReadyStateChange, adapter); 316 | }) 317 | adapter.off('connect', onConnect); 318 | adapter.off('disconnect', onDisconnect); 319 | adapter.off('error', onError); 320 | } 321 | 322 | async function select(walletName: WalletName): Promise { 323 | const { name, adapter } = get(walletStore); 324 | if (name === walletName) return; 325 | 326 | if (adapter) await disconnect(); 327 | 328 | walletStore.updateWallet(walletName); 329 | } 330 | 331 | async function sendTransaction( 332 | transaction: Transaction | VersionedTransaction, 333 | connection: Connection, 334 | options?: SendTransactionOptions 335 | ): Promise { 336 | const { connected, adapter } = get(walletStore); 337 | if (!connected) throw newError(new WalletNotConnectedError()); 338 | if (!adapter) throw newError(new WalletNotSelectedError()); 339 | 340 | return await adapter.sendTransaction(transaction, connection, options); 341 | } 342 | 343 | function shouldAutoConnect(): boolean { 344 | const { adapter, autoConnect, ready, connected, connecting } = get(walletStore); 345 | 346 | return !( 347 | !autoConnect || 348 | !adapter || 349 | !( 350 | ready === WalletReadyState.Installed || 351 | ready === WalletReadyState.Loadable 352 | ) || 353 | connected || 354 | connecting 355 | ); 356 | } 357 | 358 | if (typeof window !== 'undefined') { 359 | // Ensure the adapter listeners are invalidated before refreshing the page. 360 | window.addEventListener('beforeunload', removeAdapterEventListeners); 361 | } 362 | -------------------------------------------------------------------------------- /packages/ui/src/lib/WalletModal.svelte: -------------------------------------------------------------------------------- 1 | 60 | 61 | 62 | 63 | 302 | --------------------------------------------------------------------------------