diff --git a/package-lock.json b/package-lock.json index 98b68364..69697b3a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9600,6 +9600,120 @@ "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" }, + "node_modules/@solana/buffer-layout": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@solana/buffer-layout/-/buffer-layout-4.0.1.tgz", + "integrity": "sha512-E1ImOIAD1tBZFRdjeM4/pzTiTApC0AOBGwyAMS4fwIodCWArzJ3DWdoh8cKxeFM2fElkxBh2Aqts1BPC373rHA==", + "dependencies": { + "buffer": "~6.0.3" + }, + "engines": { + "node": ">=5.10" + } + }, + "node_modules/@solana/web3.js": { + "version": "1.91.8", + "resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.91.8.tgz", + "integrity": "sha512-USa6OS1jbh8zOapRJ/CBZImZ8Xb7AJjROZl5adql9TpOoBN9BUzyyouS5oPuZHft7S7eB8uJPuXWYjMi6BHgOw==", + "dependencies": { + "@babel/runtime": "^7.24.5", + "@noble/curves": "^1.4.0", + "@noble/hashes": "^1.4.0", + "@solana/buffer-layout": "^4.0.1", + "agentkeepalive": "^4.5.0", + "bigint-buffer": "^1.1.5", + "bn.js": "^5.2.1", + "borsh": "^0.7.0", + "bs58": "^4.0.1", + "buffer": "6.0.3", + "fast-stable-stringify": "^1.0.0", + "jayson": "^4.1.0", + "node-fetch": "^2.7.0", + "rpc-websockets": "^7.11.0", + "superstruct": "^0.14.2" + } + }, + "node_modules/@solana/web3.js/node_modules/@babel/runtime": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.5.tgz", + "integrity": "sha512-Nms86NXrsaeU9vbBJKni6gXiEXZ4CVpYVzEjDH9Sb8vmZ3UljyA1GSOJl/6LGPO8EHLuSF9H+IxNXHPX8QHJ4g==", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@solana/web3.js/node_modules/@noble/curves": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.0.tgz", + "integrity": "sha512-p+4cb332SFCrReJkCYe8Xzm0OWi4Jji5jVdIZRL/PmacmDkFNw6MrrV+gGpiPxLHbV+zKFRywUWbaseT+tZRXg==", + "dependencies": { + "@noble/hashes": "1.4.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@solana/web3.js/node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@solana/web3.js/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/@solana/web3.js/node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + }, + "node_modules/@solana/web3.js/node_modules/superstruct": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/superstruct/-/superstruct-0.14.2.tgz", + "integrity": "sha512-nPewA6m9mR3d6k7WkZ8N8zpTWfenFH3q9pA2PkuiZxINr9DKB2+40wEQf0ixn8VaGuJ78AB6iWOtStI+/4FKZQ==" + }, + "node_modules/@solana/web3.js/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/@solana/web3.js/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/@solana/web3.js/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/@stablelib/aead": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@stablelib/aead/-/aead-1.0.1.tgz", @@ -11027,7 +11141,6 @@ "version": "3.4.35", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", - "dev": true, "dependencies": { "@types/node": "*" } @@ -13505,6 +13618,17 @@ "node": ">= 6.0.0" } }, + "node_modules/agentkeepalive": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz", + "integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==", + "dependencies": { + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -14527,6 +14651,18 @@ "node": "*" } }, + "node_modules/bigint-buffer": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/bigint-buffer/-/bigint-buffer-1.1.5.tgz", + "integrity": "sha512-trfYco6AoZ+rKhKnxA0hgX0HAbVP/s808/EuDSe2JDzUnCp/xAsli35Orvk67UrTEcwuxZqYZDmfA2RXJgxVvA==", + "hasInstallScript": true, + "dependencies": { + "bindings": "^1.3.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/bignumber.js": { "version": "7.2.1", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-7.2.1.tgz", @@ -14555,6 +14691,14 @@ "node": ">=8" } }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, "node_modules/bitsyntax": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/bitsyntax/-/bitsyntax-0.1.0.tgz", @@ -14712,6 +14856,16 @@ "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", "dev": true }, + "node_modules/borsh": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/borsh/-/borsh-0.7.0.tgz", + "integrity": "sha512-CLCsZGIBCFnPtkNnieW/a8wmreDmfUtjU2m9yHrzPXIlNbqVs0AQrSatSG6vdNYUqdc83tkQi2eHfF98ubzQLA==", + "dependencies": { + "bn.js": "^5.2.0", + "bs58": "^4.0.0", + "text-encoding-utf-8": "^1.0.2" + } + }, "node_modules/bowser": { "version": "2.11.0", "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", @@ -17407,6 +17561,17 @@ "node": ">= 14" } }, + "node_modules/delay": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/delay/-/delay-5.0.0.tgz", + "integrity": "sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -18254,6 +18419,14 @@ "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" }, + "node_modules/es6-promisify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "integrity": "sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==", + "dependencies": { + "es6-promise": "^4.0.3" + } + }, "node_modules/es6-symbol": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", @@ -20512,6 +20685,14 @@ "node >=0.6.0" ] }, + "node_modules/eyes": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", + "integrity": "sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ==", + "engines": { + "node": "> 0.1.90" + } + }, "node_modules/fake-merkle-patricia-tree": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/fake-merkle-patricia-tree/-/fake-merkle-patricia-tree-1.0.1.tgz", @@ -20588,6 +20769,11 @@ "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" }, + "node_modules/fast-stable-stringify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fast-stable-stringify/-/fast-stable-stringify-1.0.0.tgz", + "integrity": "sha512-wpYMUmFu5f00Sm0cj2pfivpmawLZ0NKdviQ4w9zJeR8JVtOpOxHmLaJuj0vxvGqMJQWyP/COUkF75/57OKyRag==" + }, "node_modules/fast-xml-parser": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.1.2.tgz", @@ -20741,6 +20927,11 @@ "webpack": "^4.0.0 || ^5.0.0" } }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" + }, "node_modules/filelist": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", @@ -22270,6 +22461,14 @@ "node": ">=10.17.0" } }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "dependencies": { + "ms": "^2.0.0" + } + }, "node_modules/iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", @@ -23335,6 +23534,77 @@ "node": ">=10" } }, + "node_modules/jayson": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/jayson/-/jayson-4.1.0.tgz", + "integrity": "sha512-R6JlbyLN53Mjku329XoRT2zJAE6ZgOQ8f91ucYdMCD4nkGCF9kZSrcGXpHIU4jeKj58zUZke2p+cdQchU7Ly7A==", + "dependencies": { + "@types/connect": "^3.4.33", + "@types/node": "^12.12.54", + "@types/ws": "^7.4.4", + "commander": "^2.20.3", + "delay": "^5.0.0", + "es6-promisify": "^5.0.0", + "eyes": "^0.1.8", + "isomorphic-ws": "^4.0.1", + "json-stringify-safe": "^5.0.1", + "JSONStream": "^1.3.5", + "uuid": "^8.3.2", + "ws": "^7.4.5" + }, + "bin": { + "jayson": "bin/jayson.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jayson/node_modules/@types/node": { + "version": "12.20.55", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", + "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==" + }, + "node_modules/jayson/node_modules/@types/ws": { + "version": "7.4.7", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.4.7.tgz", + "integrity": "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/jayson/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "node_modules/jayson/node_modules/isomorphic-ws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz", + "integrity": "sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==", + "peerDependencies": { + "ws": "*" + } + }, + "node_modules/jayson/node_modules/ws": { + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/jest": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz", @@ -24468,6 +24738,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "engines": [ + "node >= 0.2.0" + ] + }, "node_modules/jsonpointer": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", @@ -24485,6 +24763,21 @@ "node": "*" } }, + "node_modules/JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "dependencies": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + }, + "bin": { + "JSONStream": "bin.js" + }, + "engines": { + "node": "*" + } + }, "node_modules/jsonwebtoken": { "version": "8.5.1", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", @@ -30968,6 +31261,29 @@ "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==", "dev": true }, + "node_modules/rpc-websockets": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/rpc-websockets/-/rpc-websockets-7.11.0.tgz", + "integrity": "sha512-IkLYjayPv6Io8C/TdCL5gwgzd1hFz2vmBZrjMw/SPEXo51ETOhnzgS4Qy5GWi2JQN7HKHa66J3+2mv0fgNh/7w==", + "dependencies": { + "eventemitter3": "^4.0.7", + "uuid": "^8.3.2", + "ws": "^8.5.0" + }, + "funding": { + "type": "paypal", + "url": "https://paypal.me/kozjak" + }, + "optionalDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + } + }, + "node_modules/rpc-websockets/node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, "node_modules/run-async": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz", @@ -33103,6 +33419,11 @@ "node": ">=8" } }, + "node_modules/text-encoding-utf-8": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/text-encoding-utf-8/-/text-encoding-utf-8-1.0.2.tgz", + "integrity": "sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg==" + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -36730,11 +37051,14 @@ "@polkadot/types": "^10.2.1", "@polkadot/util": "^11.1.2", "@polkadot/util-crypto": "^11.1.2", + "@solana/web3.js": "^1.91.8", "@toruslabs/openlogin-ed25519": "^7.0.0", "@web3auth/ethereum-provider": "^7.3.2", "eth-json-rpc-middleware": "^9.0.1", "ethereumjs-wallet": "^1.0.2", - "json-rpc-engine": "^6.1.0" + "json-rpc-engine": "^6.1.0", + "tweetnacl": "^1.0.3", + "tweetnacl-util": "^0.15.1" } }, "packages/wallet-engine/node_modules/@biconomy/mexa": { diff --git a/packages/embed-wallet/src/types.ts b/packages/embed-wallet/src/types.ts index 1b74bade..1db6141e 100644 --- a/packages/embed-wallet/src/types.ts +++ b/packages/embed-wallet/src/types.ts @@ -45,7 +45,7 @@ export type UserInfo = { export type WalletAccount = { address: string; - type: 'ethereum' | 'ed25519'; + type: 'ethereum' | 'ed25519' | 'solana'; name: string; }; diff --git a/packages/ui/src/icons/SolanaIcon.tsx b/packages/ui/src/icons/SolanaIcon.tsx new file mode 100644 index 00000000..04a73091 --- /dev/null +++ b/packages/ui/src/icons/SolanaIcon.tsx @@ -0,0 +1,36 @@ +import { memo } from 'react'; +import { SvgIcon, SvgIconProps } from '@mui/material'; + +export const SolanaIcon = memo((props: SvgIconProps) => ( + + + + + + + + + + +)); diff --git a/packages/ui/src/icons/index.ts b/packages/ui/src/icons/index.ts index dbdf6570..4d5dce53 100644 --- a/packages/ui/src/icons/index.ts +++ b/packages/ui/src/icons/index.ts @@ -44,6 +44,7 @@ export * from './ComingSoonIcon'; export * from './EthIcon'; export * from './UsdcIcon'; export * from './UsdtIcon'; +export * from './SolanaIcon'; export * from './TransactionInIcon'; export * from './TransactionOutIcon'; export * from './TopUpIcon'; diff --git a/packages/wallet-engine/package.json b/packages/wallet-engine/package.json index e89fe996..bd8e24a7 100644 --- a/packages/wallet-engine/package.json +++ b/packages/wallet-engine/package.json @@ -10,16 +10,19 @@ "dependencies": { "@biconomy/mexa": "^3.0.6", "@cere/freeport-sc-sdk": "0.23.0", - "@polkadot/keyring": "^11.1.2", "@polkadot/api": "^10.2.1", + "@polkadot/keyring": "^11.1.2", + "@polkadot/types": "^10.2.1", "@polkadot/util": "^11.1.2", "@polkadot/util-crypto": "^11.1.2", - "@polkadot/types": "^10.2.1", + "@solana/web3.js": "^1.91.8", "@toruslabs/openlogin-ed25519": "^7.0.0", "@web3auth/ethereum-provider": "^7.3.2", "eth-json-rpc-middleware": "^9.0.1", "ethereumjs-wallet": "^1.0.2", - "json-rpc-engine": "^6.1.0" + "json-rpc-engine": "^6.1.0", + "tweetnacl": "^1.0.3", + "tweetnacl-util": "^0.15.1" }, "scripts": {} } diff --git a/packages/wallet-engine/src/accounts.ts b/packages/wallet-engine/src/accounts.ts index feb1ac4c..b95e4639 100644 --- a/packages/wallet-engine/src/accounts.ts +++ b/packages/wallet-engine/src/accounts.ts @@ -3,6 +3,7 @@ import { getED25519Key } from '@toruslabs/openlogin-ed25519'; import { decodeAddress, encodeAddress, isEthereumAddress } from '@polkadot/util-crypto'; import { hexToU8a, isHex } from '@polkadot/util'; import { Keyring } from '@polkadot/keyring'; +import { Keypair as SolKeypair } from '@solana/web3.js'; import { KeyPair, KeyType, Account } from './types'; import { CERE_SS58_PREFIX } from './constants'; @@ -29,6 +30,18 @@ const pairFactoryMap: Record KeyPair> = { address: encodeAddress(publicKey, CERE_SS58_PREFIX), }; }, + + solana: (privateKey) => { + const { sk: ed25519Key } = getED25519Key(privateKey); + const { publicKey, secretKey } = SolKeypair.fromSecretKey(ed25519Key); + + return { + type: 'solana', + publicKey: publicKey.toBuffer(), + secretKey: Buffer.from(secretKey), + address: publicKey.toBase58(), + }; + }, }; export type KeyPairOptions = { @@ -45,6 +58,10 @@ export const getKeyPair = ({ privateKey, type }: KeyPairOptions): KeyPair => { }; export const exportAccountToJson = ({ privateKey, type, passphrase }: KeyPairOptions & { passphrase?: string }) => { + if (type === 'solana') { + throw new Error('Not implemented'); + } + const { publicKey, secretKey } = getKeyPair({ type, privateKey }); const keyring = new Keyring({ type }); @@ -67,5 +84,10 @@ const isValidPolkadotAddress = (address: string) => { } }; -export const isValidAddress = (address: string, type: KeyType) => - type === 'ethereum' ? isEthereumAddress(address) : isValidPolkadotAddress(address); +export const isValidAddress = (address: string, type: KeyType) => { + if (type === 'solana') { + throw new Error('Not implemented'); + } + + return type === 'ethereum' ? isEthereumAddress(address) : isValidPolkadotAddress(address); +}; diff --git a/packages/wallet-engine/src/engine/accounts.ts b/packages/wallet-engine/src/engine/accounts.ts index 343425b6..71b7e54a 100644 --- a/packages/wallet-engine/src/engine/accounts.ts +++ b/packages/wallet-engine/src/engine/accounts.ts @@ -22,7 +22,7 @@ export const createAccountsEngine = ({ getPrivateKey, getAccounts, onUpdateAccou engine.push( createScaffoldMiddleware({ wallet_accounts: createAsyncMiddleware(async (req, res) => { - res.result = createAccounts(['ethereum', 'ed25519']); + res.result = createAccounts(['ethereum', 'ed25519', 'solana']); }), ed25519_accounts: createAsyncMiddleware(async (req, res) => { @@ -33,13 +33,17 @@ export const createAccountsEngine = ({ getPrivateKey, getAccounts, onUpdateAccou res.result = createAccounts(['ethereum']).map((account) => account.address); }), + solana_accounts: createAsyncMiddleware(async (req, res) => { + res.result = createAccounts(['solana']).map((account) => account.address); + }), + eth_requestAccounts: createAsyncMiddleware(async (req, res) => { res.result = createAccounts(['ethereum']).map((account) => account.address); }), wallet_updateAccounts: createAsyncMiddleware(async (req, res) => { - const accounts = createAccounts(['ethereum', 'ed25519']); - const [eth, ed255519] = accounts; + const accounts = createAccounts(['ethereum', 'ed25519', 'solana']); + const [eth, ed255519, solana] = accounts; onUpdateAccounts(accounts); @@ -49,6 +53,7 @@ export const createAccountsEngine = ({ getPrivateKey, getAccounts, onUpdateAccou engine.emit('message', { type: 'wallet_accountsChanged', data: accounts }); engine.emit('message', { type: 'eth_accountChanged', data: eth }); engine.emit('message', { type: 'ed25519_accountChanged', data: ed255519 }); + engine.emit('message', { type: 'solana_accountChanged', data: solana }); /** * Standard eip-1193 event diff --git a/packages/wallet-engine/src/engine/approve.ts b/packages/wallet-engine/src/engine/approve.ts index 7929b242..d42b1918 100644 --- a/packages/wallet-engine/src/engine/approve.ts +++ b/packages/wallet-engine/src/engine/approve.ts @@ -127,6 +127,16 @@ export const createApproveEngine = ({ }); }), + solana_signMessage: createRequestMiddleware<[string, string]>(async (req, proceed) => { + const [account, message] = req.params!; + + await onPersonalSign({ + preopenInstanceId: req.preopenInstanceId, + params: [message, account, 'solana' as KeyType], + proceed, + }); + }), + eth_sendTransaction: createRequestMiddleware<[IncomingTransaction]>(async (req, proceed) => { await onSendTransaction({ preopenInstanceId: req.preopenInstanceId, diff --git a/packages/wallet-engine/src/engine/provider.ts b/packages/wallet-engine/src/engine/provider.ts index 9302ff69..6f09763b 100644 --- a/packages/wallet-engine/src/engine/provider.ts +++ b/packages/wallet-engine/src/engine/provider.ts @@ -10,12 +10,14 @@ import { createPermissionsEngine, PermissionsEngineOptions } from './permissions import type { EthereumEngineOptions } from './ethereum'; import type { PolkadotEngineOptions } from './polkadot'; import type { AccountsEngineOptions } from './accounts'; +import type { SolanaEngineOptions } from './solana'; export type ProviderEngineOptions = WalletEngineOptions & AccountsEngineOptions & ApproveEngineOptions & EthereumEngineOptions & PolkadotEngineOptions & + SolanaEngineOptions & PermissionsEngineOptions; class EngineProvider extends EventEmitter implements Provider { @@ -53,6 +55,12 @@ class UnsafeEngine extends Engine { return createPolkadotEngine(options); }); + this.pushEngine( + import(/* webpackChunkName: "accountsEngine" */ './solana').then(({ createSolanaEngine }) => + createSolanaEngine(options), + ), + ); + /** * Should always be the last one since it is currently handles real RPC requests * TODO: Replace with fetch middleware in future diff --git a/packages/wallet-engine/src/engine/solana.ts b/packages/wallet-engine/src/engine/solana.ts new file mode 100644 index 00000000..efba43fd --- /dev/null +++ b/packages/wallet-engine/src/engine/solana.ts @@ -0,0 +1,47 @@ +import { createAsyncMiddleware, createScaffoldMiddleware } from 'json-rpc-engine'; +import { u8aToHex } from '@polkadot/util'; +import nacl from 'tweetnacl'; +import { decodeUTF8 } from 'tweetnacl-util'; + +import { Engine } from './engine'; +import { getKeyPair } from '../accounts'; + +export type SolanaEngineOptions = { + getPrivateKey: () => string | undefined; +}; + +export const createSolanaEngine = ({ getPrivateKey }: SolanaEngineOptions) => { + const engine = new Engine(); + + const getPair = (address: string) => { + const privateKey = getPrivateKey(); + + if (!privateKey) { + throw new Error('No private key was provided!'); + } + + return getKeyPair({ type: 'solana', privateKey }); + }; + + engine.push( + createScaffoldMiddleware({ + /** + * Sign a message with solana keypair + * https://solana.com/developers/cookbook/wallets/sign-message + * + * TODO: Rethink the implementation later after the solana blockchain hackathon + */ + solana_signMessage: createAsyncMiddleware(async (req, res) => { + const [address, message] = req.params as string[]; + const pair = getPair(address); + + const messageBytes = decodeUTF8(message); + const signature = nacl.sign.detached(messageBytes, pair.secretKey); + + res.result = u8aToHex(signature); + }), + }), + ); + + return engine; +}; diff --git a/packages/wallet-engine/src/types.ts b/packages/wallet-engine/src/types.ts index db1ed20c..d6e39647 100644 --- a/packages/wallet-engine/src/types.ts +++ b/packages/wallet-engine/src/types.ts @@ -9,7 +9,13 @@ export declare type ChainConfig = { tickerName: string; }; -export type KeyType = 'ethereum' | 'ed25519'; +/** + * TODO: Solana key type was added as a temparary solution. + * Solana uses `ed25519` so it would be better to add another key property eg. `chainNamespace` instead of extending`type`. + * + * For simplification, we can use `type` for now. + */ +export type KeyType = 'ethereum' | 'ed25519' | 'solana'; export type KeyPair = { type: KeyType; address: string; diff --git a/playground/Wallet.tsx b/playground/Wallet.tsx index 8db19c9d..5a2035a4 100644 --- a/playground/Wallet.tsx +++ b/playground/Wallet.tsx @@ -11,6 +11,7 @@ export const Wallet = () => { const [ethBalance, setEthBalance] = useState(); const [cereAddress, setCereAddress] = useState(); const [cereBalance, setCereBalance] = useState(); + const [solanaAddress, setSolanaAddress] = useState(); const [isNewUser, setIsNewUser] = useState(false); const wallet = useWallet(); @@ -41,10 +42,11 @@ export const Wallet = () => { accounts.map((account) => account.address), ); - const [ethAccount, cereAccount] = accounts; + const [ethAccount, cereAccount, solanaAccount] = accounts; setCereAddress(cereAccount?.address); setEthAddress(ethAccount?.address); + setSolanaAddress(solanaAccount?.address); }); window.addEventListener('focus', () => { @@ -60,6 +62,7 @@ export const Wallet = () => { permissions: { personal_sign: {}, ed25519_signRaw: {}, + solana_signMessage: {}, }, }, @@ -219,6 +222,16 @@ export const Wallet = () => { console.log(`Signed message: ${signed}`); }, [wallet]); + const handleSolanaSign = useCallback(async () => { + const [, , solanaAccount] = await wallet.getAccounts(); + const signed = await wallet.provider.request({ + method: 'solana_signMessage', + params: [solanaAccount.address, 'Hello!!!'], + }); + + console.log(`Signed message: ${signed}`); + }, [wallet]); + const handleGetAccounts = useCallback(async () => { const accounts = await wallet.getAccounts(); @@ -264,6 +277,7 @@ export const Wallet = () => { const permissions = await wallet.requestPermissions({ personal_sign: {}, ed25519_signRaw: {}, + solana_signMessage: {}, }); console.log('Approved permissions', permissions); @@ -308,6 +322,17 @@ export const Wallet = () => { )} + {solanaAddress && ( + + + Solana Address + + + {solanaAddress} + + + )} + {ethBalance && ( @@ -374,6 +399,10 @@ export const Wallet = () => { Sign payload (ed25519) + + diff --git a/src/components/AddressDropdown/AddressDropdown.tsx b/src/components/AddressDropdown/AddressDropdown.tsx index eba25d30..b1743717 100644 --- a/src/components/AddressDropdown/AddressDropdown.tsx +++ b/src/components/AddressDropdown/AddressDropdown.tsx @@ -7,6 +7,18 @@ import { CoinIcon } from '../CoinIcon'; export type AddressDropdownProps = Pick; +const labelByType = { + ethereum: 'Polygon', + ed25519: 'Cere Network', + solana: 'Solana', +}; + +const iconByType = { + ethereum: 'matic', + ed25519: 'cere', + solana: 'solana', +}; + const AddressDropdown = (props: AddressDropdownProps) => { const store = useAccountStore(); const { selectedAccount, accounts } = store; @@ -19,8 +31,8 @@ const AddressDropdown = (props: AddressDropdownProps) => { () => accounts.map((account) => ({ address: account.address, - label: account.type === 'ethereum' ? 'Polygon' : 'Cere Network', - icon: account.type === 'ethereum' ? : , + label: labelByType[account.type], + icon: , })), [accounts], ); diff --git a/src/components/CoinIcon/coinIconsMap.ts b/src/components/CoinIcon/coinIconsMap.ts index f6ddd9a3..cb29fae6 100644 --- a/src/components/CoinIcon/coinIconsMap.ts +++ b/src/components/CoinIcon/coinIconsMap.ts @@ -1,4 +1,4 @@ -import { CereIcon, MaticIcon, UsdcIcon, EthIcon, UsdtIcon } from '@cere-wallet/ui'; +import { CereIcon, MaticIcon, UsdcIcon, EthIcon, UsdtIcon, SolanaIcon } from '@cere-wallet/ui'; import { ComponentType } from 'react'; export const coinIconsMap: Record = { @@ -7,4 +7,5 @@ export const coinIconsMap: Record = { matic: MaticIcon, usdc: UsdcIcon, usdt: UsdtIcon, + solana: SolanaIcon, }; diff --git a/src/components/Permissions/knownPermissions.ts b/src/components/Permissions/knownPermissions.ts index df16377a..aee3c3ae 100644 --- a/src/components/Permissions/knownPermissions.ts +++ b/src/components/Permissions/knownPermissions.ts @@ -16,6 +16,11 @@ export const knownPermissions: Record = { title: 'Sign message (Cere Network)', description: 'Allow the app to sign data on your behalf in background.', }, + + solana_signMessage: { + title: 'Sign message (Solana)', + description: 'Allow the app to sign data on your behalf in background.', + }, }; export const defaultDescription = 'No description available'; diff --git a/src/constants.ts b/src/constants.ts index dacc5099..6546f68e 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -42,4 +42,4 @@ export const RPC_POLLING_INTERVAL = 10000; export const AUTH_SESSION_TIMEOUT = 604800; export const AUTH_TOKEN_ISSUER = 'cere-wallet'; -export const ALLOWED_WALLET_PERMISSIONS = ['personal_sign', 'ed25519_signRaw'] as const; +export const ALLOWED_WALLET_PERMISSIONS = ['personal_sign', 'ed25519_signRaw', 'solana_signMessage'] as const; diff --git a/src/stores/AccountStore/AccountStore.ts b/src/stores/AccountStore/AccountStore.ts index 1af075f6..d4f187dd 100644 --- a/src/stores/AccountStore/AccountStore.ts +++ b/src/stores/AccountStore/AccountStore.ts @@ -95,8 +95,9 @@ export class AccountStore { get selectedAccount() { const selectedAddress = this.shared.state.selectedAddress; + const defaultAccount = this.accounts.at(0); - return this.accounts.find((account) => account.address === selectedAddress) || this.accounts.at(0); + return this.accounts.find((account) => account.address === selectedAddress) || defaultAccount; } selectAccount(address: string) { diff --git a/src/stores/ApprovalStore/PersonalSignHandler.ts b/src/stores/ApprovalStore/PersonalSignHandler.ts index 7a4ae62c..d5daa296 100644 --- a/src/stores/ApprovalStore/PersonalSignHandler.ts +++ b/src/stores/ApprovalStore/PersonalSignHandler.ts @@ -1,5 +1,5 @@ import { when } from 'mobx'; -import { PersonalSignRequest } from '@cere-wallet/wallet-engine'; +import { KeyType, PersonalSignRequest } from '@cere-wallet/wallet-engine'; import { PopupManagerStore } from '../PopupManagerStore'; import { NetworkStore } from '../NetworkStore'; @@ -15,12 +15,15 @@ export class PersonalSignHandler { async handle({ preopenInstanceId, params: [content, address, keyType] }: PersonalSignRequest) { const instanceId = preopenInstanceId || this.popupManagerStore.createModal(); - const network: ConfirmPopupState['network'] = - keyType === 'ed25519' ? { displayName: 'Cere Network', icon: 'cere' } : this.networkStore.network; + const staticNetworks: Record = { + ed25519: { displayName: 'Cere Network', icon: 'cere' }, + solana: { displayName: 'Solana', icon: 'solana' }, + ethereum: this.networkStore.network, + }; const popup = await this.popupManagerStore.proceedTo(instanceId, '/confirm', { - network, content, + network: staticNetworks[keyType], app: this.contextStore.app, status: 'pending', });