From e802b77c8f02023e6b799e122e70c58244467a41 Mon Sep 17 00:00:00 2001 From: Damjan Dimitrov Date: Fri, 1 Dec 2023 13:37:36 +0100 Subject: [PATCH 01/14] Add identity module, getWalletProfile method --- packages/sdk/src/docs-index.ts | 2 ++ packages/sdk/src/index.ts | 2 ++ packages/sdk/src/modules/identity/identity.ts | 33 +++++++++++++++++++ packages/sdk/src/types/identity.ts | 4 +++ 4 files changed, 41 insertions(+) create mode 100644 packages/sdk/src/modules/identity/identity.ts create mode 100644 packages/sdk/src/types/identity.ts diff --git a/packages/sdk/src/docs-index.ts b/packages/sdk/src/docs-index.ts index d0a2113..86d6f63 100644 --- a/packages/sdk/src/docs-index.ts +++ b/packages/sdk/src/docs-index.ts @@ -2,6 +2,7 @@ export * from './types/apillon'; export * from './types/nfts'; export * from './types/hosting'; export * from './types/storage'; +export * from './types/identity'; export * from './lib/apillon'; export * from './modules/storage/storage'; export * from './modules/storage/storage-bucket'; @@ -13,3 +14,4 @@ export * from './modules/hosting/deployment'; export * from './modules/hosting/hosting-website'; export * from './modules/nft/nft'; export * from './modules/nft/nft-collection'; +export * from './modules/identity/identity'; diff --git a/packages/sdk/src/index.ts b/packages/sdk/src/index.ts index 87307e0..fe1b1e0 100644 --- a/packages/sdk/src/index.ts +++ b/packages/sdk/src/index.ts @@ -2,8 +2,10 @@ export * from './types/apillon'; export * from './types/nfts'; export * from './types/hosting'; export * from './types/storage'; +export * from './types/identity'; export * from './lib/apillon'; export * from './lib/common'; export * from './modules/storage/storage'; export * from './modules/hosting/hosting'; export * from './modules/nft/nft'; +export * from './modules/identity/identity'; diff --git a/packages/sdk/src/modules/identity/identity.ts b/packages/sdk/src/modules/identity/identity.ts new file mode 100644 index 0000000..b6562c1 --- /dev/null +++ b/packages/sdk/src/modules/identity/identity.ts @@ -0,0 +1,33 @@ +import { ApillonModule } from '../../lib/apillon'; +import { ApillonApi } from '../../lib/apillon-api'; +import { IApillonResponse } from '../../types/apillon'; +import { WalletIdentityData } from '../../types/identity'; + +export class Identity extends ApillonModule { + /** + * Base API url for identity. + */ + private API_PREFIX = '/identity'; + + /** + * Get a wallet's online profile, including data from Subsocial, Polkadot Identity and Litentry + * @param {string} walletAddress - Wallet address to retreive data for + * @param {string} message - The message that has been signed by the wallet + * @param {string} signature - The wallet's signature, used for validation + * @returns Identity data fetched from Polkadot Identity and Subsocial + */ + public async getWalletProfile( + walletAddress: string, + message: string | Uint8Array, + signature: string | Uint8Array, + ): Promise { + const { data } = await ApillonApi.post< + IApillonResponse + >(`${this.API_PREFIX}/${walletAddress}`, { + message, + signature, + }); + + return data; + } +} diff --git a/packages/sdk/src/types/identity.ts b/packages/sdk/src/types/identity.ts new file mode 100644 index 0000000..bc2ec98 --- /dev/null +++ b/packages/sdk/src/types/identity.ts @@ -0,0 +1,4 @@ +export interface WalletIdentityData { + polkadot: object; + subsocial: object; +} From 08addf9963dd16cb60db72df75801844640d0eb1 Mon Sep 17 00:00:00 2001 From: Damjan Dimitrov Date: Wed, 6 Dec 2023 12:29:08 +0100 Subject: [PATCH 02/14] Implement EVM wallet signature verification --- package-lock.json | 913 +++++++++++++++++- packages/sdk/package.json | 11 +- packages/sdk/src/modules/identity/identity.ts | 55 ++ packages/sdk/src/tests/helpers/helper.ts | 4 + packages/sdk/src/tests/identity.test.ts | 40 + 5 files changed, 1004 insertions(+), 19 deletions(-) create mode 100644 packages/sdk/src/tests/identity.test.ts diff --git a/package-lock.json b/package-lock.json index 4f03c5d..23cd98e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,6 +25,12 @@ "npm": ">=8.4.0" } }, + "node_modules/@adraffy/ens-normalize": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.0.tgz", + "integrity": "sha512-nA9XHtlAkYfJxY7bce8DcN7eKxWWCWkU+1GR9d+U6MbNpfwQp8TI7vqOsBsMcHoT4mBu2kypKoSKnghEzOOq5Q==", + "dev": true + }, "node_modules/@ampproject/remapping": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", @@ -1404,6 +1410,30 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@noble/curves": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", + "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", + "dev": true, + "dependencies": { + "@noble/hashes": "1.3.2" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", + "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", + "dev": true, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1535,6 +1565,14 @@ "@babel/types": "^7.20.7" } }, + "node_modules/@types/bn.js": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.5.tgz", + "integrity": "sha512-V46N0zwKRF5Q00AZ6hWtN0T8gGmDUaUzLWQvHFo5yThtVwK/VCenFY3wXVbOvNfajEpsTfQM4IN9k/d6gUVX3A==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/graceful-fs": { "version": "4.1.7", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.7.tgz", @@ -1569,9 +1607,9 @@ } }, "node_modules/@types/jest": { - "version": "29.5.5", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.5.tgz", - "integrity": "sha512-ebylz2hnsWR9mYvmBFbXJXr+33UPc4+ZdxyDXh5w0FlPBTfCVN3wPL+kuOiQt3xvrK419v7XWeAs+AeOksafXg==", + "version": "29.5.11", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.11.tgz", + "integrity": "sha512-S2mHmYIVe13vrm6q4kN6fLYYAka15ALQki/vgDC3mIukEOx8WJlv0kQPM+d4w8Gp6u0uSdKND04IlTXBv0rwnQ==", "dev": true, "dependencies": { "expect": "^29.0.0", @@ -1584,10 +1622,25 @@ "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==" }, "node_modules/@types/node": { - "version": "18.15.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.3.tgz", - "integrity": "sha512-p6ua9zBxz5otCmbpb5D3U4B5Nanw6Pk3PPyX05xnxbB/fRv71N7CPmORg7uAD5P70T0xmx1pzAx/FUfa5X+3cw==", - "dev": true + "version": "18.15.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.13.tgz", + "integrity": "sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q==" + }, + "node_modules/@types/pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@types/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-uRwJqmiXmh9++aSu1VNEn3iIxWOhd8AHXNSdlaLfdAAdSTY9jYVeGWnzejM3dvrkbqE3/hyQkQQ29IFATEGlew==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/secp256k1": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@types/secp256k1/-/secp256k1-4.0.6.tgz", + "integrity": "sha512-hHxJU6PAEUn0TP4S/ZOzuTUvJWuZ6eIKeNKb5RBpODvSl6hp1Wrw4s7ATY50rklRCScUDpHzVA/DQdSjJ3UoYQ==", + "dependencies": { + "@types/node": "*" + } }, "node_modules/@types/semver": { "version": "7.3.13", @@ -1829,6 +1882,12 @@ "node": ">=0.4.0" } }, + "node_modules/aes-js": { + "version": "4.0.0-beta.5", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-4.0.0-beta.5.tgz", + "integrity": "sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==", + "dev": true + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -2067,6 +2126,14 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "node_modules/base-x": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz", + "integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -2076,6 +2143,16 @@ "node": ">=8" } }, + "node_modules/blakejs": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.2.1.tgz", + "integrity": "sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ==" + }, + "node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" + }, "node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", @@ -2096,6 +2173,24 @@ "node": ">=8" } }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==" + }, + "node_modules/browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dependencies": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, "node_modules/browserslist": { "version": "4.22.1", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz", @@ -2140,6 +2235,24 @@ "node": ">= 6" } }, + "node_modules/bs58": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", + "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", + "dependencies": { + "base-x": "^3.0.2" + } + }, + "node_modules/bs58check": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz", + "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==", + "dependencies": { + "bs58": "^4.0.0", + "create-hash": "^1.1.0", + "safe-buffer": "^5.1.2" + } + }, "node_modules/bser": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", @@ -2155,6 +2268,11 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, + "node_modules/buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==" + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -2270,6 +2388,15 @@ "node": ">=8" } }, + "node_modules/cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, "node_modules/cjs-module-lexer": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", @@ -2389,6 +2516,31 @@ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true }, + "node_modules/create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "node_modules/create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dependencies": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, "node_modules/create-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", @@ -2551,6 +2703,25 @@ "integrity": "sha512-W1+g9qs9hviII0HAwOdehGYkr+zt7KKdmCcJcjH0mYg6oL8+ioT3Skjmt7BLoAQqXhjf40AXd+HlR4oAWMlXjA==", "dev": true }, + "node_modules/elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/elliptic/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, "node_modules/emittery": { "version": "0.13.1", "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", @@ -2862,6 +3033,86 @@ "node": ">=0.10.0" } }, + "node_modules/ethereum-cryptography": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", + "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", + "dependencies": { + "@types/pbkdf2": "^3.0.0", + "@types/secp256k1": "^4.0.1", + "blakejs": "^1.1.0", + "browserify-aes": "^1.2.0", + "bs58check": "^2.1.2", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "hash.js": "^1.1.7", + "keccak": "^3.0.0", + "pbkdf2": "^3.0.17", + "randombytes": "^2.1.0", + "safe-buffer": "^5.1.2", + "scrypt-js": "^3.0.0", + "secp256k1": "^4.0.1", + "setimmediate": "^1.0.5" + } + }, + "node_modules/ethereumjs-util": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz", + "integrity": "sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg==", + "dependencies": { + "@types/bn.js": "^5.1.0", + "bn.js": "^5.1.2", + "create-hash": "^1.1.2", + "ethereum-cryptography": "^0.1.3", + "rlp": "^2.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/ethers": { + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.9.0.tgz", + "integrity": "sha512-pmfNyQzc2mseLe91FnT2vmNaTt8dDzhxZ/xItAV7uGsF4dI4ek2ufMu3rAkgQETL/TIs0GS5A+U05g9QyWnv3Q==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/ethers-io/" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@adraffy/ens-normalize": "1.10.0", + "@noble/curves": "1.2.0", + "@noble/hashes": "1.3.2", + "@types/node": "18.15.13", + "aes-js": "4.0.0-beta.5", + "tslib": "2.4.0", + "ws": "8.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/ethers/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "dev": true + }, + "node_modules/evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dependencies": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -3291,6 +3542,38 @@ "node": ">=8" } }, + "node_modules/hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -4325,6 +4608,20 @@ "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", "dev": true }, + "node_modules/keccak": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.4.tgz", + "integrity": "sha512-3vKuW0jV8J3XNTzvfyicFR5qvxrSAGl7KIhvgOu5cmWwM7tZRj3fMbj/pfIf4be7aznbc+prBWGjywox/g2Y6Q==", + "hasInstallScript": true, + "dependencies": { + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", @@ -4470,6 +4767,16 @@ "node": ">= 12" } }, + "node_modules/md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -4524,6 +4831,16 @@ "node": ">=6" } }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==" + }, "node_modules/minimatch": { "version": "7.4.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-7.4.2.tgz", @@ -4563,6 +4880,21 @@ "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==" }, + "node_modules/node-addon-api": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", + "integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==" + }, + "node_modules/node-gyp-build": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.7.1.tgz", + "integrity": "sha512-wTSrZ+8lsRRa3I3H8Xr65dLWSgCvY2l4AOnaeKdPA9TB/WYMPaTcrzf3rXvFoVvjKNVnu0CcWSx54qq9GKRUYg==", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -4868,6 +5200,21 @@ "node": ">=8" } }, + "node_modules/pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "dependencies": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + }, + "engines": { + "node": ">=0.12" + } + }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -5084,12 +5431,33 @@ } ] }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, "node_modules/react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", "dev": true }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -5201,6 +5569,26 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "node_modules/rlp": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/rlp/-/rlp-2.2.7.tgz", + "integrity": "sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ==", + "dependencies": { + "bn.js": "^5.2.0" + }, + "bin": { + "rlp": "bin/rlp" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -5223,6 +5611,25 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/safe-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-2.1.1.tgz", @@ -5231,6 +5638,25 @@ "regexp-tree": "~0.1.1" } }, + "node_modules/scrypt-js": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", + "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==" + }, + "node_modules/secp256k1": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.3.tgz", + "integrity": "sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA==", + "hasInstallScript": true, + "dependencies": { + "elliptic": "^6.5.4", + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/semver": { "version": "7.3.8", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", @@ -5256,6 +5682,23 @@ "node": ">=10" } }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==" + }, + "node_modules/sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -5380,6 +5823,14 @@ "node": ">=8" } }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", @@ -5988,6 +6439,11 @@ "punycode": "^2.1.0" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", @@ -6185,6 +6641,27 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, + "node_modules/ws": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz", + "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -6374,11 +6851,14 @@ "version": "1.1.0", "license": "MIT", "dependencies": { - "axios": "^1.3.4" + "axios": "^1.3.4", + "ethereumjs-util": "^7.1.5" }, "devDependencies": { + "@types/jest": "^29.5.11", "dotenv": "^16.0.3", "eslint-config-common": "*", + "ethers": "^6.9.0", "nodemon": "^2.0.20", "rimraf": "^5.0.5", "ts-node": "^10.9.1", @@ -6455,6 +6935,12 @@ } }, "dependencies": { + "@adraffy/ens-normalize": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.0.tgz", + "integrity": "sha512-nA9XHtlAkYfJxY7bce8DcN7eKxWWCWkU+1GR9d+U6MbNpfwQp8TI7vqOsBsMcHoT4mBu2kypKoSKnghEzOOq5Q==", + "dev": true + }, "@ampproject/remapping": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", @@ -6521,9 +7007,12 @@ "@apillon/sdk": { "version": "file:packages/sdk", "requires": { + "@types/jest": "^29.5.11", "axios": "^1.3.4", "dotenv": "^16.0.3", "eslint-config-common": "*", + "ethereumjs-util": "^7.1.5", + "ethers": "^6.9.0", "nodemon": "^2.0.20", "rimraf": "^5.0.5", "ts-node": "^10.9.1", @@ -7633,6 +8122,21 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "@noble/curves": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", + "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", + "dev": true, + "requires": { + "@noble/hashes": "1.3.2" + } + }, + "@noble/hashes": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", + "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", + "dev": true + }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -7752,6 +8256,14 @@ "@babel/types": "^7.20.7" } }, + "@types/bn.js": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.5.tgz", + "integrity": "sha512-V46N0zwKRF5Q00AZ6hWtN0T8gGmDUaUzLWQvHFo5yThtVwK/VCenFY3wXVbOvNfajEpsTfQM4IN9k/d6gUVX3A==", + "requires": { + "@types/node": "*" + } + }, "@types/graceful-fs": { "version": "4.1.7", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.7.tgz", @@ -7786,9 +8298,9 @@ } }, "@types/jest": { - "version": "29.5.5", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.5.tgz", - "integrity": "sha512-ebylz2hnsWR9mYvmBFbXJXr+33UPc4+ZdxyDXh5w0FlPBTfCVN3wPL+kuOiQt3xvrK419v7XWeAs+AeOksafXg==", + "version": "29.5.11", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.11.tgz", + "integrity": "sha512-S2mHmYIVe13vrm6q4kN6fLYYAka15ALQki/vgDC3mIukEOx8WJlv0kQPM+d4w8Gp6u0uSdKND04IlTXBv0rwnQ==", "dev": true, "requires": { "expect": "^29.0.0", @@ -7801,10 +8313,25 @@ "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==" }, "@types/node": { - "version": "18.15.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.3.tgz", - "integrity": "sha512-p6ua9zBxz5otCmbpb5D3U4B5Nanw6Pk3PPyX05xnxbB/fRv71N7CPmORg7uAD5P70T0xmx1pzAx/FUfa5X+3cw==", - "dev": true + "version": "18.15.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.13.tgz", + "integrity": "sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q==" + }, + "@types/pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@types/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-uRwJqmiXmh9++aSu1VNEn3iIxWOhd8AHXNSdlaLfdAAdSTY9jYVeGWnzejM3dvrkbqE3/hyQkQQ29IFATEGlew==", + "requires": { + "@types/node": "*" + } + }, + "@types/secp256k1": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@types/secp256k1/-/secp256k1-4.0.6.tgz", + "integrity": "sha512-hHxJU6PAEUn0TP4S/ZOzuTUvJWuZ6eIKeNKb5RBpODvSl6hp1Wrw4s7ATY50rklRCScUDpHzVA/DQdSjJ3UoYQ==", + "requires": { + "@types/node": "*" + } }, "@types/semver": { "version": "7.3.13", @@ -7946,6 +8473,12 @@ "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", "dev": true }, + "aes-js": { + "version": "4.0.0-beta.5", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-4.0.0-beta.5.tgz", + "integrity": "sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==", + "dev": true + }, "ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -8130,12 +8663,30 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "base-x": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz", + "integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, "binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "dev": true }, + "blakejs": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.2.1.tgz", + "integrity": "sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ==" + }, + "bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" + }, "brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", @@ -8153,6 +8704,24 @@ "fill-range": "^7.0.1" } }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==" + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, "browserslist": { "version": "4.22.1", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz", @@ -8174,6 +8743,24 @@ "fast-json-stable-stringify": "2.x" } }, + "bs58": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", + "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", + "requires": { + "base-x": "^3.0.2" + } + }, + "bs58check": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz", + "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==", + "requires": { + "bs58": "^4.0.0", + "create-hash": "^1.1.0", + "safe-buffer": "^5.1.2" + } + }, "bser": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", @@ -8189,6 +8776,11 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==" + }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -8254,6 +8846,15 @@ "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", "dev": true }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, "cjs-module-lexer": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", @@ -8350,6 +8951,31 @@ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, "create-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", @@ -8464,6 +9090,27 @@ "integrity": "sha512-W1+g9qs9hviII0HAwOdehGYkr+zt7KKdmCcJcjH0mYg6oL8+ioT3Skjmt7BLoAQqXhjf40AXd+HlR4oAWMlXjA==", "dev": true }, + "elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "requires": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, "emittery": { "version": "0.13.1", "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", @@ -8695,6 +9342,72 @@ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" }, + "ethereum-cryptography": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", + "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", + "requires": { + "@types/pbkdf2": "^3.0.0", + "@types/secp256k1": "^4.0.1", + "blakejs": "^1.1.0", + "browserify-aes": "^1.2.0", + "bs58check": "^2.1.2", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "hash.js": "^1.1.7", + "keccak": "^3.0.0", + "pbkdf2": "^3.0.17", + "randombytes": "^2.1.0", + "safe-buffer": "^5.1.2", + "scrypt-js": "^3.0.0", + "secp256k1": "^4.0.1", + "setimmediate": "^1.0.5" + } + }, + "ethereumjs-util": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz", + "integrity": "sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg==", + "requires": { + "@types/bn.js": "^5.1.0", + "bn.js": "^5.1.2", + "create-hash": "^1.1.2", + "ethereum-cryptography": "^0.1.3", + "rlp": "^2.2.4" + } + }, + "ethers": { + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.9.0.tgz", + "integrity": "sha512-pmfNyQzc2mseLe91FnT2vmNaTt8dDzhxZ/xItAV7uGsF4dI4ek2ufMu3rAkgQETL/TIs0GS5A+U05g9QyWnv3Q==", + "dev": true, + "requires": { + "@adraffy/ens-normalize": "1.10.0", + "@noble/curves": "1.2.0", + "@noble/hashes": "1.3.2", + "@types/node": "18.15.13", + "aes-js": "4.0.0-beta.5", + "tslib": "2.4.0", + "ws": "8.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "dev": true + } + } + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, "execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -9010,6 +9723,35 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" }, + "hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "requires": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + } + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, "html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -9791,6 +10533,16 @@ "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", "dev": true }, + "keccak": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.4.tgz", + "integrity": "sha512-3vKuW0jV8J3XNTzvfyicFR5qvxrSAGl7KIhvgOu5cmWwM7tZRj3fMbj/pfIf4be7aznbc+prBWGjywox/g2Y6Q==", + "requires": { + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0", + "readable-stream": "^3.6.0" + } + }, "kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", @@ -9899,6 +10651,16 @@ "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", "dev": true }, + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, "merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -9938,6 +10700,16 @@ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==" + }, "minimatch": { "version": "7.4.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-7.4.2.tgz", @@ -9968,6 +10740,16 @@ "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==" }, + "node-addon-api": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", + "integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==" + }, + "node-gyp-build": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.7.1.tgz", + "integrity": "sha512-wTSrZ+8lsRRa3I3H8Xr65dLWSgCvY2l4AOnaeKdPA9TB/WYMPaTcrzf3rXvFoVvjKNVnu0CcWSx54qq9GKRUYg==" + }, "node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -10189,6 +10971,18 @@ "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" }, + "pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, "picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -10328,12 +11122,30 @@ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "requires": { + "safe-buffer": "^5.1.0" + } + }, "react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", "dev": true }, + "readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, "readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -10407,6 +11219,23 @@ "glob": "^9.2.0" } }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "rlp": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/rlp/-/rlp-2.2.7.tgz", + "integrity": "sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ==", + "requires": { + "bn.js": "^5.2.0" + } + }, "run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -10415,6 +11244,11 @@ "queue-microtask": "^1.2.2" } }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, "safe-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-2.1.1.tgz", @@ -10423,6 +11257,21 @@ "regexp-tree": "~0.1.1" } }, + "scrypt-js": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", + "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==" + }, + "secp256k1": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.3.tgz", + "integrity": "sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA==", + "requires": { + "elliptic": "^6.5.4", + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0" + } + }, "semver": { "version": "7.3.8", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", @@ -10441,6 +11290,20 @@ } } }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==" + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -10539,6 +11402,14 @@ } } }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + } + }, "string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", @@ -10924,6 +11795,11 @@ "punycode": "^2.1.0" } }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, "v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", @@ -11080,6 +11956,13 @@ } } }, + "ws": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz", + "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==", + "dev": true, + "requires": {} + }, "y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 6359e66..2c25f26 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -44,15 +44,18 @@ "decentralized" ], "dependencies": { - "axios": "^1.3.4" + "axios": "^1.3.4", + "ethereumjs-util": "^7.1.5" }, "devDependencies": { + "@types/jest": "^29.5.11", + "dotenv": "^16.0.3", "eslint-config-common": "*", + "ethers": "^6.9.0", "nodemon": "^2.0.20", + "rimraf": "^5.0.5", "ts-node": "^10.9.1", "tsconfig": "*", - "rimraf": "^5.0.5", - "dotenv": "^16.0.3", "typedoc": "^0.25.1" }, "jest": { @@ -80,4 +83,4 @@ "preset": "ts-jest", "verbose": true } -} \ No newline at end of file +} diff --git a/packages/sdk/src/modules/identity/identity.ts b/packages/sdk/src/modules/identity/identity.ts index b6562c1..0b8aaea 100644 --- a/packages/sdk/src/modules/identity/identity.ts +++ b/packages/sdk/src/modules/identity/identity.ts @@ -2,6 +2,13 @@ import { ApillonModule } from '../../lib/apillon'; import { ApillonApi } from '../../lib/apillon-api'; import { IApillonResponse } from '../../types/apillon'; import { WalletIdentityData } from '../../types/identity'; +import { + bufferToHex, + ecrecover, + fromRpcSig, + keccak256, + publicToAddress, +} from 'ethereumjs-util'; export class Identity extends ApillonModule { /** @@ -9,6 +16,15 @@ export class Identity extends ApillonModule { */ private API_PREFIX = '/identity'; + /** + * Generate a message presented to the user when requested to sign using their wallet + * @param {string} [customText='Please sign this message'] + * @returns {string} + */ + public generateSigningMessage(customText = 'Please sign this message') { + return `${customText}\n${new Date().getTime()}`; + } + /** * Get a wallet's online profile, including data from Subsocial, Polkadot Identity and Litentry * @param {string} walletAddress - Wallet address to retreive data for @@ -30,4 +46,43 @@ export class Identity extends ApillonModule { return data; } + + /** + * Check if a signed message from an EVM wallet address is valid + * @param {string} walletAddress - Wallet address which signed the message + * @param {string} message - The message that has been signed by the wallet + * @param {string} signature - The wallet's signature, used for validation + * @returns {{isValid: boolean; address: string;}} + */ + public validateEvmWalletSignature( + walletAddress: string, + message: string | Uint8Array, + signature: string, + ): { isValid: boolean; address: string } { + // Prefix the message and hash it using Keccak-256 + const prefixedMessage = keccak256( + Buffer.from( + `\x19Ethereum Signed Message:\n${message.length}${message}`, + 'utf-8', + ), + ); + // Split the signature into its components + const signatureParams = fromRpcSig(signature); + + // Recover the public key + const publicKey = ecrecover( + prefixedMessage, + signatureParams.v, + signatureParams.r, + signatureParams.s, + ); + + // Recover the address from the signature and public key + const address = bufferToHex(publicToAddress(publicKey)).toLowerCase(); + + return { + isValid: address.toLowerCase() === walletAddress.toLowerCase(), + address, + }; + } } diff --git a/packages/sdk/src/tests/helpers/helper.ts b/packages/sdk/src/tests/helpers/helper.ts index 087a7a2..d335df3 100644 --- a/packages/sdk/src/tests/helpers/helper.ts +++ b/packages/sdk/src/tests/helpers/helper.ts @@ -30,3 +30,7 @@ export function getWebsiteUUID() { export function getMintAddress() { return process.env['MINT_ADDRESS']; } + +export function getWalletPrivateKey() { + return process.env['WALLET_PRIVATE_KEY']; +} diff --git a/packages/sdk/src/tests/identity.test.ts b/packages/sdk/src/tests/identity.test.ts new file mode 100644 index 0000000..329653e --- /dev/null +++ b/packages/sdk/src/tests/identity.test.ts @@ -0,0 +1,40 @@ +import { ApillonConfig } from '../lib/apillon'; +import { Identity } from '../modules/identity/identity'; +import { getConfig, getWalletPrivateKey } from './helpers/helper'; +import { Wallet } from 'ethers'; + +describe('IPNS tests for StorageBucket', () => { + let config: ApillonConfig; + + beforeAll(async () => { + config = getConfig(); + }); + + test('Validate EVM wallet signature', async () => { + const identity = new Identity(config); + + const customMessage = 'Identity SDK test'; + const message = identity.generateSigningMessage(customMessage); + const [firstPart, secondPart] = message.split('\n'); + // Validate that custom signing message was generated correctly + expect(firstPart).toEqual(customMessage); + expect(+secondPart).toBeLessThan(new Date().getTime()); + + // Create a wallet from your private key + const wallet = new Wallet(getWalletPrivateKey()); + + const signature = await wallet.signMessage(message); + + console.log('Message:', message); + console.log('Signature:', signature); + + const res = identity.validateEvmWalletSignature( + wallet.address, + message, + signature, + ); + + expect(res.isValid).toBeTruthy(); + expect(res.address.toLowerCase()).toEqual(wallet.address.toLowerCase()); + }); +}); From 401bf4a7c88a28175bd5bbf85c970039d2891c98 Mon Sep 17 00:00:00 2001 From: Damjan Dimitrov Date: Wed, 6 Dec 2023 12:59:22 +0100 Subject: [PATCH 03/14] Wallet identity updates --- packages/sdk/src/modules/identity/identity.ts | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/packages/sdk/src/modules/identity/identity.ts b/packages/sdk/src/modules/identity/identity.ts index 0b8aaea..280d6c0 100644 --- a/packages/sdk/src/modules/identity/identity.ts +++ b/packages/sdk/src/modules/identity/identity.ts @@ -14,7 +14,7 @@ export class Identity extends ApillonModule { /** * Base API url for identity. */ - private API_PREFIX = '/identity'; + private API_PREFIX = '/wallet-identity'; /** * Generate a message presented to the user when requested to sign using their wallet @@ -26,23 +26,16 @@ export class Identity extends ApillonModule { } /** - * Get a wallet's online profile, including data from Subsocial, Polkadot Identity and Litentry + * Get a wallet's online identity profile, including data from Subsocial and Polkadot Identity * @param {string} walletAddress - Wallet address to retreive data for - * @param {string} message - The message that has been signed by the wallet - * @param {string} signature - The wallet's signature, used for validation * @returns Identity data fetched from Polkadot Identity and Subsocial */ - public async getWalletProfile( + public async getWalletIdentity( walletAddress: string, - message: string | Uint8Array, - signature: string | Uint8Array, ): Promise { - const { data } = await ApillonApi.post< - IApillonResponse - >(`${this.API_PREFIX}/${walletAddress}`, { - message, - signature, - }); + const { data } = await ApillonApi.get>( + `${this.API_PREFIX}?address=${walletAddress}`, + ); return data; } From 39456caf90f9018f0344a5de59c2384eefed1894 Mon Sep 17 00:00:00 2001 From: Damjan Dimitrov Date: Wed, 6 Dec 2023 13:21:11 +0100 Subject: [PATCH 04/14] Add identity profile tests --- packages/sdk/src/tests/identity.test.ts | 16 ++++++++++++++++ packages/sdk/src/types/identity.ts | 10 ++++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/packages/sdk/src/tests/identity.test.ts b/packages/sdk/src/tests/identity.test.ts index 329653e..b9f2e95 100644 --- a/packages/sdk/src/tests/identity.test.ts +++ b/packages/sdk/src/tests/identity.test.ts @@ -37,4 +37,20 @@ describe('IPNS tests for StorageBucket', () => { expect(res.isValid).toBeTruthy(); expect(res.address.toLowerCase()).toEqual(wallet.address.toLowerCase()); }); + + test('Get wallet identity profile', async () => { + const identity = new Identity(config); + + const { subsocial } = await identity.getWalletIdentity( + '3rJriA6MiYj7oFXv5hgxvSuacenm8fk76Kb5TEEHcWWQVvii', + ); + expect(subsocial.content.name).toBe('dev only'); + expect(subsocial.content.interests).toContain('crypto'); + + const { polkadot } = await identity.getWalletIdentity( + '5HqHQDGcHqSQELAyr5PbJNAcQJew4vsoNCf5kkSpXcUGMtCK', + ); + expect(polkadot.display.Raw).toBe('Web 3.0 Technologies Foundation'); + expect(polkadot.web.Raw).toBe('https://web3.foundation/'); + }); }); diff --git a/packages/sdk/src/types/identity.ts b/packages/sdk/src/types/identity.ts index bc2ec98..3f62929 100644 --- a/packages/sdk/src/types/identity.ts +++ b/packages/sdk/src/types/identity.ts @@ -1,4 +1,10 @@ export interface WalletIdentityData { - polkadot: object; - subsocial: object; + /** + * [Polkadot Identity Info DTO](https://github.com/polkadot-js/api/blob/c73c26d13324a6211a7cf4e401aa032c87f7aa10/packages/types-augment/src/lookup/types-substrate.ts#L3331) + */ + polkadot: any; + /** + * [Subsocial SpaceData DTO](https://docs.subsocial.network/js-docs/js-sdk/modules/dto.html#spacedata) + */ + subsocial: any; } From 4f9a2030d25749a080b8481efb6ced3689afceaf Mon Sep 17 00:00:00 2001 From: Damjan Dimitrov Date: Thu, 7 Dec 2023 09:44:36 +0100 Subject: [PATCH 05/14] Implement polkadot signature verify --- package-lock.json | 557 +++++++++++++++++- packages/sdk/package.json | 1 + packages/sdk/src/modules/identity/identity.ts | 21 + packages/sdk/src/tests/identity.test.ts | 14 + 4 files changed, 588 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 23cd98e..e5974b7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1414,7 +1414,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", - "dev": true, "dependencies": { "@noble/hashes": "1.3.2" }, @@ -1426,7 +1425,6 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", - "dev": true, "engines": { "node": ">= 16" }, @@ -1476,6 +1474,298 @@ "node": ">=14" } }, + "node_modules/@polkadot/networks": { + "version": "12.6.1", + "resolved": "https://registry.npmjs.org/@polkadot/networks/-/networks-12.6.1.tgz", + "integrity": "sha512-pzyirxTYAnsx+6kyLYcUk26e4TLz3cX6p2KhTgAVW77YnpGX5VTKTbYykyXC8fXFd/migeQsLaa2raFN47mwoA==", + "dependencies": { + "@polkadot/util": "12.6.1", + "@substrate/ss58-registry": "^1.44.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/networks/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@polkadot/util": { + "version": "12.6.1", + "resolved": "https://registry.npmjs.org/@polkadot/util/-/util-12.6.1.tgz", + "integrity": "sha512-10ra3VfXtK8ZSnWI7zjhvRrhupg3rd4iFC3zCaXmRpOU+AmfIoCFVEmuUuC66gyXiz2/g6k5E6j0lWQCOProSQ==", + "dependencies": { + "@polkadot/x-bigint": "12.6.1", + "@polkadot/x-global": "12.6.1", + "@polkadot/x-textdecoder": "12.6.1", + "@polkadot/x-textencoder": "12.6.1", + "@types/bn.js": "^5.1.5", + "bn.js": "^5.2.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/util-crypto": { + "version": "12.6.1", + "resolved": "https://registry.npmjs.org/@polkadot/util-crypto/-/util-crypto-12.6.1.tgz", + "integrity": "sha512-2ezWFLmdgeDXqB9NAUdgpp3s2rQztNrZLY+y0SJYNOG4ch+PyodTW/qSksnOrVGVdRhZ5OESRE9xvo9LYV5UAw==", + "dependencies": { + "@noble/curves": "^1.2.0", + "@noble/hashes": "^1.3.2", + "@polkadot/networks": "12.6.1", + "@polkadot/util": "12.6.1", + "@polkadot/wasm-crypto": "^7.3.1", + "@polkadot/wasm-util": "^7.3.1", + "@polkadot/x-bigint": "12.6.1", + "@polkadot/x-randomvalues": "12.6.1", + "@scure/base": "^1.1.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "12.6.1" + } + }, + "node_modules/@polkadot/util-crypto/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@polkadot/util/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@polkadot/wasm-bridge": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-bridge/-/wasm-bridge-7.3.2.tgz", + "integrity": "sha512-AJEXChcf/nKXd5Q/YLEV5dXQMle3UNT7jcXYmIffZAo/KI394a+/24PaISyQjoNC0fkzS1Q8T5pnGGHmXiVz2g==", + "dependencies": { + "@polkadot/wasm-util": "7.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "*", + "@polkadot/x-randomvalues": "*" + } + }, + "node_modules/@polkadot/wasm-bridge/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@polkadot/wasm-crypto": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto/-/wasm-crypto-7.3.2.tgz", + "integrity": "sha512-+neIDLSJ6jjVXsjyZ5oLSv16oIpwp+PxFqTUaZdZDoA2EyFRQB8pP7+qLsMNk+WJuhuJ4qXil/7XiOnZYZ+wxw==", + "dependencies": { + "@polkadot/wasm-bridge": "7.3.2", + "@polkadot/wasm-crypto-asmjs": "7.3.2", + "@polkadot/wasm-crypto-init": "7.3.2", + "@polkadot/wasm-crypto-wasm": "7.3.2", + "@polkadot/wasm-util": "7.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "*", + "@polkadot/x-randomvalues": "*" + } + }, + "node_modules/@polkadot/wasm-crypto-asmjs": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-asmjs/-/wasm-crypto-asmjs-7.3.2.tgz", + "integrity": "sha512-QP5eiUqUFur/2UoF2KKKYJcesc71fXhQFLT3D4ZjG28Mfk2ZPI0QNRUfpcxVQmIUpV5USHg4geCBNuCYsMm20Q==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "*" + } + }, + "node_modules/@polkadot/wasm-crypto-asmjs/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@polkadot/wasm-crypto-init": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-init/-/wasm-crypto-init-7.3.2.tgz", + "integrity": "sha512-FPq73zGmvZtnuJaFV44brze3Lkrki3b4PebxCy9Fplw8nTmisKo9Xxtfew08r0njyYh+uiJRAxPCXadkC9sc8g==", + "dependencies": { + "@polkadot/wasm-bridge": "7.3.2", + "@polkadot/wasm-crypto-asmjs": "7.3.2", + "@polkadot/wasm-crypto-wasm": "7.3.2", + "@polkadot/wasm-util": "7.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "*", + "@polkadot/x-randomvalues": "*" + } + }, + "node_modules/@polkadot/wasm-crypto-init/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@polkadot/wasm-crypto-wasm": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-wasm/-/wasm-crypto-wasm-7.3.2.tgz", + "integrity": "sha512-15wd0EMv9IXs5Abp1ZKpKKAVyZPhATIAHfKsyoWCEFDLSOA0/K0QGOxzrAlsrdUkiKZOq7uzSIgIDgW8okx2Mw==", + "dependencies": { + "@polkadot/wasm-util": "7.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "*" + } + }, + "node_modules/@polkadot/wasm-crypto-wasm/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@polkadot/wasm-crypto/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@polkadot/wasm-util": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-util/-/wasm-util-7.3.2.tgz", + "integrity": "sha512-bmD+Dxo1lTZyZNxbyPE380wd82QsX+43mgCm40boyKrRppXEyQmWT98v/Poc7chLuskYb6X8IQ6lvvK2bGR4Tg==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "*" + } + }, + "node_modules/@polkadot/wasm-util/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@polkadot/x-bigint": { + "version": "12.6.1", + "resolved": "https://registry.npmjs.org/@polkadot/x-bigint/-/x-bigint-12.6.1.tgz", + "integrity": "sha512-YlABeVIlgYQZJ4ZpW/+akFGGxw5jMGt4g5vaP7EumlORGneJHzzWJYDmI5v2y7j1zvC9ofOle7z4tRmtN/QDew==", + "dependencies": { + "@polkadot/x-global": "12.6.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/x-bigint/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@polkadot/x-global": { + "version": "12.6.1", + "resolved": "https://registry.npmjs.org/@polkadot/x-global/-/x-global-12.6.1.tgz", + "integrity": "sha512-w5t19HIdBPuyu7X/AiCyH2DsKqxBF0KpF4Ymolnx8PfcSIgnq9ZOmgs74McPR6FgEmeEkr9uNKujZrsfURi1ug==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/x-global/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@polkadot/x-randomvalues": { + "version": "12.6.1", + "resolved": "https://registry.npmjs.org/@polkadot/x-randomvalues/-/x-randomvalues-12.6.1.tgz", + "integrity": "sha512-1uVKlfYYbgIgGV5v1Dgn960cGovenWm5pmg+aTMeUGXVYiJwRD2zOpLyC1i/tP454iA74j74pmWb8Nkn0tJZUQ==", + "dependencies": { + "@polkadot/x-global": "12.6.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "12.6.1", + "@polkadot/wasm-util": "*" + } + }, + "node_modules/@polkadot/x-randomvalues/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@polkadot/x-textdecoder": { + "version": "12.6.1", + "resolved": "https://registry.npmjs.org/@polkadot/x-textdecoder/-/x-textdecoder-12.6.1.tgz", + "integrity": "sha512-IasodJeV1f2Nr/VtA207+LXCQEqYcG8y9qB/EQcRsrEP58NbwwxM5Z2obV0lSjJOxRTJ4/OlhUwnLHwcbIp6+g==", + "dependencies": { + "@polkadot/x-global": "12.6.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/x-textdecoder/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@polkadot/x-textencoder": { + "version": "12.6.1", + "resolved": "https://registry.npmjs.org/@polkadot/x-textencoder/-/x-textencoder-12.6.1.tgz", + "integrity": "sha512-sTq/+tXqBhGe01a1rjieSHFh3y935vuRgtahVgVJZnfqh5SmLPgSN5tTPxZWzyx7gHIfotle8laTJbJarv7V1A==", + "dependencies": { + "@polkadot/x-global": "12.6.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/x-textencoder/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@scure/base": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.3.tgz", + "integrity": "sha512-/+SgoRjLq7Xlf0CWuLHq2LUZeL/w65kfzAPG5NH9pcmBhs+nunQTn4gvdwgMTIXnt9b2C/1SeL2XiysZEyIC9Q==", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -1500,6 +1790,11 @@ "@sinonjs/commons": "^3.0.0" } }, + "node_modules/@substrate/ss58-registry": { + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/@substrate/ss58-registry/-/ss58-registry-1.44.0.tgz", + "integrity": "sha512-7lQ/7mMCzVNSEfDS4BCqnRnKCFKpcOaPrxMeGTXHX1YQzM/m2BBHjbK2C3dJvjv7GYxMiaTq/HdWQj1xS6ss+A==" + }, "node_modules/@tsconfig/node10": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", @@ -6851,6 +7146,7 @@ "version": "1.1.0", "license": "MIT", "dependencies": { + "@polkadot/util-crypto": "^12.6.1", "axios": "^1.3.4", "ethereumjs-util": "^7.1.5" }, @@ -7007,6 +7303,7 @@ "@apillon/sdk": { "version": "file:packages/sdk", "requires": { + "@polkadot/util-crypto": "*", "@types/jest": "^29.5.11", "axios": "^1.3.4", "dotenv": "^16.0.3", @@ -8126,7 +8423,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", - "dev": true, "requires": { "@noble/hashes": "1.3.2" } @@ -8134,8 +8430,7 @@ "@noble/hashes": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", - "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", - "dev": true + "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==" }, "@nodelib/fs.scandir": { "version": "2.1.5", @@ -8167,6 +8462,253 @@ "dev": true, "optional": true }, + "@polkadot/networks": { + "version": "12.6.1", + "resolved": "https://registry.npmjs.org/@polkadot/networks/-/networks-12.6.1.tgz", + "integrity": "sha512-pzyirxTYAnsx+6kyLYcUk26e4TLz3cX6p2KhTgAVW77YnpGX5VTKTbYykyXC8fXFd/migeQsLaa2raFN47mwoA==", + "requires": { + "@polkadot/util": "12.6.1", + "@substrate/ss58-registry": "^1.44.0", + "tslib": "^2.6.2" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } + }, + "@polkadot/util": { + "version": "12.6.1", + "resolved": "https://registry.npmjs.org/@polkadot/util/-/util-12.6.1.tgz", + "integrity": "sha512-10ra3VfXtK8ZSnWI7zjhvRrhupg3rd4iFC3zCaXmRpOU+AmfIoCFVEmuUuC66gyXiz2/g6k5E6j0lWQCOProSQ==", + "requires": { + "@polkadot/x-bigint": "12.6.1", + "@polkadot/x-global": "12.6.1", + "@polkadot/x-textdecoder": "12.6.1", + "@polkadot/x-textencoder": "12.6.1", + "@types/bn.js": "^5.1.5", + "bn.js": "^5.2.1", + "tslib": "^2.6.2" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } + }, + "@polkadot/util-crypto": { + "version": "12.6.1", + "resolved": "https://registry.npmjs.org/@polkadot/util-crypto/-/util-crypto-12.6.1.tgz", + "integrity": "sha512-2ezWFLmdgeDXqB9NAUdgpp3s2rQztNrZLY+y0SJYNOG4ch+PyodTW/qSksnOrVGVdRhZ5OESRE9xvo9LYV5UAw==", + "requires": { + "@noble/curves": "^1.2.0", + "@noble/hashes": "^1.3.2", + "@polkadot/networks": "12.6.1", + "@polkadot/util": "12.6.1", + "@polkadot/wasm-crypto": "^7.3.1", + "@polkadot/wasm-util": "^7.3.1", + "@polkadot/x-bigint": "12.6.1", + "@polkadot/x-randomvalues": "12.6.1", + "@scure/base": "^1.1.3", + "tslib": "^2.6.2" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } + }, + "@polkadot/wasm-bridge": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-bridge/-/wasm-bridge-7.3.2.tgz", + "integrity": "sha512-AJEXChcf/nKXd5Q/YLEV5dXQMle3UNT7jcXYmIffZAo/KI394a+/24PaISyQjoNC0fkzS1Q8T5pnGGHmXiVz2g==", + "requires": { + "@polkadot/wasm-util": "7.3.2", + "tslib": "^2.6.2" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } + }, + "@polkadot/wasm-crypto": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto/-/wasm-crypto-7.3.2.tgz", + "integrity": "sha512-+neIDLSJ6jjVXsjyZ5oLSv16oIpwp+PxFqTUaZdZDoA2EyFRQB8pP7+qLsMNk+WJuhuJ4qXil/7XiOnZYZ+wxw==", + "requires": { + "@polkadot/wasm-bridge": "7.3.2", + "@polkadot/wasm-crypto-asmjs": "7.3.2", + "@polkadot/wasm-crypto-init": "7.3.2", + "@polkadot/wasm-crypto-wasm": "7.3.2", + "@polkadot/wasm-util": "7.3.2", + "tslib": "^2.6.2" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } + }, + "@polkadot/wasm-crypto-asmjs": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-asmjs/-/wasm-crypto-asmjs-7.3.2.tgz", + "integrity": "sha512-QP5eiUqUFur/2UoF2KKKYJcesc71fXhQFLT3D4ZjG28Mfk2ZPI0QNRUfpcxVQmIUpV5USHg4geCBNuCYsMm20Q==", + "requires": { + "tslib": "^2.6.2" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } + }, + "@polkadot/wasm-crypto-init": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-init/-/wasm-crypto-init-7.3.2.tgz", + "integrity": "sha512-FPq73zGmvZtnuJaFV44brze3Lkrki3b4PebxCy9Fplw8nTmisKo9Xxtfew08r0njyYh+uiJRAxPCXadkC9sc8g==", + "requires": { + "@polkadot/wasm-bridge": "7.3.2", + "@polkadot/wasm-crypto-asmjs": "7.3.2", + "@polkadot/wasm-crypto-wasm": "7.3.2", + "@polkadot/wasm-util": "7.3.2", + "tslib": "^2.6.2" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } + }, + "@polkadot/wasm-crypto-wasm": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-wasm/-/wasm-crypto-wasm-7.3.2.tgz", + "integrity": "sha512-15wd0EMv9IXs5Abp1ZKpKKAVyZPhATIAHfKsyoWCEFDLSOA0/K0QGOxzrAlsrdUkiKZOq7uzSIgIDgW8okx2Mw==", + "requires": { + "@polkadot/wasm-util": "7.3.2", + "tslib": "^2.6.2" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } + }, + "@polkadot/wasm-util": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-util/-/wasm-util-7.3.2.tgz", + "integrity": "sha512-bmD+Dxo1lTZyZNxbyPE380wd82QsX+43mgCm40boyKrRppXEyQmWT98v/Poc7chLuskYb6X8IQ6lvvK2bGR4Tg==", + "requires": { + "tslib": "^2.6.2" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } + }, + "@polkadot/x-bigint": { + "version": "12.6.1", + "resolved": "https://registry.npmjs.org/@polkadot/x-bigint/-/x-bigint-12.6.1.tgz", + "integrity": "sha512-YlABeVIlgYQZJ4ZpW/+akFGGxw5jMGt4g5vaP7EumlORGneJHzzWJYDmI5v2y7j1zvC9ofOle7z4tRmtN/QDew==", + "requires": { + "@polkadot/x-global": "12.6.1", + "tslib": "^2.6.2" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } + }, + "@polkadot/x-global": { + "version": "12.6.1", + "resolved": "https://registry.npmjs.org/@polkadot/x-global/-/x-global-12.6.1.tgz", + "integrity": "sha512-w5t19HIdBPuyu7X/AiCyH2DsKqxBF0KpF4Ymolnx8PfcSIgnq9ZOmgs74McPR6FgEmeEkr9uNKujZrsfURi1ug==", + "requires": { + "tslib": "^2.6.2" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } + }, + "@polkadot/x-randomvalues": { + "version": "12.6.1", + "resolved": "https://registry.npmjs.org/@polkadot/x-randomvalues/-/x-randomvalues-12.6.1.tgz", + "integrity": "sha512-1uVKlfYYbgIgGV5v1Dgn960cGovenWm5pmg+aTMeUGXVYiJwRD2zOpLyC1i/tP454iA74j74pmWb8Nkn0tJZUQ==", + "requires": { + "@polkadot/x-global": "12.6.1", + "tslib": "^2.6.2" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } + }, + "@polkadot/x-textdecoder": { + "version": "12.6.1", + "resolved": "https://registry.npmjs.org/@polkadot/x-textdecoder/-/x-textdecoder-12.6.1.tgz", + "integrity": "sha512-IasodJeV1f2Nr/VtA207+LXCQEqYcG8y9qB/EQcRsrEP58NbwwxM5Z2obV0lSjJOxRTJ4/OlhUwnLHwcbIp6+g==", + "requires": { + "@polkadot/x-global": "12.6.1", + "tslib": "^2.6.2" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } + }, + "@polkadot/x-textencoder": { + "version": "12.6.1", + "resolved": "https://registry.npmjs.org/@polkadot/x-textencoder/-/x-textencoder-12.6.1.tgz", + "integrity": "sha512-sTq/+tXqBhGe01a1rjieSHFh3y935vuRgtahVgVJZnfqh5SmLPgSN5tTPxZWzyx7gHIfotle8laTJbJarv7V1A==", + "requires": { + "@polkadot/x-global": "12.6.1", + "tslib": "^2.6.2" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } + }, + "@scure/base": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.3.tgz", + "integrity": "sha512-/+SgoRjLq7Xlf0CWuLHq2LUZeL/w65kfzAPG5NH9pcmBhs+nunQTn4gvdwgMTIXnt9b2C/1SeL2XiysZEyIC9Q==" + }, "@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -8191,6 +8733,11 @@ "@sinonjs/commons": "^3.0.0" } }, + "@substrate/ss58-registry": { + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/@substrate/ss58-registry/-/ss58-registry-1.44.0.tgz", + "integrity": "sha512-7lQ/7mMCzVNSEfDS4BCqnRnKCFKpcOaPrxMeGTXHX1YQzM/m2BBHjbK2C3dJvjv7GYxMiaTq/HdWQj1xS6ss+A==" + }, "@tsconfig/node10": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 2c25f26..79443dd 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -44,6 +44,7 @@ "decentralized" ], "dependencies": { + "@polkadot/util-crypto": "^12.6.1", "axios": "^1.3.4", "ethereumjs-util": "^7.1.5" }, diff --git a/packages/sdk/src/modules/identity/identity.ts b/packages/sdk/src/modules/identity/identity.ts index 280d6c0..d674352 100644 --- a/packages/sdk/src/modules/identity/identity.ts +++ b/packages/sdk/src/modules/identity/identity.ts @@ -9,6 +9,7 @@ import { keccak256, publicToAddress, } from 'ethereumjs-util'; +import { signatureVerify, encodeAddress } from '@polkadot/util-crypto'; export class Identity extends ApillonModule { /** @@ -78,4 +79,24 @@ export class Identity extends ApillonModule { address, }; } + + /** + * Check if a signed message from a Polkadot wallet address is valid + * @param {string} walletAddress - Wallet address which signed the message + * @param {string} message - The message that has been signed by the wallet + * @param {string} signature - The wallet's signature, used for validation + * @returns {{isValid: boolean; address: string;}} + */ + public validatePolkadotWalletSignature( + walletAddress: string, + message: string | Uint8Array, + signature: string, + ): { isValid: boolean; address: string } { + const { isValid, publicKey } = signatureVerify( + message, + signature, + walletAddress, + ); + return { isValid, address: encodeAddress(publicKey) }; + } } diff --git a/packages/sdk/src/tests/identity.test.ts b/packages/sdk/src/tests/identity.test.ts index b9f2e95..2b1fab5 100644 --- a/packages/sdk/src/tests/identity.test.ts +++ b/packages/sdk/src/tests/identity.test.ts @@ -53,4 +53,18 @@ describe('IPNS tests for StorageBucket', () => { expect(polkadot.display.Raw).toBe('Web 3.0 Technologies Foundation'); expect(polkadot.web.Raw).toBe('https://web3.foundation/'); }); + + test('Validate Polkadot wallet signature', async () => { + const identity = new Identity(config); + const wallet = '5FNpf1ARUkrjBwrTncyCbPndg915YfaAnzToMkEcddiXWLyd'; + const res = identity.validatePolkadotWalletSignature( + wallet, + 'Please sign this message.', + // Fill the below value with you own signature + // you can obtain a sample with wallet login at https://app.apillon.io/login + '', + ); + expect(res.isValid).toBeTruthy(); + expect(res.address.toLowerCase()).toEqual(wallet.toLowerCase()); + }); }); From cd9917726d23421fd094ce459f43d80c33f1a24c Mon Sep 17 00:00:00 2001 From: Damjan Dimitrov Date: Fri, 8 Dec 2023 11:25:21 +0100 Subject: [PATCH 06/14] Add Identity README section --- packages/sdk/README.md | 40 +++++++++++++++++++ packages/sdk/src/modules/identity/identity.ts | 2 +- packages/sdk/src/tests/identity.test.ts | 2 +- 3 files changed, 42 insertions(+), 2 deletions(-) diff --git a/packages/sdk/README.md b/packages/sdk/README.md index 3a5191a..62147db 100644 --- a/packages/sdk/README.md +++ b/packages/sdk/README.md @@ -290,3 +290,43 @@ await collection.transferOwnership( '0x5BA8B0c24bA5307b67E619ad500a635204F73bF1' ); ``` + + +## Identity + +Identity module encapsulates functionalities for validating EVM and Polkadot wallet signatures, as well as fetching Polkadot Identity data for any wallet. + +For detailed hosting SDK method, class and property documentation visit [SDK identity docs](https://sdk-docs.apillon.io/classes/Identity.html). + +### Usage example + +```ts +const identity = new Identity({ + key: 'yourApiKey', + secret: 'yourApiSecret', + logLevel: LogLevel.NONE, +}); + +// generate a custom message to be signed by the user's wallet +const message = await identity.generateSigningMessage( + 'Custom display message here', +); + +// validate an EVM wallet's signature for a given message +const { isValid } = await identity.validateEvmWalletSignature( + walletAddress, + signature, + message, +); + +// validate a Polkadot wallet's signature for a given message +const { isValid, address } = await identity.validatePolkadotWalletSignature( + walletAddress, + signature, + message, +); + +// obtain on-chain identity data for a Polkadot wallet +const { polkadot, subsocial } = await identity.getWalletIdentity(address); +``` + diff --git a/packages/sdk/src/modules/identity/identity.ts b/packages/sdk/src/modules/identity/identity.ts index d674352..90fdf31 100644 --- a/packages/sdk/src/modules/identity/identity.ts +++ b/packages/sdk/src/modules/identity/identity.ts @@ -27,7 +27,7 @@ export class Identity extends ApillonModule { } /** - * Get a wallet's online identity profile, including data from Subsocial and Polkadot Identity + * Get a wallet's on-chain identity data, including Subsocial and Polkadot Identity data * @param {string} walletAddress - Wallet address to retreive data for * @returns Identity data fetched from Polkadot Identity and Subsocial */ diff --git a/packages/sdk/src/tests/identity.test.ts b/packages/sdk/src/tests/identity.test.ts index 2b1fab5..36217d4 100644 --- a/packages/sdk/src/tests/identity.test.ts +++ b/packages/sdk/src/tests/identity.test.ts @@ -18,7 +18,7 @@ describe('IPNS tests for StorageBucket', () => { const [firstPart, secondPart] = message.split('\n'); // Validate that custom signing message was generated correctly expect(firstPart).toEqual(customMessage); - expect(+secondPart).toBeLessThan(new Date().getTime()); + expect(+secondPart).toBeLessThanOrEqual(new Date().getTime()); // Create a wallet from your private key const wallet = new Wallet(getWalletPrivateKey()); From a72176888de0fb41b1d3fe723e747aa1d202cb23 Mon Sep 17 00:00:00 2001 From: Damjan Dimitrov Date: Fri, 8 Dec 2023 14:52:55 +0100 Subject: [PATCH 07/14] Identity module updates (signature timestamp) --- packages/sdk/README.md | 12 +-- packages/sdk/src/modules/identity/identity.ts | 64 ++++++++------ packages/sdk/src/tests/identity.test.ts | 85 ++++++++++++++----- packages/sdk/src/types/identity.ts | 40 +++++++++ 4 files changed, 149 insertions(+), 52 deletions(-) diff --git a/packages/sdk/README.md b/packages/sdk/README.md index 62147db..84fbdd0 100644 --- a/packages/sdk/README.md +++ b/packages/sdk/README.md @@ -308,16 +308,18 @@ const identity = new Identity({ }); // generate a custom message to be signed by the user's wallet -const message = await identity.generateSigningMessage( +const { message, timestamp } = await identity.generateSigningMessage( 'Custom display message here', ); // validate an EVM wallet's signature for a given message -const { isValid } = await identity.validateEvmWalletSignature( - walletAddress, - signature, +const { isValid } = await identity.validateEvmWalletSignature({ message, -); + signature, + walletAddress: '0xa79bg13g2...', + timestamp, + signatureValidityMinutes: 15, +}); // validate a Polkadot wallet's signature for a given message const { isValid, address } = await identity.validatePolkadotWalletSignature( diff --git a/packages/sdk/src/modules/identity/identity.ts b/packages/sdk/src/modules/identity/identity.ts index 90fdf31..e4f032a 100644 --- a/packages/sdk/src/modules/identity/identity.ts +++ b/packages/sdk/src/modules/identity/identity.ts @@ -1,7 +1,11 @@ import { ApillonModule } from '../../lib/apillon'; import { ApillonApi } from '../../lib/apillon-api'; import { IApillonResponse } from '../../types/apillon'; -import { WalletIdentityData } from '../../types/identity'; +import { + IValidateEvmWalletSignature, + VerifySignedMessageResult, + WalletIdentityData, +} from '../../types/identity'; import { bufferToHex, ecrecover, @@ -17,15 +21,6 @@ export class Identity extends ApillonModule { */ private API_PREFIX = '/wallet-identity'; - /** - * Generate a message presented to the user when requested to sign using their wallet - * @param {string} [customText='Please sign this message'] - * @returns {string} - */ - public generateSigningMessage(customText = 'Please sign this message') { - return `${customText}\n${new Date().getTime()}`; - } - /** * Get a wallet's on-chain identity data, including Subsocial and Polkadot Identity data * @param {string} walletAddress - Wallet address to retreive data for @@ -41,18 +36,36 @@ export class Identity extends ApillonModule { return data; } + /** + * Generate a message presented to the user when requested to sign using their wallet + * @param {string} [customText='Please sign this message'] + * @returns {{message: string, timestamp: number}} + */ + public generateSigningMessage(customText = 'Please sign this message'): { + message: string; + timestamp: number; + } { + const timestamp = new Date().getTime(); + const message = `${customText}\n${timestamp}`; + return { message, timestamp }; + } + /** * Check if a signed message from an EVM wallet address is valid - * @param {string} walletAddress - Wallet address which signed the message - * @param {string} message - The message that has been signed by the wallet - * @param {string} signature - The wallet's signature, used for validation - * @returns {{isValid: boolean; address: string;}} + * @param {IValidateEvmWalletSignature} data - The data used to validate the EVM signature + * @returns {VerifySignedMessageResult} */ public validateEvmWalletSignature( - walletAddress: string, - message: string | Uint8Array, - signature: string, - ): { isValid: boolean; address: string } { + data: IValidateEvmWalletSignature, + ): VerifySignedMessageResult { + const { walletAddress, message, timestamp } = data; + + // Check if the timestamp is within the valid time range (default 10 minutes) + const isValidTimestamp = timestamp + ? new Date().getTime() - timestamp <= + (data.signatureValidityMinutes || 10) * 60_000 + : true; + // Prefix the message and hash it using Keccak-256 const prefixedMessage = keccak256( Buffer.from( @@ -61,7 +74,7 @@ export class Identity extends ApillonModule { ), ); // Split the signature into its components - const signatureParams = fromRpcSig(signature); + const signatureParams = fromRpcSig(data.signature); // Recover the public key const publicKey = ecrecover( @@ -72,10 +85,13 @@ export class Identity extends ApillonModule { ); // Recover the address from the signature and public key - const address = bufferToHex(publicToAddress(publicKey)).toLowerCase(); + const address = bufferToHex(publicToAddress(publicKey)); return { - isValid: address.toLowerCase() === walletAddress.toLowerCase(), + isValid: walletAddress + ? isValidTimestamp && + address.toLowerCase() === walletAddress.toLowerCase() + : true, address, }; } @@ -83,14 +99,14 @@ export class Identity extends ApillonModule { /** * Check if a signed message from a Polkadot wallet address is valid * @param {string} walletAddress - Wallet address which signed the message - * @param {string} message - The message that has been signed by the wallet - * @param {string} signature - The wallet's signature, used for validation + * @param {string | Uint8Array} message - The message that has been signed by the wallet + * @param {string | Uint8Array} signature - The wallet's signature, used for validation * @returns {{isValid: boolean; address: string;}} */ public validatePolkadotWalletSignature( walletAddress: string, message: string | Uint8Array, - signature: string, + signature: string | Uint8Array, ): { isValid: boolean; address: string } { const { isValid, publicKey } = signatureVerify( message, diff --git a/packages/sdk/src/tests/identity.test.ts b/packages/sdk/src/tests/identity.test.ts index 36217d4..161b03f 100644 --- a/packages/sdk/src/tests/identity.test.ts +++ b/packages/sdk/src/tests/identity.test.ts @@ -3,39 +3,78 @@ import { Identity } from '../modules/identity/identity'; import { getConfig, getWalletPrivateKey } from './helpers/helper'; import { Wallet } from 'ethers'; -describe('IPNS tests for StorageBucket', () => { +describe('Identity Module tests', () => { let config: ApillonConfig; + let wallet: Wallet; beforeAll(async () => { config = getConfig(); + wallet = new Wallet(getWalletPrivateKey()); }); - test('Validate EVM wallet signature', async () => { - const identity = new Identity(config); + describe('EVM wallet signature tests', () => { + test('Validate EVM wallet signature', async () => { + const identity = new Identity(config); - const customMessage = 'Identity SDK test'; - const message = identity.generateSigningMessage(customMessage); - const [firstPart, secondPart] = message.split('\n'); - // Validate that custom signing message was generated correctly - expect(firstPart).toEqual(customMessage); - expect(+secondPart).toBeLessThanOrEqual(new Date().getTime()); + const customMessage = 'Identity SDK test'; + const { message } = identity.generateSigningMessage(customMessage); + const [firstPart, secondPart] = message.split('\n'); + // Validate that custom signing message was generated correctly + expect(firstPart).toEqual(customMessage); + expect(+secondPart).toBeLessThanOrEqual(new Date().getTime()); - // Create a wallet from your private key - const wallet = new Wallet(getWalletPrivateKey()); + const signature = await wallet.signMessage(message); - const signature = await wallet.signMessage(message); + const res = identity.validateEvmWalletSignature({ + walletAddress: wallet.address, + message, + signature, + }); - console.log('Message:', message); - console.log('Signature:', signature); + expect(res.isValid).toBeTruthy(); + expect(res.address.toLowerCase()).toEqual(wallet.address.toLowerCase()); + }); - const res = identity.validateEvmWalletSignature( - wallet.address, - message, - signature, - ); + test('Validate EVM wallet signature with timestamp', async () => { + const identity = new Identity(config); - expect(res.isValid).toBeTruthy(); - expect(res.address.toLowerCase()).toEqual(wallet.address.toLowerCase()); + const customMessage = 'Identity SDK test'; + const { timestamp, message } = + identity.generateSigningMessage(customMessage); + + const signature = await wallet.signMessage(message); + + const res = identity.validateEvmWalletSignature({ + message, + signature, + timestamp, + signatureValidityMinutes: 1, + }); + + expect(res.isValid).toBeTruthy(); + expect(res.address.toLowerCase()).toEqual(wallet.address.toLowerCase()); + }); + + test('Validate EVM wallet signature with invalid timestamp', async () => { + const identity = new Identity(config); + + const customMessage = 'Identity SDK test'; + const { message } = identity.generateSigningMessage(customMessage); + + const signature = await wallet.signMessage(message); + const date = new Date(); + const thirtyMinEarlier = date.setTime(date.getTime() - 30 * 60_000); + + const res = identity.validateEvmWalletSignature({ + walletAddress: wallet.address, + message, + signature, + timestamp: thirtyMinEarlier, + }); + + expect(res.isValid).toBeFalsy(); + expect(res.address.toLowerCase()).toEqual(wallet.address.toLowerCase()); + }); }); test('Get wallet identity profile', async () => { @@ -54,9 +93,9 @@ describe('IPNS tests for StorageBucket', () => { expect(polkadot.web.Raw).toBe('https://web3.foundation/'); }); - test('Validate Polkadot wallet signature', async () => { + test.skip('Validate Polkadot wallet signature', async () => { const identity = new Identity(config); - const wallet = '5FNpf1ARUkrjBwrTncyCbPndg915YfaAnzToMkEcddiXWLyd'; + const wallet = ''; const res = identity.validatePolkadotWalletSignature( wallet, 'Please sign this message.', diff --git a/packages/sdk/src/types/identity.ts b/packages/sdk/src/types/identity.ts index 3f62929..7773c4d 100644 --- a/packages/sdk/src/types/identity.ts +++ b/packages/sdk/src/types/identity.ts @@ -8,3 +8,43 @@ export interface WalletIdentityData { */ subsocial: any; } + +/** + * Represents the parameters checking the validity of a signed message from an EVM wallet address. + */ +export interface IValidateEvmWalletSignature { + /** + * The message that has been signed by the wallet. + */ + message: string; + /** + * The wallet's signature for the given message + */ + signature: string; + /** + * (Optional) Wallet address parameter, used to check if address obtained from signature matches the parameter address + */ + walletAddress?: string; + /** + * The timestamp when the message was generated, for added security (optional). + */ + timestamp?: number; + /** + * For how many minutes the wallet signature is valid. + */ + signatureValidityMinutes?: number; +} + +/** + * Represents the result of checking the validity of a signed message. + */ +export interface VerifySignedMessageResult { + /** + * Indicates whether the message signature is valid. + */ + isValid: boolean; + /** + * The wallet address associated with the signature. + */ + address: string; +} From b6e129c9cfc5e4ca0754493a06bb8dc8e7eb7bf4 Mon Sep 17 00:00:00 2001 From: Damjan Dimitrov Date: Tue, 12 Dec 2023 10:30:22 +0100 Subject: [PATCH 08/14] Identity unit tests update --- .vscode/launch.json | 2 +- packages/sdk/package.json | 1 + packages/sdk/src/tests/helpers/helper.ts | 4 - packages/sdk/src/tests/identity.test.ts | 173 ++++++++++++++++++----- 4 files changed, 140 insertions(+), 40 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index bbb0356..f5aef9b 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -10,7 +10,7 @@ "${relativeFile}", "--config=${workspaceFolder}/package.json", "--runInBand", - "--detectOpenHandles", + // "--detectOpenHandles", "--forceExit" ], "cwd": "${workspaceFolder}", diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 79443dd..fd07121 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -49,6 +49,7 @@ "ethereumjs-util": "^7.1.5" }, "devDependencies": { + "@polkadot/keyring": "^12.6.1", "@types/jest": "^29.5.11", "dotenv": "^16.0.3", "eslint-config-common": "*", diff --git a/packages/sdk/src/tests/helpers/helper.ts b/packages/sdk/src/tests/helpers/helper.ts index d335df3..087a7a2 100644 --- a/packages/sdk/src/tests/helpers/helper.ts +++ b/packages/sdk/src/tests/helpers/helper.ts @@ -30,7 +30,3 @@ export function getWebsiteUUID() { export function getMintAddress() { return process.env['MINT_ADDRESS']; } - -export function getWalletPrivateKey() { - return process.env['WALLET_PRIVATE_KEY']; -} diff --git a/packages/sdk/src/tests/identity.test.ts b/packages/sdk/src/tests/identity.test.ts index 161b03f..b1242d9 100644 --- a/packages/sdk/src/tests/identity.test.ts +++ b/packages/sdk/src/tests/identity.test.ts @@ -1,79 +1,170 @@ +import { + cryptoWaitReady, + mnemonicGenerate, + mnemonicToMiniSecret, + ed25519PairFromSeed, + encodeAddress, +} from '@polkadot/util-crypto'; import { ApillonConfig } from '../lib/apillon'; import { Identity } from '../modules/identity/identity'; -import { getConfig, getWalletPrivateKey } from './helpers/helper'; +import { getConfig } from './helpers/helper'; import { Wallet } from 'ethers'; +import { Keyring } from '@polkadot/keyring'; describe('Identity Module tests', () => { let config: ApillonConfig; - let wallet: Wallet; + let identity: Identity; beforeAll(async () => { config = getConfig(); - wallet = new Wallet(getWalletPrivateKey()); + identity = new Identity(config); + await cryptoWaitReady(); }); describe('EVM wallet signature tests', () => { test('Validate EVM wallet signature', async () => { - const identity = new Identity(config); - - const customMessage = 'Identity SDK test'; + const customMessage = 'Identity EVM SDK test'; const { message } = identity.generateSigningMessage(customMessage); const [firstPart, secondPart] = message.split('\n'); // Validate that custom signing message was generated correctly expect(firstPart).toEqual(customMessage); expect(+secondPart).toBeLessThanOrEqual(new Date().getTime()); - const signature = await wallet.signMessage(message); + const { walletAddress, signature } = await generateEvmWalletAndSignature( + message, + ); - const res = identity.validateEvmWalletSignature({ - walletAddress: wallet.address, + const { isValid, address } = identity.validateEvmWalletSignature({ + walletAddress, message, signature, }); - expect(res.isValid).toBeTruthy(); - expect(res.address.toLowerCase()).toEqual(wallet.address.toLowerCase()); + expect(isValid).toBeTruthy(); + expect(address.toLowerCase()).toEqual(walletAddress.toLowerCase()); }); test('Validate EVM wallet signature with timestamp', async () => { const identity = new Identity(config); - const customMessage = 'Identity SDK test'; + const customMessage = 'Identity EVM SDK test'; const { timestamp, message } = identity.generateSigningMessage(customMessage); + expect(timestamp).toBeLessThanOrEqual(new Date().getTime()); - const signature = await wallet.signMessage(message); + const { walletAddress, signature } = await generateEvmWalletAndSignature( + message, + ); - const res = identity.validateEvmWalletSignature({ + const { isValid, address } = identity.validateEvmWalletSignature({ message, signature, timestamp, signatureValidityMinutes: 1, }); - expect(res.isValid).toBeTruthy(); - expect(res.address.toLowerCase()).toEqual(wallet.address.toLowerCase()); + expect(isValid).toBeTruthy(); + expect(address.toLowerCase()).toEqual(walletAddress.toLowerCase()); }); test('Validate EVM wallet signature with invalid timestamp', async () => { const identity = new Identity(config); - const customMessage = 'Identity SDK test'; + const customMessage = 'Identity EVM SDK test'; const { message } = identity.generateSigningMessage(customMessage); - const signature = await wallet.signMessage(message); + const { walletAddress, signature } = await generateEvmWalletAndSignature( + message, + ); + const date = new Date(); const thirtyMinEarlier = date.setTime(date.getTime() - 30 * 60_000); - const res = identity.validateEvmWalletSignature({ - walletAddress: wallet.address, + const { isValid, address } = identity.validateEvmWalletSignature({ + walletAddress, message, signature, timestamp: thirtyMinEarlier, }); - expect(res.isValid).toBeFalsy(); - expect(res.address.toLowerCase()).toEqual(wallet.address.toLowerCase()); + expect(isValid).toBeFalsy(); + expect(address.toLowerCase()).toEqual(walletAddress.toLowerCase()); + }); + }); + + describe('Polkadot wallet signature tests', () => { + test('Validate Polkadot wallet signature', async () => { + const identity = new Identity(config); + + const customMessage = 'Identity Polkadot SDK test'; + const { message } = identity.generateSigningMessage(customMessage); + const [firstPart, secondPart] = message.split('\n'); + // Validate that custom signing message was generated correctly + expect(firstPart).toEqual(customMessage); + expect(+secondPart).toBeLessThanOrEqual(new Date().getTime()); + + const { walletAddress, signature } = + generatePolkadotWalletAndSignature(message); + + const { isValid, address } = identity.validatePolkadotWalletSignature({ + walletAddress, + signature, + message, + }); + + expect(isValid).toBeTruthy(); + expect(address.toLowerCase()).toEqual( + encodeAddress(walletAddress).toLowerCase(), + ); + }); + + test('Validate Polkadot wallet signature with timestamp', async () => { + const identity = new Identity(config); + + const customMessage = 'Identity Polkadot SDK test'; + const { timestamp, message } = + identity.generateSigningMessage(customMessage); + expect(timestamp).toBeLessThanOrEqual(new Date().getTime()); + + const { walletAddress, signature } = + generatePolkadotWalletAndSignature(message); + + const { isValid, address } = identity.validatePolkadotWalletSignature({ + walletAddress, + signature, + message, + timestamp, + signatureValidityMinutes: 1, + }); + + expect(isValid).toBeTruthy(); + expect(address.toLowerCase()).toEqual( + encodeAddress(walletAddress).toLowerCase(), + ); + }); + + test('Validate Polkadot wallet signature with invalid timestamp', async () => { + const identity = new Identity(config); + + const customMessage = 'Identity Polkadot SDK test'; + const { message } = identity.generateSigningMessage(customMessage); + + const { walletAddress, signature } = + generatePolkadotWalletAndSignature(message); + const date = new Date(); + const thirtyMinEarlier = date.setTime(date.getTime() - 30 * 60_000); + + const { isValid, address } = identity.validatePolkadotWalletSignature({ + walletAddress, + message, + signature, + timestamp: thirtyMinEarlier, + }); + + expect(isValid).toBeFalsy(); + expect(address.toLowerCase()).toEqual( + encodeAddress(walletAddress).toLowerCase(), + ); }); }); @@ -93,17 +184,29 @@ describe('Identity Module tests', () => { expect(polkadot.web.Raw).toBe('https://web3.foundation/'); }); - test.skip('Validate Polkadot wallet signature', async () => { - const identity = new Identity(config); - const wallet = ''; - const res = identity.validatePolkadotWalletSignature( - wallet, - 'Please sign this message.', - // Fill the below value with you own signature - // you can obtain a sample with wallet login at https://app.apillon.io/login - '', - ); - expect(res.isValid).toBeTruthy(); - expect(res.address.toLowerCase()).toEqual(wallet.toLowerCase()); - }); + const generateEvmWalletAndSignature = async (message: string) => { + const mnemonic = Wallet.createRandom().mnemonic.phrase; + const wallet = Wallet.fromPhrase(mnemonic); + const signature = await wallet.signMessage(message); + + return { walletAddress: wallet.address, signature }; + }; + + const generatePolkadotWalletAndSignature = (message: string) => { + const mnemonic = mnemonicGenerate(); + const seedPhrase = mnemonicToMiniSecret(mnemonic); + + // wallet address obtained from seed phrase + const walletAddress = ed25519PairFromSeed(seedPhrase).publicKey; + + const keypair = new Keyring({ + ss58Format: 38, + type: 'ed25519', + }).createFromUri(mnemonic); + + // Sign message with generated wallet + const signature = keypair.sign(message); + + return { walletAddress, signature }; + }; }); From 6697abb685b9d89ec729a4bda8d410968ea005f7 Mon Sep 17 00:00:00 2001 From: Damjan Dimitrov Date: Tue, 12 Dec 2023 10:30:30 +0100 Subject: [PATCH 09/14] Identity module updates --- packages/sdk/src/modules/identity/identity.ts | 48 +++++++++++-------- packages/sdk/src/types/identity.ts | 29 +++++++++-- 2 files changed, 54 insertions(+), 23 deletions(-) diff --git a/packages/sdk/src/modules/identity/identity.ts b/packages/sdk/src/modules/identity/identity.ts index e4f032a..c84ebbe 100644 --- a/packages/sdk/src/modules/identity/identity.ts +++ b/packages/sdk/src/modules/identity/identity.ts @@ -3,6 +3,7 @@ import { ApillonApi } from '../../lib/apillon-api'; import { IApillonResponse } from '../../types/apillon'; import { IValidateEvmWalletSignature, + IValidatePolkadotWalletSignature, VerifySignedMessageResult, WalletIdentityData, } from '../../types/identity'; @@ -52,7 +53,7 @@ export class Identity extends ApillonModule { /** * Check if a signed message from an EVM wallet address is valid - * @param {IValidateEvmWalletSignature} data - The data used to validate the EVM signature + * @param {IValidateWalletSignature} data - The data used to validate the EVM signature * @returns {VerifySignedMessageResult} */ public validateEvmWalletSignature( @@ -74,45 +75,52 @@ export class Identity extends ApillonModule { ), ); // Split the signature into its components - const signatureParams = fromRpcSig(data.signature); + const { v, r, s } = fromRpcSig(data.signature); // Recover the public key - const publicKey = ecrecover( - prefixedMessage, - signatureParams.v, - signatureParams.r, - signatureParams.s, - ); + const publicKey = ecrecover(prefixedMessage, v, r, s); // Recover the address from the signature and public key const address = bufferToHex(publicToAddress(publicKey)); + const isValidAddress = walletAddress + ? address.toLowerCase() === walletAddress.toLowerCase() + : true; + return { - isValid: walletAddress - ? isValidTimestamp && - address.toLowerCase() === walletAddress.toLowerCase() - : true, + isValid: isValidTimestamp && isValidAddress, address, }; } /** * Check if a signed message from a Polkadot wallet address is valid - * @param {string} walletAddress - Wallet address which signed the message - * @param {string | Uint8Array} message - The message that has been signed by the wallet - * @param {string | Uint8Array} signature - The wallet's signature, used for validation + * @param {IValidatePolkadotWalletSignature} data - The data used to validate the Polkadot signature * @returns {{isValid: boolean; address: string;}} */ public validatePolkadotWalletSignature( - walletAddress: string, - message: string | Uint8Array, - signature: string | Uint8Array, - ): { isValid: boolean; address: string } { + data: IValidatePolkadotWalletSignature, + ): { + isValid: boolean; + address: string; + } { + const { message, signature, walletAddress, timestamp } = data; + + // Check if the timestamp is within the valid time range (default 10 minutes) + const isValidTimestamp = timestamp + ? new Date().getTime() - timestamp <= + (data.signatureValidityMinutes || 10) * 60_000 + : true; + const { isValid, publicKey } = signatureVerify( message, signature, walletAddress, ); - return { isValid, address: encodeAddress(publicKey) }; + + return { + isValid: isValidTimestamp && isValid, + address: encodeAddress(publicKey), + }; } } diff --git a/packages/sdk/src/types/identity.ts b/packages/sdk/src/types/identity.ts index 7773c4d..cf5a0b4 100644 --- a/packages/sdk/src/types/identity.ts +++ b/packages/sdk/src/types/identity.ts @@ -10,9 +10,10 @@ export interface WalletIdentityData { } /** - * Represents the parameters checking the validity of a signed message from an EVM wallet address. + * Represents the parameters for checking validity of a signed message from an EVM wallet address. */ -export interface IValidateEvmWalletSignature { +export interface IValidateEvmWalletSignature + extends IValidateSignatureTimestamp { /** * The message that has been signed by the wallet. */ @@ -25,12 +26,34 @@ export interface IValidateEvmWalletSignature { * (Optional) Wallet address parameter, used to check if address obtained from signature matches the parameter address */ walletAddress?: string; +} + +/** + * Represents the parameters for checking validity of a signed message from a Polkadot wallet address. + */ +export interface IValidatePolkadotWalletSignature + extends IValidateSignatureTimestamp { + /** + * The message that has been signed by the wallet. + */ + message: string | Uint8Array; + /** + * The wallet's signature for the given message + */ + signature: string | Uint8Array; + /** + * Wallet address parameter, used to check if address obtained from signature matches the parameter address + */ + walletAddress: string | Uint8Array; +} + +interface IValidateSignatureTimestamp { /** * The timestamp when the message was generated, for added security (optional). */ timestamp?: number; /** - * For how many minutes the wallet signature is valid. + * For how many minutes the wallet signature is valid (default 10). */ signatureValidityMinutes?: number; } From d01e5c525f2e6ff74239eb9e00464d790b67746e Mon Sep 17 00:00:00 2001 From: Damjan Dimitrov Date: Tue, 12 Dec 2023 10:31:35 +0100 Subject: [PATCH 10/14] Upgrade version to 1.2.0 --- package-lock.json | 55 +++++++++++++++++++++++++++++++++++---- package.json | 2 +- packages/cli/package.json | 2 +- packages/sdk/package.json | 2 +- 4 files changed, 53 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index e5974b7..2ece87e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "apillon-web3-tools", - "version": "1.1.0", + "version": "1.2.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "apillon-web3-tools", - "version": "1.1.0", + "version": "1.2.0", "license": "MIT", "workspaces": [ "packages/*" @@ -1474,6 +1474,30 @@ "node": ">=14" } }, + "node_modules/@polkadot/keyring": { + "version": "12.6.1", + "resolved": "https://registry.npmjs.org/@polkadot/keyring/-/keyring-12.6.1.tgz", + "integrity": "sha512-cicTctZr5Jy5vgNT2FsNiKoTZnz6zQkgDoIYv79NI+p1Fhwc9C+DN/iMCnk3Cm9vR2gSAd2fSV+Y5iKVDhAmUw==", + "dev": true, + "dependencies": { + "@polkadot/util": "12.6.1", + "@polkadot/util-crypto": "12.6.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "12.6.1", + "@polkadot/util-crypto": "12.6.1" + } + }, + "node_modules/@polkadot/keyring/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, "node_modules/@polkadot/networks": { "version": "12.6.1", "resolved": "https://registry.npmjs.org/@polkadot/networks/-/networks-12.6.1.tgz", @@ -7040,7 +7064,7 @@ }, "packages/cli": { "name": "@apillon/cli", - "version": "1.1.0", + "version": "1.2.0", "license": "MIT", "dependencies": { "@apillon/sdk": "*", @@ -7143,7 +7167,7 @@ }, "packages/sdk": { "name": "@apillon/sdk", - "version": "1.1.0", + "version": "1.2.0", "license": "MIT", "dependencies": { "@polkadot/util-crypto": "^12.6.1", @@ -7151,6 +7175,7 @@ "ethereumjs-util": "^7.1.5" }, "devDependencies": { + "@polkadot/keyring": "^12.6.1", "@types/jest": "^29.5.11", "dotenv": "^16.0.3", "eslint-config-common": "*", @@ -7303,7 +7328,8 @@ "@apillon/sdk": { "version": "file:packages/sdk", "requires": { - "@polkadot/util-crypto": "*", + "@polkadot/keyring": "^12.6.1", + "@polkadot/util-crypto": "^12.6.1", "@types/jest": "^29.5.11", "axios": "^1.3.4", "dotenv": "^16.0.3", @@ -8462,6 +8488,25 @@ "dev": true, "optional": true }, + "@polkadot/keyring": { + "version": "12.6.1", + "resolved": "https://registry.npmjs.org/@polkadot/keyring/-/keyring-12.6.1.tgz", + "integrity": "sha512-cicTctZr5Jy5vgNT2FsNiKoTZnz6zQkgDoIYv79NI+p1Fhwc9C+DN/iMCnk3Cm9vR2gSAd2fSV+Y5iKVDhAmUw==", + "dev": true, + "requires": { + "@polkadot/util": "12.6.1", + "@polkadot/util-crypto": "12.6.1", + "tslib": "^2.6.2" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + } + } + }, "@polkadot/networks": { "version": "12.6.1", "resolved": "https://registry.npmjs.org/@polkadot/networks/-/networks-12.6.1.tgz", diff --git a/package.json b/package.json index 4f560bf..7bfaf6f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "apillon-web3-tools", - "version": "1.1.0", + "version": "1.2.0", "description": "Monorepo for Apillon tools", "author": "Apillon", "license": "MIT", diff --git a/packages/cli/package.json b/packages/cli/package.json index c701865..f08b0d8 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,7 +1,7 @@ { "name": "@apillon/cli", "description": "▶◀ Apillon CLI tools ▶◀", - "version": "1.1.0", + "version": "1.2.0", "author": "Apillon", "license": "MIT", "main": "./dist/index.js", diff --git a/packages/sdk/package.json b/packages/sdk/package.json index fd07121..8ef48c4 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -1,7 +1,7 @@ { "name": "@apillon/sdk", "description": "▶◀ Apillon SDK for NodeJS ▶◀", - "version": "1.1.0", + "version": "1.2.0", "author": "Apillon", "license": "MIT", "main": "./dist/index.js", From 2363857561906c822d744f5e670dfe5e46fd33d2 Mon Sep 17 00:00:00 2001 From: Damjan Dimitrov Date: Tue, 12 Dec 2023 10:42:41 +0100 Subject: [PATCH 11/14] Update Identity module README --- packages/sdk/README.md | 20 +++++++++++++------- packages/tsconfig/base.json | 2 +- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/packages/sdk/README.md b/packages/sdk/README.md index 84fbdd0..5590a80 100644 --- a/packages/sdk/README.md +++ b/packages/sdk/README.md @@ -313,20 +313,26 @@ const { message, timestamp } = await identity.generateSigningMessage( ); // validate an EVM wallet's signature for a given message -const { isValid } = await identity.validateEvmWalletSignature({ +const { isValid, address } = await identity.validateEvmWalletSignature({ message, - signature, + signature, // signature obtained from the user's wallet by the client app walletAddress: '0xa79bg13g2...', + /* + * optional - check signature time validity by providing a timestamp + * which indicates when the signature was generated + */ timestamp, + // additionally, specify for how many minutes the timestamp is valid signatureValidityMinutes: 15, }); // validate a Polkadot wallet's signature for a given message -const { isValid, address } = await identity.validatePolkadotWalletSignature( - walletAddress, - signature, - message, -); +const { isValid: isPolkadotValid } = + await identity.validatePolkadotWalletSignature({ + message, + signature, // signature obtained from the user's wallet by the client app + walletAddress: '5HqHQDGcHqS...', + }); // obtain on-chain identity data for a Polkadot wallet const { polkadot, subsocial } = await identity.getWalletIdentity(address); diff --git a/packages/tsconfig/base.json b/packages/tsconfig/base.json index 470daff..24d0a2c 100644 --- a/packages/tsconfig/base.json +++ b/packages/tsconfig/base.json @@ -17,7 +17,7 @@ "noUnusedLocals": false, "noUnusedParameters": false, "preserveWatchOutput": true, - "removeComments": true, + "removeComments": false, "skipLibCheck": true, "sourceMap": true, "target": "es2017" From e66349e094bc946fdc3450b1284cce515dc22199 Mon Sep 17 00:00:00 2001 From: Damjan Dimitrov Date: Wed, 13 Dec 2023 10:14:59 +0100 Subject: [PATCH 12/14] Signature timestamp validation updates --- packages/sdk/README.md | 63 ++++++++++++------- packages/sdk/src/modules/identity/identity.ts | 12 ++++ packages/sdk/src/tests/identity.test.ts | 36 +++++------ packages/sdk/src/types/identity.ts | 3 + 4 files changed, 73 insertions(+), 41 deletions(-) diff --git a/packages/sdk/README.md b/packages/sdk/README.md index 5590a80..aeea131 100644 --- a/packages/sdk/README.md +++ b/packages/sdk/README.md @@ -301,40 +301,59 @@ For detailed hosting SDK method, class and property documentation visit [SDK ide ### Usage example ```ts +import { Identity } from './modules/identity/identity'; +import { LogLevel } from './types/apillon'; + const identity = new Identity({ key: 'yourApiKey', secret: 'yourApiSecret', logLevel: LogLevel.NONE, }); -// generate a custom message to be signed by the user's wallet -const { message, timestamp } = await identity.generateSigningMessage( - 'Custom display message here', -); +// obtain on-chain identity data for a Polkadot wallet +const { polkadot, subsocial } = await identity.getWalletIdentity(address); -// validate an EVM wallet's signature for a given message -const { isValid, address } = await identity.validateEvmWalletSignature({ - message, - signature, // signature obtained from the user's wallet by the client app - walletAddress: '0xa79bg13g2...', - /* - * optional - check signature time validity by providing a timestamp - * which indicates when the signature was generated - */ - timestamp, - // additionally, specify for how many minutes the timestamp is valid - signatureValidityMinutes: 15, -}); +async function validateEvmWalletSignature() { + // generate a custom message to be signed by the user's wallet + const { message, timestamp } = await identity.generateSigningMessage( + 'Custom display message here', + ); + const walletAddress = '0xa79bg13g2...'; -// validate a Polkadot wallet's signature for a given message -const { isValid: isPolkadotValid } = - await identity.validatePolkadotWalletSignature({ + // validate an EVM wallet's signature for a given message + const { isValid, address } = await identity.validateEvmWalletSignature({ message, signature, // signature obtained from the user's wallet by the client app + walletAddress, + /* + * optional - check signature time validity by providing a timestamp + * which indicates when the signature was generated + */ + timestamp, + // additionally, specify for how many minutes the timestamp is valid + signatureValidityMinutes: 15, + }); + + console.log(isValid); // true + console.log(address.toLowerCase() === walletAddress.toLowerCase()); // true +} + +async function validatePolkadotWalletSignature() { + // If you wish to generate the message yourself and validate the timestamp, + // use a signing message as shown below: + const timestamp = new Date().getTime(); + const message = 'Message from my Dapp'; + const signingMessage = `${message}\n${timestamp}`; + + // validate a Polkadot wallet's signature for a given signing message + const { isValid } = await identity.validatePolkadotWalletSignature({ + message: signingMessage, + signature, // signature obtained from the user's wallet by the client app walletAddress: '5HqHQDGcHqS...', + timestamp, + signatureValidityMinutes: 5, }); +} -// obtain on-chain identity data for a Polkadot wallet -const { polkadot, subsocial } = await identity.getWalletIdentity(address); ``` diff --git a/packages/sdk/src/modules/identity/identity.ts b/packages/sdk/src/modules/identity/identity.ts index c84ebbe..1cd55d7 100644 --- a/packages/sdk/src/modules/identity/identity.ts +++ b/packages/sdk/src/modules/identity/identity.ts @@ -61,6 +61,9 @@ export class Identity extends ApillonModule { ): VerifySignedMessageResult { const { walletAddress, message, timestamp } = data; + if (timestamp && message?.split('\n').at(-1) !== `${timestamp}`) { + throw new Error('Message does not contain a valid timestamp'); + } // Check if the timestamp is within the valid time range (default 10 minutes) const isValidTimestamp = timestamp ? new Date().getTime() - timestamp <= @@ -106,6 +109,15 @@ export class Identity extends ApillonModule { } { const { message, signature, walletAddress, timestamp } = data; + const signingMessage = + message instanceof Uint8Array + ? Buffer.from(message).toString('base64') + : message; + + if (timestamp && signingMessage?.split('\n').at(-1) !== `${timestamp}`) { + throw new Error('Message does not contain a valid timestamp'); + } + // Check if the timestamp is within the valid time range (default 10 minutes) const isValidTimestamp = timestamp ? new Date().getTime() - timestamp <= diff --git a/packages/sdk/src/tests/identity.test.ts b/packages/sdk/src/tests/identity.test.ts index b1242d9..003d304 100644 --- a/packages/sdk/src/tests/identity.test.ts +++ b/packages/sdk/src/tests/identity.test.ts @@ -80,15 +80,15 @@ describe('Identity Module tests', () => { const date = new Date(); const thirtyMinEarlier = date.setTime(date.getTime() - 30 * 60_000); - const { isValid, address } = identity.validateEvmWalletSignature({ - walletAddress, - message, - signature, - timestamp: thirtyMinEarlier, - }); - - expect(isValid).toBeFalsy(); - expect(address.toLowerCase()).toEqual(walletAddress.toLowerCase()); + const validate = () => + identity.validateEvmWalletSignature({ + walletAddress, + message, + signature, + timestamp: thirtyMinEarlier, + }); + + expect(validate).toThrow('Message does not contain a valid timestamp'); }); }); @@ -154,17 +154,15 @@ describe('Identity Module tests', () => { const date = new Date(); const thirtyMinEarlier = date.setTime(date.getTime() - 30 * 60_000); - const { isValid, address } = identity.validatePolkadotWalletSignature({ - walletAddress, - message, - signature, - timestamp: thirtyMinEarlier, - }); + const validate = () => + identity.validatePolkadotWalletSignature({ + walletAddress, + message, + signature, + timestamp: thirtyMinEarlier, + }); - expect(isValid).toBeFalsy(); - expect(address.toLowerCase()).toEqual( - encodeAddress(walletAddress).toLowerCase(), - ); + expect(validate).toThrow(); }); }); diff --git a/packages/sdk/src/types/identity.ts b/packages/sdk/src/types/identity.ts index cf5a0b4..88bd146 100644 --- a/packages/sdk/src/types/identity.ts +++ b/packages/sdk/src/types/identity.ts @@ -50,6 +50,9 @@ export interface IValidatePolkadotWalletSignature interface IValidateSignatureTimestamp { /** * The timestamp when the message was generated, for added security (optional). + * + * If you are generating the message yourself and you wish to validate the timestamp, + * append `\n${timestamp}` to the end of the message with your own timestamp. */ timestamp?: number; /** From e17d6c5331ac398450cf402c8a5005dc54cf6faa Mon Sep 17 00:00:00 2001 From: Damjan Dimitrov Date: Wed, 20 Dec 2023 14:46:36 +0100 Subject: [PATCH 13/14] Update launch.json --- .vscode/launch.json | 1 - 1 file changed, 1 deletion(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index f5aef9b..ab3dbcd 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -10,7 +10,6 @@ "${relativeFile}", "--config=${workspaceFolder}/package.json", "--runInBand", - // "--detectOpenHandles", "--forceExit" ], "cwd": "${workspaceFolder}", From b0efe0257ee821cff32f88d805cdd4db3a35f305 Mon Sep 17 00:00:00 2001 From: Damjan Dimitrov Date: Wed, 20 Dec 2023 14:54:02 +0100 Subject: [PATCH 14/14] Add notes to identity README --- packages/sdk/README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/sdk/README.md b/packages/sdk/README.md index aeea131..6f848aa 100644 --- a/packages/sdk/README.md +++ b/packages/sdk/README.md @@ -304,6 +304,7 @@ For detailed hosting SDK method, class and property documentation visit [SDK ide import { Identity } from './modules/identity/identity'; import { LogLevel } from './types/apillon'; +// Note: for signature-related methods API config is not required const identity = new Identity({ key: 'yourApiKey', secret: 'yourApiSecret', @@ -318,6 +319,12 @@ async function validateEvmWalletSignature() { const { message, timestamp } = await identity.generateSigningMessage( 'Custom display message here', ); + + // alternatively, you can generate you own signing message with timestamp like this: + // const timestamp = new Date().getTime(); + // const message = 'Message from my Dapp'; + // const signingMessage = `${message}\n${timestamp}`; + const walletAddress = '0xa79bg13g2...'; // validate an EVM wallet's signature for a given message