diff --git a/index.ts b/index.ts index cb32678..e18b711 100644 --- a/index.ts +++ b/index.ts @@ -3,4 +3,7 @@ import zanoWallet from "./src/zanoWallet"; import {useZanoWallet} from "./src/hooks"; export {useZanoWallet}; +import validateWallet from "./src/server"; +export {validateWallet}; + export default zanoWallet; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index ef79cea..fb3e49e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,15 +9,26 @@ "version": "2.3.0", "license": "ISC", "dependencies": { + "axios": "^1.7.2", "react": "^18.3.1", "uuid": "^10.0.0" }, "devDependencies": { + "@types/node": "^20.14.12", "@types/react": "^18.3.3", "@types/uuid": "^10.0.0", "typescript": "^5.5.4" } }, + "node_modules/@types/node": { + "version": "20.14.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.12.tgz", + "integrity": "sha512-r7wNXakLeSsGT0H1AU863vS2wa5wBOK4bWMjZz2wj+8nBx+m5PeIn0k8AloSLpRuiwdRQZwarZqHE4FNArPuJQ==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, "node_modules/@types/prop-types": { "version": "15.7.12", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", @@ -40,12 +51,78 @@ "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==", "dev": true }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/axios": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz", + "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", "dev": true }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -62,6 +139,30 @@ "loose-envify": "cli.js" } }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/react": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", @@ -86,6 +187,12 @@ "node": ">=14.17" } }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, "node_modules/uuid": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", diff --git a/package.json b/package.json index 35e6816..be2b7e9 100644 --- a/package.json +++ b/package.json @@ -21,11 +21,13 @@ "author": "", "license": "ISC", "devDependencies": { + "@types/node": "^20.14.12", "@types/react": "^18.3.3", "@types/uuid": "^10.0.0", "typescript": "^5.5.4" }, "dependencies": { + "axios": "^1.7.2", "react": "^18.3.1", "uuid": "^10.0.0" }, diff --git a/src/server.ts b/src/server.ts new file mode 100644 index 0000000..c5d4b3e --- /dev/null +++ b/src/server.ts @@ -0,0 +1,94 @@ +import axios from 'axios'; + +interface BaseAuthData { + address: string; + signature: string; + message: string; +} + +interface AliasAuth extends BaseAuthData { + alias: string; +} + +interface PkeyAuth extends BaseAuthData { + pkey: string; +} + +type AuthData = AliasAuth | PkeyAuth; + + +interface ValidationParams { + buff: string; + sig: string; + alias?: string; + pkey?: string; +} + + +async function validateWallet(rpcUrl: string, authData: AuthData) { + + async function fetchZanoApi(method: string, params: any) { + return await axios.post( + 'http://195.201.107.230:33340/json_rpc', + { + "id": 0, + "jsonrpc": "2.0", + "method": method, + "params": params, + } + ).then(res => res.data); + } + + const { message, address, signature } = authData; + + const alias = (authData as AliasAuth).alias || undefined; + const pkey = (authData as PkeyAuth).pkey || undefined; + + if (!message || (!alias && !pkey) || !signature) { + return false; + } + + const validationParams: ValidationParams = { + "buff": Buffer.from(message).toString("base64"), + "sig": signature + }; + + if (alias) { + validationParams['alias'] = alias; + } else { + validationParams['pkey'] = pkey; + } + + const response = await fetchZanoApi( + 'validate_signature', + validationParams + ); + + const valid = response?.result?.status === 'OK'; + + if (!valid) { + return false; + } + + if (alias) { + const aliasDetailsResponse = await fetchZanoApi( + 'get_alias_details', + { + "alias": alias, + } + ); + + const aliasDetails = aliasDetailsResponse?.result?.alias_details; + const aliasAddress = aliasDetails?.address; + + const addressValid = !!aliasAddress && aliasAddress === address; + + if (!addressValid) { + return false; + } + } + + return valid; +} + +export default validateWallet; \ No newline at end of file