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',
});