mirror of
https://github.com/logos-messaging/OpChan.git
synced 2026-01-06 23:03:07 +00:00
chore: improve signatures - bind w1/s1 with delegated keypairs
This commit is contained in:
parent
5e0622183e
commit
414747f396
106
package-lock.json
generated
106
package-lock.json
generated
@ -44,6 +44,7 @@
|
|||||||
"@reown/appkit-wallet-button": "^1.7.17",
|
"@reown/appkit-wallet-button": "^1.7.17",
|
||||||
"@tanstack/react-query": "^5.84.1",
|
"@tanstack/react-query": "^5.84.1",
|
||||||
"@waku/sdk": "^0.0.35-67a7287.0",
|
"@waku/sdk": "^0.0.35-67a7287.0",
|
||||||
|
"bitcoinjs-message": "^2.2.0",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"cmdk": "^1.0.0",
|
"cmdk": "^1.0.0",
|
||||||
@ -64,6 +65,7 @@
|
|||||||
"tailwindcss-animate": "^1.0.7",
|
"tailwindcss-animate": "^1.0.7",
|
||||||
"uuid": "^11.1.0",
|
"uuid": "^11.1.0",
|
||||||
"vaul": "^0.9.3",
|
"vaul": "^0.9.3",
|
||||||
|
"viem": "^2.37.1",
|
||||||
"wagmi": "^2.16.1",
|
"wagmi": "^2.16.1",
|
||||||
"zod": "^3.23.8"
|
"zod": "^3.23.8"
|
||||||
},
|
},
|
||||||
@ -8249,7 +8251,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
|
||||||
"integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
|
"integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"file-uri-to-path": "1.0.0"
|
"file-uri-to-path": "1.0.0"
|
||||||
}
|
}
|
||||||
@ -8284,7 +8285,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/bip66/-/bip66-1.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/bip66/-/bip66-1.1.5.tgz",
|
||||||
"integrity": "sha512-nemMHz95EmS38a26XbbdxIYj5csHd3RMP3H5bwQknX0WYHF01qhpufP42mLOwVICuH2JmhIhXiWs89MfUGL7Xw==",
|
"integrity": "sha512-nemMHz95EmS38a26XbbdxIYj5csHd3RMP3H5bwQknX0WYHF01qhpufP42mLOwVICuH2JmhIhXiWs89MfUGL7Xw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"safe-buffer": "^5.0.1"
|
"safe-buffer": "^5.0.1"
|
||||||
}
|
}
|
||||||
@ -8322,7 +8322,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/bitcoinjs-message/-/bitcoinjs-message-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/bitcoinjs-message/-/bitcoinjs-message-2.2.0.tgz",
|
||||||
"integrity": "sha512-103Wy3xg8Y9o+pdhGP4M3/mtQQuUWs6sPuOp1mYphSUoSMHjHTlkj32K4zxU8qMH0Ckv23emfkGlFWtoWZ7YFA==",
|
"integrity": "sha512-103Wy3xg8Y9o+pdhGP4M3/mtQQuUWs6sPuOp1mYphSUoSMHjHTlkj32K4zxU8qMH0Ckv23emfkGlFWtoWZ7YFA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bech32": "^1.1.3",
|
"bech32": "^1.1.3",
|
||||||
"bs58check": "^2.1.2",
|
"bs58check": "^2.1.2",
|
||||||
@ -8340,7 +8339,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.11.tgz",
|
"resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.11.tgz",
|
||||||
"integrity": "sha512-xz7wQ8xDhdyP7tQxwdteLYeFfS68tSMNCZ/Y37WJ4bhGfKPpqEIlmIyueQHqOyoPhE6xNUqjzRr8ra0eF9VRvA==",
|
"integrity": "sha512-xz7wQ8xDhdyP7tQxwdteLYeFfS68tSMNCZ/Y37WJ4bhGfKPpqEIlmIyueQHqOyoPhE6xNUqjzRr8ra0eF9VRvA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"safe-buffer": "^5.0.1"
|
"safe-buffer": "^5.0.1"
|
||||||
}
|
}
|
||||||
@ -8349,22 +8347,19 @@
|
|||||||
"version": "1.1.4",
|
"version": "1.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz",
|
||||||
"integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==",
|
"integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==",
|
||||||
"license": "MIT",
|
"license": "MIT"
|
||||||
"optional": true
|
|
||||||
},
|
},
|
||||||
"node_modules/bitcoinjs-message/node_modules/bn.js": {
|
"node_modules/bitcoinjs-message/node_modules/bn.js": {
|
||||||
"version": "4.12.2",
|
"version": "4.12.2",
|
||||||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz",
|
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz",
|
||||||
"integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==",
|
"integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==",
|
||||||
"license": "MIT",
|
"license": "MIT"
|
||||||
"optional": true
|
|
||||||
},
|
},
|
||||||
"node_modules/bitcoinjs-message/node_modules/bs58": {
|
"node_modules/bitcoinjs-message/node_modules/bs58": {
|
||||||
"version": "4.0.1",
|
"version": "4.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz",
|
||||||
"integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==",
|
"integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"base-x": "^3.0.2"
|
"base-x": "^3.0.2"
|
||||||
}
|
}
|
||||||
@ -8374,7 +8369,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz",
|
||||||
"integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==",
|
"integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bs58": "^4.0.0",
|
"bs58": "^4.0.0",
|
||||||
"create-hash": "^1.1.0",
|
"create-hash": "^1.1.0",
|
||||||
@ -8387,7 +8381,6 @@
|
|||||||
"integrity": "sha512-tArjQw2P0RTdY7QmkNehgp6TVvQXq6ulIhxv8gaH6YubKG/wxxAoNKcbuXjDhybbc+b2Ihc7e0xxiGN744UIiQ==",
|
"integrity": "sha512-tArjQw2P0RTdY7QmkNehgp6TVvQXq6ulIhxv8gaH6YubKG/wxxAoNKcbuXjDhybbc+b2Ihc7e0xxiGN744UIiQ==",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bindings": "^1.5.0",
|
"bindings": "^1.5.0",
|
||||||
"bip66": "^1.1.5",
|
"bip66": "^1.1.5",
|
||||||
@ -8447,15 +8440,13 @@
|
|||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
|
||||||
"integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==",
|
"integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==",
|
||||||
"license": "MIT",
|
"license": "MIT"
|
||||||
"optional": true
|
|
||||||
},
|
},
|
||||||
"node_modules/browserify-aes": {
|
"node_modules/browserify-aes": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
|
||||||
"integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==",
|
"integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"buffer-xor": "^1.0.3",
|
"buffer-xor": "^1.0.3",
|
||||||
"cipher-base": "^1.0.0",
|
"cipher-base": "^1.0.0",
|
||||||
@ -8561,7 +8552,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/buffer-equals/-/buffer-equals-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/buffer-equals/-/buffer-equals-1.0.4.tgz",
|
||||||
"integrity": "sha512-99MsCq0j5+RhubVEtKQgKaD6EM+UP3xJgIvQqwJ3SOLDUekzxMX1ylXBng+Wa2sh7mGT0W6RUly8ojjr1Tt6nA==",
|
"integrity": "sha512-99MsCq0j5+RhubVEtKQgKaD6EM+UP3xJgIvQqwJ3SOLDUekzxMX1ylXBng+Wa2sh7mGT0W6RUly8ojjr1Tt6nA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
@ -8570,8 +8560,7 @@
|
|||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz",
|
||||||
"integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==",
|
"integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==",
|
||||||
"license": "MIT",
|
"license": "MIT"
|
||||||
"optional": true
|
|
||||||
},
|
},
|
||||||
"node_modules/bufferutil": {
|
"node_modules/bufferutil": {
|
||||||
"version": "4.0.9",
|
"version": "4.0.9",
|
||||||
@ -8808,7 +8797,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.6.tgz",
|
||||||
"integrity": "sha512-3Ek9H3X6pj5TgenXYtNWdaBon1tgYCaebd+XPg0keyjEbEfkD4KkmAxkQ/i1vYvxdcT5nscLBfq9VJRmCBcFSw==",
|
"integrity": "sha512-3Ek9H3X6pj5TgenXYtNWdaBon1tgYCaebd+XPg0keyjEbEfkD4KkmAxkQ/i1vYvxdcT5nscLBfq9VJRmCBcFSw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"inherits": "^2.0.4",
|
"inherits": "^2.0.4",
|
||||||
"safe-buffer": "^5.2.1"
|
"safe-buffer": "^5.2.1"
|
||||||
@ -9356,7 +9344,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
|
||||||
"integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==",
|
"integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cipher-base": "^1.0.1",
|
"cipher-base": "^1.0.1",
|
||||||
"inherits": "^2.0.1",
|
"inherits": "^2.0.1",
|
||||||
@ -9370,7 +9357,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
|
"resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
|
||||||
"integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==",
|
"integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cipher-base": "^1.0.3",
|
"cipher-base": "^1.0.3",
|
||||||
"create-hash": "^1.1.0",
|
"create-hash": "^1.1.0",
|
||||||
@ -9827,7 +9813,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/drbg.js/-/drbg.js-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/drbg.js/-/drbg.js-1.0.1.tgz",
|
||||||
"integrity": "sha512-F4wZ06PvqxYLFEZKkFxTDcns9oFNk34hvmJSEwdzsxVQ8YI5YaxtACgQatkYgv2VI2CFkUd2Y+xosPQnHv809g==",
|
"integrity": "sha512-F4wZ06PvqxYLFEZKkFxTDcns9oFNk34hvmJSEwdzsxVQ8YI5YaxtACgQatkYgv2VI2CFkUd2Y+xosPQnHv809g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"browserify-aes": "^1.0.6",
|
"browserify-aes": "^1.0.6",
|
||||||
"create-hash": "^1.1.2",
|
"create-hash": "^1.1.2",
|
||||||
@ -9942,7 +9927,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.1.tgz",
|
||||||
"integrity": "sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==",
|
"integrity": "sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bn.js": "^4.11.9",
|
"bn.js": "^4.11.9",
|
||||||
"brorand": "^1.1.0",
|
"brorand": "^1.1.0",
|
||||||
@ -9957,8 +9941,7 @@
|
|||||||
"version": "4.12.2",
|
"version": "4.12.2",
|
||||||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz",
|
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz",
|
||||||
"integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==",
|
"integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==",
|
||||||
"license": "MIT",
|
"license": "MIT"
|
||||||
"optional": true
|
|
||||||
},
|
},
|
||||||
"node_modules/embla-carousel": {
|
"node_modules/embla-carousel": {
|
||||||
"version": "8.3.0",
|
"version": "8.3.0",
|
||||||
@ -10550,7 +10533,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz",
|
||||||
"integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==",
|
"integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"md5.js": "^1.3.4",
|
"md5.js": "^1.3.4",
|
||||||
"safe-buffer": "^5.1.1"
|
"safe-buffer": "^5.1.1"
|
||||||
@ -10691,8 +10673,7 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
|
||||||
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
|
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
|
||||||
"license": "MIT",
|
"license": "MIT"
|
||||||
"optional": true
|
|
||||||
},
|
},
|
||||||
"node_modules/fill-range": {
|
"node_modules/fill-range": {
|
||||||
"version": "7.1.1",
|
"version": "7.1.1",
|
||||||
@ -11086,7 +11067,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz",
|
||||||
"integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==",
|
"integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"inherits": "^2.0.4",
|
"inherits": "^2.0.4",
|
||||||
"readable-stream": "^3.6.0",
|
"readable-stream": "^3.6.0",
|
||||||
@ -11101,7 +11081,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
|
"resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
|
||||||
"integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==",
|
"integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"inherits": "^2.0.3",
|
"inherits": "^2.0.3",
|
||||||
"minimalistic-assert": "^1.0.1"
|
"minimalistic-assert": "^1.0.1"
|
||||||
@ -11136,7 +11115,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
|
||||||
"integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==",
|
"integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"hash.js": "^1.0.3",
|
"hash.js": "^1.0.3",
|
||||||
"minimalistic-assert": "^1.0.0",
|
"minimalistic-assert": "^1.0.0",
|
||||||
@ -12178,7 +12156,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
|
"resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
|
||||||
"integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==",
|
"integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"hash-base": "^3.0.0",
|
"hash-base": "^3.0.0",
|
||||||
"inherits": "^2.0.1",
|
"inherits": "^2.0.1",
|
||||||
@ -12248,15 +12225,13 @@
|
|||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
|
||||||
"integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==",
|
"integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==",
|
||||||
"license": "ISC",
|
"license": "ISC"
|
||||||
"optional": true
|
|
||||||
},
|
},
|
||||||
"node_modules/minimalistic-crypto-utils": {
|
"node_modules/minimalistic-crypto-utils": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
|
||||||
"integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==",
|
"integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==",
|
||||||
"license": "MIT",
|
"license": "MIT"
|
||||||
"optional": true
|
|
||||||
},
|
},
|
||||||
"node_modules/minimatch": {
|
"node_modules/minimatch": {
|
||||||
"version": "3.1.2",
|
"version": "3.1.2",
|
||||||
@ -12348,8 +12323,7 @@
|
|||||||
"version": "2.23.0",
|
"version": "2.23.0",
|
||||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.23.0.tgz",
|
"resolved": "https://registry.npmjs.org/nan/-/nan-2.23.0.tgz",
|
||||||
"integrity": "sha512-1UxuyYGdoQHcGg87Lkqm3FzefucTa0NAiOcuRsDmysep3c1LVCRK2krrUDafMWtjSG04htvAmvg96+SDknOmgQ==",
|
"integrity": "sha512-1UxuyYGdoQHcGg87Lkqm3FzefucTa0NAiOcuRsDmysep3c1LVCRK2krrUDafMWtjSG04htvAmvg96+SDknOmgQ==",
|
||||||
"license": "MIT",
|
"license": "MIT"
|
||||||
"optional": true
|
|
||||||
},
|
},
|
||||||
"node_modules/nanoid": {
|
"node_modules/nanoid": {
|
||||||
"version": "3.3.7",
|
"version": "3.3.7",
|
||||||
@ -13749,7 +13723,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz",
|
||||||
"integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==",
|
"integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"hash-base": "^3.0.0",
|
"hash-base": "^3.0.0",
|
||||||
"inherits": "^2.0.1"
|
"inherits": "^2.0.1"
|
||||||
@ -15045,9 +15018,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/viem": {
|
"node_modules/viem": {
|
||||||
"version": "2.33.2",
|
"version": "2.37.1",
|
||||||
"resolved": "https://registry.npmjs.org/viem/-/viem-2.33.2.tgz",
|
"resolved": "https://registry.npmjs.org/viem/-/viem-2.37.1.tgz",
|
||||||
"integrity": "sha512-/720OaM4dHWs8vXwNpyet+PRERhPaW+n/1UVSCzyb9jkmwwVfaiy/R6YfCFb4v+XXbo8s3Fapa3DM5yCRSkulA==",
|
"integrity": "sha512-IzacdIXYlOvzDJwNKIVa53LP/LaP70qvBGAIoGH6R+n06S/ru/nnQxLNZ6+JImvIcxwNwgAl0jUA6FZEIQQWSw==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "github",
|
"type": "github",
|
||||||
@ -15056,14 +15029,14 @@
|
|||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@noble/curves": "1.9.2",
|
"@noble/curves": "1.9.1",
|
||||||
"@noble/hashes": "1.8.0",
|
"@noble/hashes": "1.8.0",
|
||||||
"@scure/bip32": "1.7.0",
|
"@scure/bip32": "1.7.0",
|
||||||
"@scure/bip39": "1.6.0",
|
"@scure/bip39": "1.6.0",
|
||||||
"abitype": "1.0.8",
|
"abitype": "1.0.8",
|
||||||
"isows": "1.0.7",
|
"isows": "1.0.7",
|
||||||
"ox": "0.8.6",
|
"ox": "0.9.3",
|
||||||
"ws": "8.18.2"
|
"ws": "8.18.3"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"typescript": ">=5.0.4"
|
"typescript": ">=5.0.4"
|
||||||
@ -15074,6 +15047,21 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/viem/node_modules/@noble/curves": {
|
||||||
|
"version": "1.9.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.1.tgz",
|
||||||
|
"integrity": "sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@noble/hashes": "1.8.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^14.21.3 || >=16"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://paulmillr.com/funding/"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/viem/node_modules/@scure/bip32": {
|
"node_modules/viem/node_modules/@scure/bip32": {
|
||||||
"version": "1.7.0",
|
"version": "1.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.7.0.tgz",
|
||||||
@ -15108,9 +15096,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/viem/node_modules/ox": {
|
"node_modules/viem/node_modules/ox": {
|
||||||
"version": "0.8.6",
|
"version": "0.9.3",
|
||||||
"resolved": "https://registry.npmjs.org/ox/-/ox-0.8.6.tgz",
|
"resolved": "https://registry.npmjs.org/ox/-/ox-0.9.3.tgz",
|
||||||
"integrity": "sha512-eiKcgiVVEGDtEpEdFi1EGoVVI48j6icXHce9nFwCNM7CKG3uoCXKdr4TPhS00Iy1TR2aWSF1ltPD0x/YgqIL9w==",
|
"integrity": "sha512-KzyJP+fPV4uhuuqrTZyok4DC7vFzi7HLUFiUNEmpbyh59htKWkOC98IONC1zgXJPbHAhQgqs6B0Z6StCGhmQvg==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "github",
|
"type": "github",
|
||||||
@ -15121,11 +15109,11 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@adraffy/ens-normalize": "^1.11.0",
|
"@adraffy/ens-normalize": "^1.11.0",
|
||||||
"@noble/ciphers": "^1.3.0",
|
"@noble/ciphers": "^1.3.0",
|
||||||
"@noble/curves": "^1.9.1",
|
"@noble/curves": "1.9.1",
|
||||||
"@noble/hashes": "^1.8.0",
|
"@noble/hashes": "^1.8.0",
|
||||||
"@scure/bip32": "^1.7.0",
|
"@scure/bip32": "^1.7.0",
|
||||||
"@scure/bip39": "^1.6.0",
|
"@scure/bip39": "^1.6.0",
|
||||||
"abitype": "^1.0.8",
|
"abitype": "^1.0.9",
|
||||||
"eventemitter3": "5.0.1"
|
"eventemitter3": "5.0.1"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
@ -15137,23 +15125,23 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/viem/node_modules/ws": {
|
"node_modules/viem/node_modules/ox/node_modules/abitype": {
|
||||||
"version": "8.18.2",
|
"version": "1.0.9",
|
||||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.2.tgz",
|
"resolved": "https://registry.npmjs.org/abitype/-/abitype-1.0.9.tgz",
|
||||||
"integrity": "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==",
|
"integrity": "sha512-oN0S++TQmlwWuB+rkA6aiEefLv3SP+2l/tC5mux/TLj6qdA6rF15Vbpex4fHovLsMkwLwTIRj8/Q8vXCS3GfOg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"funding": {
|
||||||
"node": ">=10.0.0"
|
"url": "https://github.com/sponsors/wevm"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"bufferutil": "^4.0.1",
|
"typescript": ">=5.0.4",
|
||||||
"utf-8-validate": ">=5.0.2"
|
"zod": "^3 >=3.22.0"
|
||||||
},
|
},
|
||||||
"peerDependenciesMeta": {
|
"peerDependenciesMeta": {
|
||||||
"bufferutil": {
|
"typescript": {
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
"utf-8-validate": {
|
"zod": {
|
||||||
"optional": true
|
"optional": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -51,6 +51,7 @@
|
|||||||
"@reown/appkit-wallet-button": "^1.7.17",
|
"@reown/appkit-wallet-button": "^1.7.17",
|
||||||
"@tanstack/react-query": "^5.84.1",
|
"@tanstack/react-query": "^5.84.1",
|
||||||
"@waku/sdk": "^0.0.35-67a7287.0",
|
"@waku/sdk": "^0.0.35-67a7287.0",
|
||||||
|
"bitcoinjs-message": "^2.2.0",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"cmdk": "^1.0.0",
|
"cmdk": "^1.0.0",
|
||||||
@ -71,6 +72,7 @@
|
|||||||
"tailwindcss-animate": "^1.0.7",
|
"tailwindcss-animate": "^1.0.7",
|
||||||
"uuid": "^11.1.0",
|
"uuid": "^11.1.0",
|
||||||
"vaul": "^0.9.3",
|
"vaul": "^0.9.3",
|
||||||
|
"viem": "^2.37.1",
|
||||||
"wagmi": "^2.16.1",
|
"wagmi": "^2.16.1",
|
||||||
"zod": "^3.23.8"
|
"zod": "^3.23.8"
|
||||||
},
|
},
|
||||||
|
|||||||
@ -27,7 +27,7 @@ import { useAppKitAccount, useDisconnect } from '@reown/appkit/react';
|
|||||||
import { WalletWizard } from '@/components/ui/wallet-wizard';
|
import { WalletWizard } from '@/components/ui/wallet-wizard';
|
||||||
|
|
||||||
const Header = () => {
|
const Header = () => {
|
||||||
const { currentUser, verificationStatus, isDelegationValid } = useAuth();
|
const { currentUser, verificationStatus, getDelegationStatus } = useAuth();
|
||||||
const { isNetworkConnected, isRefreshing } = useForum();
|
const { isNetworkConnected, isRefreshing } = useForum();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
@ -95,9 +95,9 @@ const Header = () => {
|
|||||||
case 'verified-none':
|
case 'verified-none':
|
||||||
return 'Read-Only Access';
|
return 'Read-Only Access';
|
||||||
case 'verified-basic':
|
case 'verified-basic':
|
||||||
return isDelegationValid() ? 'Full Access' : 'Setup Key';
|
return getDelegationStatus().isValid ? 'Full Access' : 'Setup Key';
|
||||||
case 'verified-owner':
|
case 'verified-owner':
|
||||||
return isDelegationValid() ? 'Premium Access' : 'Setup Key';
|
return getDelegationStatus().isValid ? 'Premium Access' : 'Setup Key';
|
||||||
default:
|
default:
|
||||||
return 'Setup Account';
|
return 'Setup Account';
|
||||||
}
|
}
|
||||||
@ -112,13 +112,13 @@ const Header = () => {
|
|||||||
case 'verified-none':
|
case 'verified-none':
|
||||||
return <CircleSlash className="w-3 h-3" />;
|
return <CircleSlash className="w-3 h-3" />;
|
||||||
case 'verified-basic':
|
case 'verified-basic':
|
||||||
return isDelegationValid() ? (
|
return getDelegationStatus().isValid ? (
|
||||||
<CheckCircle className="w-3 h-3" />
|
<CheckCircle className="w-3 h-3" />
|
||||||
) : (
|
) : (
|
||||||
<Key className="w-3 h-3" />
|
<CheckCircle className="w-3 h-3" />
|
||||||
);
|
);
|
||||||
case 'verified-owner':
|
case 'verified-owner':
|
||||||
return isDelegationValid() ? (
|
return getDelegationStatus().isValid ? (
|
||||||
<CheckCircle className="w-3 h-3" />
|
<CheckCircle className="w-3 h-3" />
|
||||||
) : (
|
) : (
|
||||||
<Key className="w-3 h-3" />
|
<Key className="w-3 h-3" />
|
||||||
@ -137,9 +137,9 @@ const Header = () => {
|
|||||||
case 'verified-none':
|
case 'verified-none':
|
||||||
return 'secondary';
|
return 'secondary';
|
||||||
case 'verified-basic':
|
case 'verified-basic':
|
||||||
return isDelegationValid() ? 'default' : 'outline';
|
return getDelegationStatus().isValid ? 'default' : 'outline';
|
||||||
case 'verified-owner':
|
case 'verified-owner':
|
||||||
return isDelegationValid() ? 'default' : 'outline';
|
return getDelegationStatus().isValid ? 'default' : 'outline';
|
||||||
default:
|
default:
|
||||||
return 'outline';
|
return 'outline';
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,8 +20,7 @@ export function DelegationStep({
|
|||||||
const {
|
const {
|
||||||
currentUser,
|
currentUser,
|
||||||
delegateKey,
|
delegateKey,
|
||||||
isDelegationValid,
|
getDelegationStatus,
|
||||||
delegationTimeRemaining,
|
|
||||||
isAuthenticating,
|
isAuthenticating,
|
||||||
clearDelegation,
|
clearDelegation,
|
||||||
} = useAuth();
|
} = useAuth();
|
||||||
@ -162,23 +161,30 @@ export function DelegationStep({
|
|||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
{/* Status */}
|
{/* Status */}
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
{isDelegationValid() ? (
|
{getDelegationStatus().isValid ? (
|
||||||
<CheckCircle className="h-4 w-4 text-green-500" />
|
<CheckCircle className="h-4 w-4 text-green-500" />
|
||||||
) : (
|
) : (
|
||||||
<AlertCircle className="h-4 w-4 text-yellow-500" />
|
<AlertCircle className="h-4 w-4 text-yellow-500" />
|
||||||
)}
|
)}
|
||||||
<span
|
<span
|
||||||
className={`text-sm font-medium ${
|
className={`text-sm font-medium ${
|
||||||
isDelegationValid() ? 'text-green-400' : 'text-yellow-400'
|
getDelegationStatus().isValid
|
||||||
|
? 'text-green-400'
|
||||||
|
: 'text-yellow-400'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{isDelegationValid() ? 'Delegated' : 'Required'}
|
{getDelegationStatus().isValid ? 'Delegated' : 'Required'}
|
||||||
</span>
|
</span>
|
||||||
{isDelegationValid() && (
|
{getDelegationStatus().isValid && (
|
||||||
<span className="text-xs text-neutral-400">
|
<span className="text-xs text-neutral-400">
|
||||||
{Math.floor(delegationTimeRemaining() / (1000 * 60 * 60))}h{' '}
|
|
||||||
{Math.floor(
|
{Math.floor(
|
||||||
(delegationTimeRemaining() % (1000 * 60 * 60)) / (1000 * 60)
|
(getDelegationStatus().timeRemaining || 0) / (1000 * 60 * 60)
|
||||||
|
)}
|
||||||
|
h{' '}
|
||||||
|
{Math.floor(
|
||||||
|
((getDelegationStatus().timeRemaining || 0) %
|
||||||
|
(1000 * 60 * 60)) /
|
||||||
|
(1000 * 60)
|
||||||
)}
|
)}
|
||||||
m remaining
|
m remaining
|
||||||
</span>
|
</span>
|
||||||
@ -186,7 +192,7 @@ export function DelegationStep({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Duration Selection */}
|
{/* Duration Selection */}
|
||||||
{!isDelegationValid() && (
|
{!getDelegationStatus().isValid && (
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<label className="text-sm font-medium text-neutral-300">
|
<label className="text-sm font-medium text-neutral-300">
|
||||||
Delegation Duration:
|
Delegation Duration:
|
||||||
@ -223,7 +229,7 @@ export function DelegationStep({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Delegated Browser Public Key */}
|
{/* Delegated Browser Public Key */}
|
||||||
{isDelegationValid() && currentUser?.browserPubKey && (
|
{getDelegationStatus().isValid && currentUser?.browserPubKey && (
|
||||||
<div className="text-xs text-neutral-400">
|
<div className="text-xs text-neutral-400">
|
||||||
<div className="font-mono break-all bg-neutral-800 p-2 rounded">
|
<div className="font-mono break-all bg-neutral-800 p-2 rounded">
|
||||||
{currentUser.browserPubKey}
|
{currentUser.browserPubKey}
|
||||||
@ -239,7 +245,7 @@ export function DelegationStep({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Delete Button for Active Delegations */}
|
{/* Delete Button for Active Delegations */}
|
||||||
{isDelegationValid() && (
|
{getDelegationStatus().isValid && (
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<Button
|
<Button
|
||||||
onClick={clearDelegation}
|
onClick={clearDelegation}
|
||||||
@ -257,7 +263,7 @@ export function DelegationStep({
|
|||||||
|
|
||||||
{/* Action Buttons */}
|
{/* Action Buttons */}
|
||||||
<div className="mt-auto space-y-2">
|
<div className="mt-auto space-y-2">
|
||||||
{isDelegationValid() ? (
|
{getDelegationStatus().isValid ? (
|
||||||
<Button
|
<Button
|
||||||
onClick={handleComplete}
|
onClick={handleComplete}
|
||||||
className="w-full bg-green-600 hover:bg-green-700 text-white"
|
className="w-full bg-green-600 hover:bg-green-700 text-white"
|
||||||
|
|||||||
@ -28,7 +28,8 @@ export function WalletWizard({
|
|||||||
}: WalletWizardProps) {
|
}: WalletWizardProps) {
|
||||||
const [currentStep, setCurrentStep] = React.useState<WizardStep>(1);
|
const [currentStep, setCurrentStep] = React.useState<WizardStep>(1);
|
||||||
const [isLoading, setIsLoading] = React.useState(false);
|
const [isLoading, setIsLoading] = React.useState(false);
|
||||||
const { isAuthenticated, verificationStatus, isDelegationValid } = useAuth();
|
const { isAuthenticated, verificationStatus, getDelegationStatus } =
|
||||||
|
useAuth();
|
||||||
const hasInitialized = React.useRef(false);
|
const hasInitialized = React.useRef(false);
|
||||||
|
|
||||||
// Reset wizard when opened and determine starting step
|
// Reset wizard when opened and determine starting step
|
||||||
@ -48,7 +49,7 @@ export function WalletWizard({
|
|||||||
(verificationStatus === 'verified-owner' ||
|
(verificationStatus === 'verified-owner' ||
|
||||||
verificationStatus === 'verified-basic' ||
|
verificationStatus === 'verified-basic' ||
|
||||||
verificationStatus === 'verified-none') &&
|
verificationStatus === 'verified-none') &&
|
||||||
!isDelegationValid()
|
!getDelegationStatus().isValid
|
||||||
) {
|
) {
|
||||||
setCurrentStep(3); // Start at delegation step if verified but no valid delegation
|
setCurrentStep(3); // Start at delegation step if verified but no valid delegation
|
||||||
} else {
|
} else {
|
||||||
@ -59,7 +60,7 @@ export function WalletWizard({
|
|||||||
} else if (!open) {
|
} else if (!open) {
|
||||||
hasInitialized.current = false;
|
hasInitialized.current = false;
|
||||||
}
|
}
|
||||||
}, [open, isAuthenticated, verificationStatus, isDelegationValid]);
|
}, [open, isAuthenticated, verificationStatus, getDelegationStatus]);
|
||||||
|
|
||||||
const handleStepComplete = (step: WizardStep) => {
|
const handleStepComplete = (step: WizardStep) => {
|
||||||
if (step < 3) {
|
if (step < 3) {
|
||||||
@ -100,7 +101,7 @@ export function WalletWizard({
|
|||||||
verificationStatus !== 'verified-none')
|
verificationStatus !== 'verified-none')
|
||||||
)
|
)
|
||||||
return 'disabled';
|
return 'disabled';
|
||||||
if (isDelegationValid()) return 'complete';
|
if (getDelegationStatus().isValid) return 'complete';
|
||||||
return 'current';
|
return 'current';
|
||||||
}
|
}
|
||||||
return 'disabled';
|
return 'disabled';
|
||||||
|
|||||||
@ -3,7 +3,11 @@ import { useToast } from '@/components/ui/use-toast';
|
|||||||
import { OpchanMessage } from '@/types/forum';
|
import { OpchanMessage } from '@/types/forum';
|
||||||
import { User, EVerificationStatus, DisplayPreference } from '@/types/identity';
|
import { User, EVerificationStatus, DisplayPreference } from '@/types/identity';
|
||||||
import { WalletManager } from '@/lib/wallet';
|
import { WalletManager } from '@/lib/wallet';
|
||||||
import { DelegationManager, DelegationDuration, DelegationFullStatus } from '@/lib/delegation';
|
import {
|
||||||
|
DelegationManager,
|
||||||
|
DelegationDuration,
|
||||||
|
DelegationFullStatus,
|
||||||
|
} from '@/lib/delegation';
|
||||||
import { useAppKitAccount, useDisconnect, modal } from '@reown/appkit/react';
|
import { useAppKitAccount, useDisconnect, modal } from '@reown/appkit/react';
|
||||||
|
|
||||||
export type VerificationStatus =
|
export type VerificationStatus =
|
||||||
@ -25,7 +29,7 @@ interface AuthContextType {
|
|||||||
getDelegationStatus: () => DelegationFullStatus;
|
getDelegationStatus: () => DelegationFullStatus;
|
||||||
clearDelegation: () => void;
|
clearDelegation: () => void;
|
||||||
signMessage: (message: OpchanMessage) => Promise<OpchanMessage | null>;
|
signMessage: (message: OpchanMessage) => Promise<OpchanMessage | null>;
|
||||||
verifyMessage: (message: OpchanMessage) => boolean;
|
verifyMessage: (message: OpchanMessage) => Promise<boolean>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const AuthContext = createContext<AuthContextType | undefined>(undefined);
|
const AuthContext = createContext<AuthContextType | undefined>(undefined);
|
||||||
@ -170,7 +174,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
|
|||||||
user.address,
|
user.address,
|
||||||
user.walletType,
|
user.walletType,
|
||||||
duration,
|
duration,
|
||||||
(message) => walletManager.signMessage(message)
|
message => walletManager.signMessage(message)
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(
|
console.error(
|
||||||
@ -447,7 +451,10 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getDelegationStatus = (): DelegationFullStatus => {
|
const getDelegationStatus = (): DelegationFullStatus => {
|
||||||
return delegationManager.getStatus(currentUser?.address, currentUser?.walletType);
|
return delegationManager.getStatus(
|
||||||
|
currentUser?.address,
|
||||||
|
currentUser?.walletType
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const clearDelegation = (): void => {
|
const clearDelegation = (): void => {
|
||||||
@ -477,8 +484,8 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
|
|||||||
): Promise<OpchanMessage | null> => {
|
): Promise<OpchanMessage | null> => {
|
||||||
return delegationManager.signMessage(message);
|
return delegationManager.signMessage(message);
|
||||||
},
|
},
|
||||||
verifyMessage: (message: OpchanMessage): boolean => {
|
verifyMessage: async (message: OpchanMessage): Promise<boolean> => {
|
||||||
return delegationManager.verify(message);
|
return await delegationManager.verify(message);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -105,10 +105,11 @@ export function ForumProvider({ children }: { children: React.ReactNode }) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Transform message cache data to the expected types
|
// Transform message cache data to the expected types
|
||||||
const updateStateFromCache = useCallback(() => {
|
const updateStateFromCache = useCallback(async () => {
|
||||||
// Use the verifyMessage function from delegationManager if available
|
// Use the verifyMessage function from delegationManager if available
|
||||||
const verifyFn = isAuthenticated
|
const verifyFn = isAuthenticated
|
||||||
? (message: OpchanMessage) => delegationManager.verifyMessage(message)
|
? async (message: OpchanMessage) =>
|
||||||
|
await delegationManager.verify(message)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
// Build user verification status for relevance calculation
|
// Build user verification status for relevance calculation
|
||||||
@ -162,7 +163,7 @@ export function ForumProvider({ children }: { children: React.ReactNode }) {
|
|||||||
relevanceCalculator.buildUserVerificationStatus(allUsers);
|
relevanceCalculator.buildUserVerificationStatus(allUsers);
|
||||||
|
|
||||||
// Transform data with relevance calculation (initial pass)
|
// Transform data with relevance calculation (initial pass)
|
||||||
const { cells, posts, comments } = getDataFromCache(
|
const { cells, posts, comments } = await getDataFromCache(
|
||||||
verifyFn,
|
verifyFn,
|
||||||
initialStatus
|
initialStatus
|
||||||
);
|
);
|
||||||
@ -208,7 +209,7 @@ export function ForumProvider({ children }: { children: React.ReactNode }) {
|
|||||||
});
|
});
|
||||||
const enrichedStatus =
|
const enrichedStatus =
|
||||||
relevanceCalculator.buildUserVerificationStatus(enrichedUsers);
|
relevanceCalculator.buildUserVerificationStatus(enrichedUsers);
|
||||||
const transformed = getDataFromCache(verifyFn, enrichedStatus);
|
const transformed = await getDataFromCache(verifyFn, enrichedStatus);
|
||||||
setCells(transformed.cells);
|
setCells(transformed.cells);
|
||||||
setPosts(transformed.posts);
|
setPosts(transformed.posts);
|
||||||
setComments(transformed.comments);
|
setComments(transformed.comments);
|
||||||
@ -220,7 +221,7 @@ export function ForumProvider({ children }: { children: React.ReactNode }) {
|
|||||||
setIsRefreshing(true);
|
setIsRefreshing(true);
|
||||||
try {
|
try {
|
||||||
// SDS handles message syncing automatically, just update UI
|
// SDS handles message syncing automatically, just update UI
|
||||||
updateStateFromCache();
|
await updateStateFromCache();
|
||||||
toast({
|
toast({
|
||||||
title: 'Data Refreshed',
|
title: 'Data Refreshed',
|
||||||
description: 'Your view has been updated.',
|
description: 'Your view has been updated.',
|
||||||
|
|||||||
@ -34,8 +34,9 @@ export const useDelegation = () => {
|
|||||||
hasDelegation: status.hasDelegation,
|
hasDelegation: status.hasDelegation,
|
||||||
isValid: status.isValid,
|
isValid: status.isValid,
|
||||||
timeRemaining: status.timeRemaining,
|
timeRemaining: status.timeRemaining,
|
||||||
expiresAt:
|
expiresAt: status.timeRemaining
|
||||||
status.timeRemaining ? new Date(Date.now() + status.timeRemaining) : undefined,
|
? new Date(Date.now() + status.timeRemaining)
|
||||||
|
: undefined,
|
||||||
publicKey: status.publicKey,
|
publicKey: status.publicKey,
|
||||||
address: status.address,
|
address: status.address,
|
||||||
walletType: status.walletType,
|
walletType: status.walletType,
|
||||||
|
|||||||
@ -12,32 +12,32 @@ export const useMessageSigning = () => {
|
|||||||
const {
|
const {
|
||||||
signMessage: contextSignMessage,
|
signMessage: contextSignMessage,
|
||||||
verifyMessage: contextVerifyMessage,
|
verifyMessage: contextVerifyMessage,
|
||||||
isDelegationValid,
|
getDelegationStatus,
|
||||||
} = context;
|
} = context;
|
||||||
|
|
||||||
const signMessage = useCallback(
|
const signMessage = useCallback(
|
||||||
async (message: OpchanMessage): Promise<OpchanMessage | null> => {
|
async (message: OpchanMessage): Promise<OpchanMessage | null> => {
|
||||||
// Check if we have a valid delegation before attempting to sign
|
// Check if we have a valid delegation before attempting to sign
|
||||||
if (!isDelegationValid()) {
|
if (!getDelegationStatus().isValid) {
|
||||||
console.warn('No valid delegation found. Cannot sign message.');
|
console.warn('No valid delegation found. Cannot sign message.');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return contextSignMessage(message);
|
return contextSignMessage(message);
|
||||||
},
|
},
|
||||||
[contextSignMessage, isDelegationValid]
|
[contextSignMessage, getDelegationStatus]
|
||||||
);
|
);
|
||||||
|
|
||||||
const verifyMessage = useCallback(
|
const verifyMessage = useCallback(
|
||||||
(message: OpchanMessage): boolean => {
|
async (message: OpchanMessage): Promise<boolean> => {
|
||||||
return contextVerifyMessage(message);
|
return await contextVerifyMessage(message);
|
||||||
},
|
},
|
||||||
[contextVerifyMessage]
|
[contextVerifyMessage]
|
||||||
);
|
);
|
||||||
|
|
||||||
const canSignMessages = useCallback((): boolean => {
|
const canSignMessages = useCallback((): boolean => {
|
||||||
return isDelegationValid();
|
return getDelegationStatus().isValid;
|
||||||
}, [isDelegationValid]);
|
}, [getDelegationStatus]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
// Message signing
|
// Message signing
|
||||||
|
|||||||
117
src/lib/delegation/crypto.ts
Normal file
117
src/lib/delegation/crypto.ts
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
import * as ed from '@noble/ed25519';
|
||||||
|
import { sha512 } from '@noble/hashes/sha512';
|
||||||
|
import { bytesToHex, hexToBytes } from '@/lib/utils';
|
||||||
|
import { WalletManager } from '@/lib/wallet';
|
||||||
|
|
||||||
|
// Set up ed25519 with sha512
|
||||||
|
ed.etc.sha512Sync = (...m) => sha512(ed.etc.concatBytes(...m));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delegation-specific cryptographic utilities
|
||||||
|
* Handles all cryptographic operations: key generation, signing, verification
|
||||||
|
*/
|
||||||
|
export class DelegationCrypto {
|
||||||
|
/**
|
||||||
|
* Create a standardized delegation authorization message
|
||||||
|
* @param browserPublicKey - The browser public key being authorized
|
||||||
|
* @param walletAddress - The wallet address doing the authorization
|
||||||
|
* @param expiryTimestamp - When the delegation expires
|
||||||
|
* @param nonce - Unique nonce for replay protection
|
||||||
|
* @returns string - The authorization message to be signed
|
||||||
|
*/
|
||||||
|
static createAuthMessage(
|
||||||
|
browserPublicKey: string,
|
||||||
|
walletAddress: string,
|
||||||
|
expiryTimestamp: number,
|
||||||
|
nonce: string
|
||||||
|
): string {
|
||||||
|
return `I, ${walletAddress}, authorize browser key ${browserPublicKey} until ${expiryTimestamp} (nonce: ${nonce})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify a wallet signature using WalletManager
|
||||||
|
* @param authMessage - The message that was signed
|
||||||
|
* @param walletSignature - The signature to verify
|
||||||
|
* @param walletAddress - The wallet address that signed
|
||||||
|
* @param walletType - The type of wallet
|
||||||
|
* @returns Promise<boolean> - True if signature is valid
|
||||||
|
*/
|
||||||
|
static async verifyWalletSignature(
|
||||||
|
authMessage: string,
|
||||||
|
walletSignature: string,
|
||||||
|
walletAddress: string,
|
||||||
|
walletType: 'bitcoin' | 'ethereum'
|
||||||
|
): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
return await WalletManager.verifySignature(
|
||||||
|
authMessage,
|
||||||
|
walletSignature,
|
||||||
|
walletAddress,
|
||||||
|
walletType
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error verifying wallet signature:', error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a new browser-based keypair for signing messages
|
||||||
|
* @returns Object with public and private keys in hex format
|
||||||
|
*/
|
||||||
|
static generateKeypair(): { publicKey: string; privateKey: string } {
|
||||||
|
const privateKey = ed.utils.randomPrivateKey();
|
||||||
|
const privateKeyHex = bytesToHex(privateKey);
|
||||||
|
|
||||||
|
const publicKey = ed.getPublicKey(privateKey);
|
||||||
|
const publicKeyHex = bytesToHex(publicKey);
|
||||||
|
|
||||||
|
return {
|
||||||
|
privateKey: privateKeyHex,
|
||||||
|
publicKey: publicKeyHex,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sign a raw string message using a private key
|
||||||
|
* @param message - The message to sign
|
||||||
|
* @param privateKeyHex - The private key in hex format
|
||||||
|
* @returns The signature in hex format or null if signing fails
|
||||||
|
*/
|
||||||
|
static signRaw(message: string, privateKeyHex: string): string | null {
|
||||||
|
try {
|
||||||
|
const privateKeyBytes = hexToBytes(privateKeyHex);
|
||||||
|
const messageBytes = new TextEncoder().encode(message);
|
||||||
|
|
||||||
|
const signature = ed.sign(messageBytes, privateKeyBytes);
|
||||||
|
return bytesToHex(signature);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error signing with private key:', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify a signature made with a public key
|
||||||
|
* @param message - The original message
|
||||||
|
* @param signature - The signature to verify in hex format
|
||||||
|
* @param publicKey - The public key in hex format
|
||||||
|
* @returns True if signature is valid
|
||||||
|
*/
|
||||||
|
static verifyRaw(
|
||||||
|
message: string,
|
||||||
|
signature: string,
|
||||||
|
publicKey: string
|
||||||
|
): boolean {
|
||||||
|
try {
|
||||||
|
const messageBytes = new TextEncoder().encode(message);
|
||||||
|
const signatureBytes = hexToBytes(signature);
|
||||||
|
const publicKeyBytes = hexToBytes(publicKey);
|
||||||
|
|
||||||
|
return ed.verify(signatureBytes, messageBytes, publicKeyBytes);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error verifying signature:', error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,15 +1,14 @@
|
|||||||
import * as ed from '@noble/ed25519';
|
|
||||||
import { sha512 } from '@noble/hashes/sha512';
|
|
||||||
import { bytesToHex, hexToBytes } from '@/lib/utils';
|
|
||||||
import { OpchanMessage } from '@/types/forum';
|
import { OpchanMessage } from '@/types/forum';
|
||||||
import { UnsignedMessage } from '@/types/waku';
|
import { UnsignedMessage } from '@/types/waku';
|
||||||
import { DelegationDuration, DelegationInfo, DelegationStatus } from './types';
|
import {
|
||||||
|
DelegationDuration,
|
||||||
|
DelegationInfo,
|
||||||
|
DelegationStatus,
|
||||||
|
DelegationProof,
|
||||||
|
} from './types';
|
||||||
import { DelegationStorage } from './storage';
|
import { DelegationStorage } from './storage';
|
||||||
|
import { DelegationCrypto } from './crypto';
|
||||||
|
|
||||||
// Set up ed25519 with sha512
|
|
||||||
ed.etc.sha512Sync = (...m) => sha512(ed.etc.concatBytes(...m));
|
|
||||||
|
|
||||||
// Enhanced status interface that consolidates all delegation information
|
|
||||||
export interface DelegationFullStatus extends DelegationStatus {
|
export interface DelegationFullStatus extends DelegationStatus {
|
||||||
publicKey?: string;
|
publicKey?: string;
|
||||||
address?: string;
|
address?: string;
|
||||||
@ -17,15 +16,11 @@ export interface DelegationFullStatus extends DelegationStatus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class DelegationManager {
|
export class DelegationManager {
|
||||||
// Duration options in hours
|
|
||||||
private static readonly DURATION_HOURS = {
|
private static readonly DURATION_HOURS = {
|
||||||
'7days': 24 * 7, // 168 hours
|
'7days': 24 * 7,
|
||||||
'30days': 24 * 30, // 720 hours
|
'30days': 24 * 30,
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the number of hours for a given duration
|
|
||||||
*/
|
|
||||||
static getDurationHours(duration: DelegationDuration): number {
|
static getDurationHours(duration: DelegationDuration): number {
|
||||||
return DelegationManager.DURATION_HOURS[duration];
|
return DelegationManager.DURATION_HOURS[duration];
|
||||||
}
|
}
|
||||||
@ -35,12 +30,7 @@ export class DelegationManager {
|
|||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a complete delegation with a single method call
|
* Create a delegation with cryptographic proof
|
||||||
* @param address - Wallet address to delegate from
|
|
||||||
* @param walletType - Type of wallet (bitcoin/ethereum)
|
|
||||||
* @param duration - How long the delegation should last
|
|
||||||
* @param signFunction - Function to sign the delegation message with the wallet
|
|
||||||
* @returns Promise<boolean> - Success status
|
|
||||||
*/
|
*/
|
||||||
async delegate(
|
async delegate(
|
||||||
address: string,
|
address: string,
|
||||||
@ -49,29 +39,34 @@ export class DelegationManager {
|
|||||||
signFunction: (message: string) => Promise<string>
|
signFunction: (message: string) => Promise<string>
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
// Generate new keypair
|
// Generate browser keypair
|
||||||
const keypair = this.generateKeypair();
|
const keypair = DelegationCrypto.generateKeypair();
|
||||||
|
|
||||||
// Create delegation message with expiry
|
// Create expiry and nonce
|
||||||
const expiryHours = DelegationManager.getDurationHours(duration);
|
const expiryTimestamp =
|
||||||
const expiryTimestamp = Date.now() + expiryHours * 60 * 60 * 1000;
|
Date.now() +
|
||||||
const delegationMessage = this.createDelegationMessage(
|
DelegationManager.getDurationHours(duration) * 60 * 60 * 1000;
|
||||||
|
const nonce = crypto.randomUUID();
|
||||||
|
|
||||||
|
// Create and sign authorization message
|
||||||
|
const authMessage = DelegationCrypto.createAuthMessage(
|
||||||
keypair.publicKey,
|
keypair.publicKey,
|
||||||
address,
|
address,
|
||||||
expiryTimestamp
|
|
||||||
);
|
|
||||||
|
|
||||||
// Sign the delegation message with wallet
|
|
||||||
const signature = await signFunction(delegationMessage);
|
|
||||||
|
|
||||||
// Create and store the delegation
|
|
||||||
const delegationInfo: DelegationInfo = {
|
|
||||||
signature,
|
|
||||||
expiryTimestamp,
|
expiryTimestamp,
|
||||||
browserPublicKey: keypair.publicKey,
|
nonce
|
||||||
browserPrivateKey: keypair.privateKey,
|
);
|
||||||
|
const walletSignature = await signFunction(authMessage);
|
||||||
|
|
||||||
|
// Store delegation
|
||||||
|
const delegationInfo: DelegationInfo = {
|
||||||
|
authMessage,
|
||||||
|
walletSignature,
|
||||||
|
expiryTimestamp,
|
||||||
walletAddress: address,
|
walletAddress: address,
|
||||||
walletType,
|
walletType,
|
||||||
|
browserPublicKey: keypair.publicKey,
|
||||||
|
browserPrivateKey: keypair.privateKey,
|
||||||
|
nonce,
|
||||||
};
|
};
|
||||||
|
|
||||||
DelegationStorage.store(delegationInfo);
|
DelegationStorage.store(delegationInfo);
|
||||||
@ -83,117 +78,115 @@ export class DelegationManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get comprehensive delegation status
|
* Sign a message with delegated key
|
||||||
* @param currentAddress - Optional address to validate against
|
|
||||||
* @param currentWalletType - Optional wallet type to validate against
|
|
||||||
* @returns Complete delegation status information
|
|
||||||
*/
|
|
||||||
getStatus(
|
|
||||||
currentAddress?: string,
|
|
||||||
currentWalletType?: 'bitcoin' | 'ethereum'
|
|
||||||
): DelegationFullStatus {
|
|
||||||
const delegation = DelegationStorage.retrieve();
|
|
||||||
|
|
||||||
if (!delegation) {
|
|
||||||
return {
|
|
||||||
hasDelegation: false,
|
|
||||||
isValid: false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if delegation has expired
|
|
||||||
const now = Date.now();
|
|
||||||
const hasExpired = now >= delegation.expiryTimestamp;
|
|
||||||
|
|
||||||
// Check address/wallet type matching if provided
|
|
||||||
const addressMatches = !currentAddress || delegation.walletAddress === currentAddress;
|
|
||||||
const walletTypeMatches = !currentWalletType || delegation.walletType === currentWalletType;
|
|
||||||
|
|
||||||
const isValid = !hasExpired && addressMatches && walletTypeMatches;
|
|
||||||
const timeRemaining = Math.max(0, delegation.expiryTimestamp - now);
|
|
||||||
|
|
||||||
return {
|
|
||||||
hasDelegation: true,
|
|
||||||
isValid,
|
|
||||||
timeRemaining: timeRemaining > 0 ? timeRemaining : undefined,
|
|
||||||
publicKey: delegation.browserPublicKey,
|
|
||||||
address: delegation.walletAddress,
|
|
||||||
walletType: delegation.walletType,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clear the stored delegation
|
|
||||||
*/
|
|
||||||
clear(): void {
|
|
||||||
DelegationStorage.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sign a message with the delegated browser key
|
|
||||||
* @param message - Unsigned message to sign
|
|
||||||
* @returns Signed message or null if delegation invalid
|
|
||||||
*/
|
*/
|
||||||
signMessage(message: UnsignedMessage): OpchanMessage | null {
|
signMessage(message: UnsignedMessage): OpchanMessage | null {
|
||||||
const status = this.getStatus();
|
const delegation = DelegationStorage.retrieve();
|
||||||
if (!status.isValid) {
|
if (!delegation || Date.now() >= delegation.expiryTimestamp) {
|
||||||
console.error('No valid key delegation found. Cannot sign message.');
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const delegation = DelegationStorage.retrieve();
|
// Sign message content
|
||||||
if (!delegation) return null;
|
|
||||||
|
|
||||||
// Create the message content to sign (without signature fields)
|
|
||||||
const messageToSign = JSON.stringify({
|
const messageToSign = JSON.stringify({
|
||||||
...message,
|
...message,
|
||||||
signature: undefined,
|
signature: undefined,
|
||||||
browserPubKey: undefined,
|
browserPubKey: undefined,
|
||||||
|
delegationProof: undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
const signature = this.signRaw(messageToSign);
|
const signature = DelegationCrypto.signRaw(
|
||||||
|
messageToSign,
|
||||||
|
delegation.browserPrivateKey
|
||||||
|
);
|
||||||
if (!signature) return null;
|
if (!signature) return null;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...message,
|
...message,
|
||||||
signature,
|
signature,
|
||||||
browserPubKey: delegation.browserPublicKey,
|
browserPubKey: delegation.browserPublicKey,
|
||||||
|
delegationProof: this.createProof(delegation),
|
||||||
} as OpchanMessage;
|
} as OpchanMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verify a message signature
|
* Verify a signed message
|
||||||
* @param message - Signed message to verify
|
|
||||||
* @returns True if signature is valid
|
|
||||||
*/
|
*/
|
||||||
verify(message: OpchanMessage): boolean {
|
async verify(message: OpchanMessage): Promise<boolean> {
|
||||||
// Check for required signature fields
|
// Check required fields
|
||||||
if (!message.signature || !message.browserPubKey) {
|
if (
|
||||||
const messageId = message.id || `${message.type}-${message.timestamp}`;
|
!message.signature ||
|
||||||
console.warn('Message is missing signature information', messageId);
|
!message.browserPubKey ||
|
||||||
|
!message.delegationProof ||
|
||||||
|
!message.author
|
||||||
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reconstruct the original signed content
|
// Verify message signature
|
||||||
const signedContent = JSON.stringify({
|
const signedContent = JSON.stringify({
|
||||||
...message,
|
...message,
|
||||||
signature: undefined,
|
signature: undefined,
|
||||||
browserPubKey: undefined,
|
browserPubKey: undefined,
|
||||||
|
delegationProof: undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Verify the signature
|
if (
|
||||||
const isValid = this.verifyRaw(
|
!DelegationCrypto.verifyRaw(
|
||||||
signedContent,
|
signedContent,
|
||||||
message.signature,
|
message.signature,
|
||||||
message.browserPubKey
|
message.browserPubKey
|
||||||
);
|
)
|
||||||
|
) {
|
||||||
if (!isValid) {
|
return false;
|
||||||
const messageId = message.id || `${message.type}-${message.timestamp}`;
|
|
||||||
console.warn(`Invalid signature for message ${messageId}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return isValid;
|
// Verify delegation proof
|
||||||
|
return await this.verifyProof(
|
||||||
|
message.delegationProof,
|
||||||
|
message.browserPubKey,
|
||||||
|
message.author
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get delegation status
|
||||||
|
*/
|
||||||
|
getStatus(
|
||||||
|
currentAddress?: string,
|
||||||
|
currentWalletType?: 'bitcoin' | 'ethereum'
|
||||||
|
): DelegationFullStatus {
|
||||||
|
const delegation = DelegationStorage.retrieve();
|
||||||
|
if (!delegation) {
|
||||||
|
return { hasDelegation: false, isValid: false };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check validity
|
||||||
|
const now = Date.now();
|
||||||
|
const hasExpired = now >= delegation.expiryTimestamp;
|
||||||
|
const addressMatches =
|
||||||
|
!currentAddress || delegation.walletAddress === currentAddress;
|
||||||
|
const walletTypeMatches =
|
||||||
|
!currentWalletType || delegation.walletType === currentWalletType;
|
||||||
|
const isValid = !hasExpired && addressMatches && walletTypeMatches;
|
||||||
|
|
||||||
|
return {
|
||||||
|
hasDelegation: true,
|
||||||
|
isValid,
|
||||||
|
timeRemaining: isValid
|
||||||
|
? Math.max(0, delegation.expiryTimestamp - now)
|
||||||
|
: undefined,
|
||||||
|
publicKey: delegation.browserPublicKey,
|
||||||
|
address: delegation.walletAddress,
|
||||||
|
walletType: delegation.walletType,
|
||||||
|
proof: isValid ? this.createProof(delegation) : undefined,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear stored delegation
|
||||||
|
*/
|
||||||
|
clear(): void {
|
||||||
|
DelegationStorage.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@ -201,69 +194,53 @@ export class DelegationManager {
|
|||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a new browser-based keypair for signing messages
|
* Create delegation proof from stored info
|
||||||
*/
|
*/
|
||||||
private generateKeypair(): { publicKey: string; privateKey: string } {
|
private createProof(delegation: DelegationInfo): DelegationProof {
|
||||||
const privateKey = ed.utils.randomPrivateKey();
|
|
||||||
const privateKeyHex = bytesToHex(privateKey);
|
|
||||||
|
|
||||||
const publicKey = ed.getPublicKey(privateKey);
|
|
||||||
const publicKeyHex = bytesToHex(publicKey);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
privateKey: privateKeyHex,
|
authMessage: delegation.authMessage,
|
||||||
publicKey: publicKeyHex,
|
walletSignature: delegation.walletSignature,
|
||||||
|
expiryTimestamp: delegation.expiryTimestamp,
|
||||||
|
walletAddress: delegation.walletAddress,
|
||||||
|
walletType: delegation.walletType,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a delegation message to be signed by the wallet
|
* Verify delegation proof
|
||||||
*/
|
*/
|
||||||
private createDelegationMessage(
|
private async verifyProof(
|
||||||
browserPublicKey: string,
|
proof: DelegationProof,
|
||||||
walletAddress: string,
|
expectedBrowserKey: string,
|
||||||
expiryTimestamp: number
|
expectedWalletAddress: string
|
||||||
): string {
|
): Promise<boolean> {
|
||||||
return `I, ${walletAddress}, delegate authority to this pubkey: ${browserPublicKey} until ${expiryTimestamp}`;
|
// Basic validation
|
||||||
}
|
if (
|
||||||
|
!proof?.walletAddress ||
|
||||||
/**
|
!proof?.authMessage ||
|
||||||
* Sign a raw string message using the browser-generated private key
|
proof?.expiryTimestamp === undefined ||
|
||||||
*/
|
proof.walletAddress !== expectedWalletAddress ||
|
||||||
private signRaw(message: string): string | null {
|
Date.now() >= proof.expiryTimestamp
|
||||||
const delegation = DelegationStorage.retrieve();
|
) {
|
||||||
if (!delegation) return null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const privateKeyBytes = hexToBytes(delegation.browserPrivateKey);
|
|
||||||
const messageBytes = new TextEncoder().encode(message);
|
|
||||||
|
|
||||||
const signature = ed.sign(messageBytes, privateKeyBytes);
|
|
||||||
return bytesToHex(signature);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error signing with browser key:', error);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Verify a signature made with the browser key
|
|
||||||
*/
|
|
||||||
private verifyRaw(
|
|
||||||
message: string,
|
|
||||||
signature: string,
|
|
||||||
publicKey: string
|
|
||||||
): boolean {
|
|
||||||
try {
|
|
||||||
const messageBytes = new TextEncoder().encode(message);
|
|
||||||
const signatureBytes = hexToBytes(signature);
|
|
||||||
const publicKeyBytes = hexToBytes(publicKey);
|
|
||||||
|
|
||||||
return ed.verify(signatureBytes, messageBytes, publicKeyBytes);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error verifying signature:', error);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Verify auth message format
|
||||||
|
if (
|
||||||
|
!proof.authMessage.includes(expectedWalletAddress) ||
|
||||||
|
!proof.authMessage.includes(expectedBrowserKey) ||
|
||||||
|
!proof.authMessage.includes(proof.expiryTimestamp.toString())
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify wallet signature
|
||||||
|
return await DelegationCrypto.verifyWalletSignature(
|
||||||
|
proof.authMessage,
|
||||||
|
proof.walletSignature,
|
||||||
|
proof.walletAddress,
|
||||||
|
proof.walletType
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -8,6 +8,24 @@ export class DelegationStorage {
|
|||||||
* Store delegation information in localStorage
|
* Store delegation information in localStorage
|
||||||
*/
|
*/
|
||||||
static store(delegation: DelegationInfo): void {
|
static store(delegation: DelegationInfo): void {
|
||||||
|
console.log('DelegationStorage.store - storing delegation:', {
|
||||||
|
hasAuthMessage: !!delegation.authMessage,
|
||||||
|
hasWalletSignature: !!delegation.walletSignature,
|
||||||
|
hasExpiryTimestamp: delegation.expiryTimestamp !== undefined,
|
||||||
|
hasWalletAddress: !!delegation.walletAddress,
|
||||||
|
hasWalletType: !!delegation.walletType,
|
||||||
|
hasBrowserPublicKey: !!delegation.browserPublicKey,
|
||||||
|
hasBrowserPrivateKey: !!delegation.browserPrivateKey,
|
||||||
|
hasNonce: !!delegation.nonce,
|
||||||
|
authMessage: delegation.authMessage,
|
||||||
|
walletSignature: delegation.walletSignature,
|
||||||
|
expiryTimestamp: delegation.expiryTimestamp,
|
||||||
|
walletAddress: delegation.walletAddress,
|
||||||
|
walletType: delegation.walletType,
|
||||||
|
browserPublicKey: delegation.browserPublicKey,
|
||||||
|
nonce: delegation.nonce,
|
||||||
|
});
|
||||||
|
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
DelegationStorage.STORAGE_KEY,
|
DelegationStorage.STORAGE_KEY,
|
||||||
JSON.stringify(delegation)
|
JSON.stringify(delegation)
|
||||||
@ -22,7 +40,25 @@ export class DelegationStorage {
|
|||||||
if (!delegationJson) return null;
|
if (!delegationJson) return null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return JSON.parse(delegationJson);
|
const delegation = JSON.parse(delegationJson);
|
||||||
|
console.log('DelegationStorage.retrieve - retrieved delegation:', {
|
||||||
|
hasAuthMessage: !!delegation.authMessage,
|
||||||
|
hasWalletSignature: !!delegation.walletSignature,
|
||||||
|
hasExpiryTimestamp: delegation.expiryTimestamp !== undefined,
|
||||||
|
hasWalletAddress: !!delegation.walletAddress,
|
||||||
|
hasWalletType: !!delegation.walletType,
|
||||||
|
hasBrowserPublicKey: !!delegation.browserPublicKey,
|
||||||
|
hasBrowserPrivateKey: !!delegation.browserPrivateKey,
|
||||||
|
hasNonce: !!delegation.nonce,
|
||||||
|
authMessage: delegation.authMessage,
|
||||||
|
walletSignature: delegation.walletSignature,
|
||||||
|
expiryTimestamp: delegation.expiryTimestamp,
|
||||||
|
walletAddress: delegation.walletAddress,
|
||||||
|
walletType: delegation.walletType,
|
||||||
|
browserPublicKey: delegation.browserPublicKey,
|
||||||
|
nonce: delegation.nonce,
|
||||||
|
});
|
||||||
|
return delegation;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Failed to parse delegation information', e);
|
console.error('Failed to parse delegation information', e);
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@ -1,19 +1,31 @@
|
|||||||
export type DelegationDuration = '7days' | '30days';
|
export type DelegationDuration = '7days' | '30days';
|
||||||
|
|
||||||
export interface DelegationSignature {
|
/**
|
||||||
signature: string; // Signature from wallet
|
* Cryptographic proof that a wallet authorized a browser key
|
||||||
|
*/
|
||||||
|
export interface DelegationProof {
|
||||||
|
authMessage: string; // "I authorize browser key: 0xabc... until 1640995200"
|
||||||
|
walletSignature: string; // Wallet's signature of authMessage
|
||||||
expiryTimestamp: number; // When this delegation expires
|
expiryTimestamp: number; // When this delegation expires
|
||||||
browserPublicKey: string; // Browser-generated public key that was delegated to
|
|
||||||
walletAddress: string; // Wallet address that signed the delegation
|
walletAddress: string; // Wallet address that signed the delegation
|
||||||
walletType: 'bitcoin' | 'ethereum'; // Type of wallet that created the delegation
|
walletType: 'bitcoin' | 'ethereum'; // Type of wallet that created the delegation
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DelegationInfo extends DelegationSignature {
|
/**
|
||||||
browserPrivateKey: string;
|
* Complete delegation information including private key (stored locally)
|
||||||
|
*/
|
||||||
|
export interface DelegationInfo extends DelegationProof {
|
||||||
|
browserPublicKey: string; // Browser-generated public key
|
||||||
|
browserPrivateKey: string; // Browser-generated private key (never shared)
|
||||||
|
nonce: string; // Unique nonce to prevent replay attacks
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Status of current delegation
|
||||||
|
*/
|
||||||
export interface DelegationStatus {
|
export interface DelegationStatus {
|
||||||
hasDelegation: boolean;
|
hasDelegation: boolean;
|
||||||
isValid: boolean;
|
isValid: boolean;
|
||||||
timeRemaining?: number;
|
timeRemaining?: number;
|
||||||
|
proof?: DelegationProof; // Include proof for verification
|
||||||
}
|
}
|
||||||
|
|||||||
@ -91,7 +91,9 @@ export class ForumActions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateStateFromCache();
|
updateStateFromCache();
|
||||||
const transformedPost = transformPost(result.message! as PostMessage);
|
const transformedPost = await transformPost(
|
||||||
|
result.message! as PostMessage
|
||||||
|
);
|
||||||
if (!transformedPost) {
|
if (!transformedPost) {
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
@ -166,7 +168,7 @@ export class ForumActions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateStateFromCache();
|
updateStateFromCache();
|
||||||
const transformedComment = transformComment(
|
const transformedComment = await transformComment(
|
||||||
result.message! as CommentMessage
|
result.message! as CommentMessage
|
||||||
);
|
);
|
||||||
if (!transformedComment) {
|
if (!transformedComment) {
|
||||||
@ -223,7 +225,9 @@ export class ForumActions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateStateFromCache();
|
updateStateFromCache();
|
||||||
const transformedCell = transformCell(result.message! as CellMessage);
|
const transformedCell = await transformCell(
|
||||||
|
result.message! as CellMessage
|
||||||
|
);
|
||||||
if (!transformedCell) {
|
if (!transformedCell) {
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
|
|||||||
@ -13,12 +13,12 @@ import { MessageValidator } from '@/lib/utils/MessageValidator';
|
|||||||
// Global validator instance for transformers
|
// Global validator instance for transformers
|
||||||
const messageValidator = new MessageValidator();
|
const messageValidator = new MessageValidator();
|
||||||
|
|
||||||
export const transformCell = (
|
export const transformCell = async (
|
||||||
cellMessage: CellMessage,
|
cellMessage: CellMessage,
|
||||||
_verifyMessage?: unknown, // Deprecated parameter, kept for compatibility
|
_verifyMessage?: unknown, // Deprecated parameter, kept for compatibility
|
||||||
userVerificationStatus?: UserVerificationStatus,
|
userVerificationStatus?: UserVerificationStatus,
|
||||||
posts?: Post[]
|
posts?: Post[]
|
||||||
): Cell | null => {
|
): Promise<Cell | null> => {
|
||||||
// MANDATORY: All messages must have valid signatures
|
// MANDATORY: All messages must have valid signatures
|
||||||
// Since CellMessage extends BaseMessage, it already has required signature fields
|
// Since CellMessage extends BaseMessage, it already has required signature fields
|
||||||
// But we still need to verify the signature cryptographically
|
// But we still need to verify the signature cryptographically
|
||||||
@ -30,7 +30,8 @@ export const transformCell = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Verify signature using the message validator's crypto service
|
// Verify signature using the message validator's crypto service
|
||||||
const validationReport = messageValidator.getValidationReport(cellMessage);
|
const validationReport =
|
||||||
|
await messageValidator.getValidationReport(cellMessage);
|
||||||
if (!validationReport.hasValidSignature) {
|
if (!validationReport.hasValidSignature) {
|
||||||
console.warn(
|
console.warn(
|
||||||
`Cell message ${cellMessage.id} failed signature validation:`,
|
`Cell message ${cellMessage.id} failed signature validation:`,
|
||||||
@ -78,11 +79,11 @@ export const transformCell = (
|
|||||||
return transformedCell;
|
return transformedCell;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const transformPost = (
|
export const transformPost = async (
|
||||||
postMessage: PostMessage,
|
postMessage: PostMessage,
|
||||||
_verifyMessage?: unknown, // Deprecated parameter, kept for compatibility
|
_verifyMessage?: unknown, // Deprecated parameter, kept for compatibility
|
||||||
userVerificationStatus?: UserVerificationStatus
|
userVerificationStatus?: UserVerificationStatus
|
||||||
): Post | null => {
|
): Promise<Post | null> => {
|
||||||
// MANDATORY: All messages must have valid signatures
|
// MANDATORY: All messages must have valid signatures
|
||||||
if (!postMessage.signature || !postMessage.browserPubKey) {
|
if (!postMessage.signature || !postMessage.browserPubKey) {
|
||||||
console.warn(
|
console.warn(
|
||||||
@ -92,7 +93,8 @@ export const transformPost = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Verify signature using the message validator's crypto service
|
// Verify signature using the message validator's crypto service
|
||||||
const validationReport = messageValidator.getValidationReport(postMessage);
|
const validationReport =
|
||||||
|
await messageValidator.getValidationReport(postMessage);
|
||||||
if (!validationReport.hasValidSignature) {
|
if (!validationReport.hasValidSignature) {
|
||||||
console.warn(
|
console.warn(
|
||||||
`Post message ${postMessage.id} failed signature validation:`,
|
`Post message ${postMessage.id} failed signature validation:`,
|
||||||
@ -105,23 +107,29 @@ export const transformPost = (
|
|||||||
vote => vote.targetId === postMessage.id
|
vote => vote.targetId === postMessage.id
|
||||||
);
|
);
|
||||||
// MANDATORY: Filter out votes with invalid signatures
|
// MANDATORY: Filter out votes with invalid signatures
|
||||||
const filteredVotes = votes.filter(vote => {
|
const filteredVotes = await Promise.all(
|
||||||
if (!vote.signature || !vote.browserPubKey) {
|
votes.map(async vote => {
|
||||||
console.warn(`Vote ${vote.id} missing signature fields`);
|
if (!vote.signature || !vote.browserPubKey) {
|
||||||
return false;
|
console.warn(`Vote ${vote.id} missing signature fields`);
|
||||||
}
|
return null;
|
||||||
const voteValidation = messageValidator.getValidationReport(vote);
|
}
|
||||||
if (!voteValidation.hasValidSignature) {
|
const voteValidation = await messageValidator.getValidationReport(vote);
|
||||||
console.warn(
|
if (!voteValidation.hasValidSignature) {
|
||||||
`Vote ${vote.id} failed signature validation:`,
|
console.warn(
|
||||||
voteValidation.errors
|
`Vote ${vote.id} failed signature validation:`,
|
||||||
);
|
voteValidation.errors
|
||||||
return false;
|
);
|
||||||
}
|
return null;
|
||||||
return true;
|
}
|
||||||
});
|
return vote;
|
||||||
const upvotes = filteredVotes.filter(vote => vote.value === 1);
|
})
|
||||||
const downvotes = filteredVotes.filter(vote => vote.value === -1);
|
).then(votes => votes.filter((vote): vote is VoteMessage => vote !== null));
|
||||||
|
const upvotes = filteredVotes.filter(
|
||||||
|
(vote): vote is VoteMessage => vote !== null && vote.value === 1
|
||||||
|
);
|
||||||
|
const downvotes = filteredVotes.filter(
|
||||||
|
(vote): vote is VoteMessage => vote !== null && vote.value === -1
|
||||||
|
);
|
||||||
|
|
||||||
const modMsg = messageManager.messageCache.moderations[postMessage.id];
|
const modMsg = messageManager.messageCache.moderations[postMessage.id];
|
||||||
const isPostModerated = !!modMsg && modMsg.targetType === 'post';
|
const isPostModerated = !!modMsg && modMsg.targetType === 'post';
|
||||||
@ -171,11 +179,13 @@ export const transformPost = (
|
|||||||
const relevanceCalculator = new RelevanceCalculator();
|
const relevanceCalculator = new RelevanceCalculator();
|
||||||
|
|
||||||
// Get comments for this post
|
// Get comments for this post
|
||||||
const comments = Object.values(messageManager.messageCache.comments)
|
const comments = await Promise.all(
|
||||||
.map(comment =>
|
Object.values(messageManager.messageCache.comments).map(comment =>
|
||||||
transformComment(comment, undefined, userVerificationStatus)
|
transformComment(comment, undefined, userVerificationStatus)
|
||||||
)
|
)
|
||||||
.filter(Boolean) as Comment[];
|
).then(comments =>
|
||||||
|
comments.filter((comment): comment is Comment => comment !== null)
|
||||||
|
);
|
||||||
const postComments = comments.filter(
|
const postComments = comments.filter(
|
||||||
comment => comment.postId === postMessage.id
|
comment => comment.postId === postMessage.id
|
||||||
);
|
);
|
||||||
@ -215,11 +225,11 @@ export const transformPost = (
|
|||||||
return transformedPost;
|
return transformedPost;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const transformComment = (
|
export const transformComment = async (
|
||||||
commentMessage: CommentMessage,
|
commentMessage: CommentMessage,
|
||||||
_verifyMessage?: unknown, // Deprecated parameter, kept for compatibility
|
_verifyMessage?: unknown, // Deprecated parameter, kept for compatibility
|
||||||
userVerificationStatus?: UserVerificationStatus
|
userVerificationStatus?: UserVerificationStatus
|
||||||
): Comment | null => {
|
): Promise<Comment | null> => {
|
||||||
// MANDATORY: All messages must have valid signatures
|
// MANDATORY: All messages must have valid signatures
|
||||||
if (!commentMessage.signature || !commentMessage.browserPubKey) {
|
if (!commentMessage.signature || !commentMessage.browserPubKey) {
|
||||||
console.warn(
|
console.warn(
|
||||||
@ -229,7 +239,8 @@ export const transformComment = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Verify signature using the message validator's crypto service
|
// Verify signature using the message validator's crypto service
|
||||||
const validationReport = messageValidator.getValidationReport(commentMessage);
|
const validationReport =
|
||||||
|
await messageValidator.getValidationReport(commentMessage);
|
||||||
if (!validationReport.hasValidSignature) {
|
if (!validationReport.hasValidSignature) {
|
||||||
console.warn(
|
console.warn(
|
||||||
`Comment message ${commentMessage.id} failed signature validation:`,
|
`Comment message ${commentMessage.id} failed signature validation:`,
|
||||||
@ -241,23 +252,29 @@ export const transformComment = (
|
|||||||
vote => vote.targetId === commentMessage.id
|
vote => vote.targetId === commentMessage.id
|
||||||
);
|
);
|
||||||
// MANDATORY: Filter out votes with invalid signatures
|
// MANDATORY: Filter out votes with invalid signatures
|
||||||
const filteredVotes = votes.filter(vote => {
|
const filteredVotes = await Promise.all(
|
||||||
if (!vote.signature || !vote.browserPubKey) {
|
votes.map(async vote => {
|
||||||
console.warn(`Vote ${vote.id} missing signature fields`);
|
if (!vote.signature || !vote.browserPubKey) {
|
||||||
return false;
|
console.warn(`Vote ${vote.id} missing signature fields`);
|
||||||
}
|
return null;
|
||||||
const voteValidation = messageValidator.getValidationReport(vote);
|
}
|
||||||
if (!voteValidation.hasValidSignature) {
|
const voteValidation = await messageValidator.getValidationReport(vote);
|
||||||
console.warn(
|
if (!voteValidation.hasValidSignature) {
|
||||||
`Vote ${vote.id} failed signature validation:`,
|
console.warn(
|
||||||
voteValidation.errors
|
`Vote ${vote.id} failed signature validation:`,
|
||||||
);
|
voteValidation.errors
|
||||||
return false;
|
);
|
||||||
}
|
return null;
|
||||||
return true;
|
}
|
||||||
});
|
return vote;
|
||||||
const upvotes = filteredVotes.filter(vote => vote.value === 1);
|
})
|
||||||
const downvotes = filteredVotes.filter(vote => vote.value === -1);
|
).then(votes => votes.filter((vote): vote is typeof vote => vote !== null));
|
||||||
|
const upvotes = filteredVotes.filter(
|
||||||
|
(vote): vote is VoteMessage => vote !== null && vote.value === 1
|
||||||
|
);
|
||||||
|
const downvotes = filteredVotes.filter(
|
||||||
|
(vote): vote is VoteMessage => vote !== null && vote.value === -1
|
||||||
|
);
|
||||||
|
|
||||||
const modMsg = messageManager.messageCache.moderations[commentMessage.id];
|
const modMsg = messageManager.messageCache.moderations[commentMessage.id];
|
||||||
const isCommentModerated = !!modMsg && modMsg.targetType === 'comment';
|
const isCommentModerated = !!modMsg && modMsg.targetType === 'comment';
|
||||||
@ -307,7 +324,7 @@ export const transformComment = (
|
|||||||
|
|
||||||
const relevanceResult = relevanceCalculator.calculateCommentScore(
|
const relevanceResult = relevanceCalculator.calculateCommentScore(
|
||||||
transformedComment,
|
transformedComment,
|
||||||
filteredVotes,
|
filteredVotes.filter((vote): vote is VoteMessage => vote !== null),
|
||||||
userVerificationStatus
|
userVerificationStatus
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -321,10 +338,10 @@ export const transformComment = (
|
|||||||
return transformedComment;
|
return transformedComment;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const transformVote = (
|
export const transformVote = async (
|
||||||
voteMessage: VoteMessage,
|
voteMessage: VoteMessage,
|
||||||
_verifyMessage?: unknown // Deprecated parameter, kept for compatibility
|
_verifyMessage?: unknown // Deprecated parameter, kept for compatibility
|
||||||
): VoteMessage | null => {
|
): Promise<VoteMessage | null> => {
|
||||||
// MANDATORY: All messages must have valid signatures
|
// MANDATORY: All messages must have valid signatures
|
||||||
if (!voteMessage.signature || !voteMessage.browserPubKey) {
|
if (!voteMessage.signature || !voteMessage.browserPubKey) {
|
||||||
console.warn(
|
console.warn(
|
||||||
@ -334,7 +351,8 @@ export const transformVote = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Verify signature using the message validator's crypto service
|
// Verify signature using the message validator's crypto service
|
||||||
const validationReport = messageValidator.getValidationReport(voteMessage);
|
const validationReport =
|
||||||
|
await messageValidator.getValidationReport(voteMessage);
|
||||||
if (!validationReport.hasValidSignature) {
|
if (!validationReport.hasValidSignature) {
|
||||||
console.warn(
|
console.warn(
|
||||||
`Vote message ${voteMessage.id} failed signature validation:`,
|
`Vote message ${voteMessage.id} failed signature validation:`,
|
||||||
@ -346,24 +364,32 @@ export const transformVote = (
|
|||||||
return voteMessage;
|
return voteMessage;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getDataFromCache = (
|
export const getDataFromCache = async (
|
||||||
_verifyMessage?: unknown, // Deprecated parameter, kept for compatibility
|
_verifyMessage?: unknown, // Deprecated parameter, kept for compatibility
|
||||||
userVerificationStatus?: UserVerificationStatus
|
userVerificationStatus?: UserVerificationStatus
|
||||||
) => {
|
): Promise<{ cells: Cell[]; posts: Post[]; comments: Comment[] }> => {
|
||||||
// First transform posts and comments to get relevance scores
|
// First transform posts and comments to get relevance scores
|
||||||
// All validation is now handled internally by the transform functions
|
// All validation is now handled internally by the transform functions
|
||||||
const posts = Object.values(messageManager.messageCache.posts)
|
const posts = await Promise.all(
|
||||||
.map(post => transformPost(post, undefined, userVerificationStatus))
|
Object.values(messageManager.messageCache.posts).map(post =>
|
||||||
.filter(Boolean) as Post[];
|
transformPost(post, undefined, userVerificationStatus)
|
||||||
|
)
|
||||||
|
).then(posts => posts.filter((post): post is Post => post !== null));
|
||||||
|
|
||||||
const comments = Object.values(messageManager.messageCache.comments)
|
const comments = await Promise.all(
|
||||||
.map(c => transformComment(c, undefined, userVerificationStatus))
|
Object.values(messageManager.messageCache.comments).map(c =>
|
||||||
.filter(Boolean) as Comment[];
|
transformComment(c, undefined, userVerificationStatus)
|
||||||
|
)
|
||||||
|
).then(comments =>
|
||||||
|
comments.filter((comment): comment is Comment => comment !== null)
|
||||||
|
);
|
||||||
|
|
||||||
// Then transform cells with posts for relevance calculation
|
// Then transform cells with posts for relevance calculation
|
||||||
const cells = Object.values(messageManager.messageCache.cells)
|
const cells = await Promise.all(
|
||||||
.map(cell => transformCell(cell, undefined, userVerificationStatus, posts))
|
Object.values(messageManager.messageCache.cells).map(cell =>
|
||||||
.filter(Boolean) as Cell[];
|
transformCell(cell, undefined, userVerificationStatus, posts)
|
||||||
|
)
|
||||||
|
).then(cells => cells.filter((cell): cell is Cell => cell !== null));
|
||||||
|
|
||||||
return { cells, posts, comments };
|
return { cells, posts, comments };
|
||||||
};
|
};
|
||||||
|
|||||||
@ -11,7 +11,7 @@ export interface MessageResult {
|
|||||||
|
|
||||||
export interface MessageServiceInterface {
|
export interface MessageServiceInterface {
|
||||||
sendMessage(message: UnsignedMessage): Promise<MessageResult>;
|
sendMessage(message: UnsignedMessage): Promise<MessageResult>;
|
||||||
verifyMessage(message: OpchanMessage): boolean;
|
verifyMessage(message: OpchanMessage): Promise<boolean>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class MessageService implements MessageServiceInterface {
|
export class MessageService implements MessageServiceInterface {
|
||||||
@ -26,13 +26,12 @@ export class MessageService implements MessageServiceInterface {
|
|||||||
*/
|
*/
|
||||||
async sendMessage(message: UnsignedMessage): Promise<MessageResult> {
|
async sendMessage(message: UnsignedMessage): Promise<MessageResult> {
|
||||||
try {
|
try {
|
||||||
const signedMessage =
|
const signedMessage = this.delegationManager.signMessage(message);
|
||||||
this.delegationManager.signMessageWithDelegatedKey(message);
|
|
||||||
|
|
||||||
if (!signedMessage) {
|
if (!signedMessage) {
|
||||||
// Check if delegation exists but is expired
|
// Check if delegation exists but is expired
|
||||||
const isDelegationExpired =
|
const delegationStatus = this.delegationManager.getStatus();
|
||||||
this.delegationManager.isDelegationValid() === false;
|
const isDelegationExpired = !delegationStatus.isValid;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
@ -81,7 +80,7 @@ export class MessageService implements MessageServiceInterface {
|
|||||||
/**
|
/**
|
||||||
* Verify a message signature
|
* Verify a message signature
|
||||||
*/
|
*/
|
||||||
verifyMessage(message: OpchanMessage): boolean {
|
async verifyMessage(message: OpchanMessage): Promise<boolean> {
|
||||||
return this.delegationManager.verifyMessage(message);
|
return await this.delegationManager.verify(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,14 +27,14 @@ export class MessageValidator {
|
|||||||
/**
|
/**
|
||||||
* Validates that a message has required signature fields and valid signature
|
* Validates that a message has required signature fields and valid signature
|
||||||
*/
|
*/
|
||||||
isValidMessage(message: unknown): message is OpchanMessage {
|
async isValidMessage(message: unknown): Promise<boolean> {
|
||||||
// Check basic structure
|
// Check basic structure
|
||||||
if (!this.hasRequiredFields(message)) {
|
if (!this.hasRequiredFields(message)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify signature - we know it's safe to cast here since hasRequiredFields passed
|
// Verify signature and delegation proof - we know it's safe to cast here since hasRequiredFields passed
|
||||||
return this.delegationManager.verifyMessage(message as OpchanMessage);
|
return await this.delegationManager.verify(message as OpchanMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -108,7 +108,7 @@ export class MessageValidator {
|
|||||||
/**
|
/**
|
||||||
* Validates a batch of messages and returns only valid ones
|
* Validates a batch of messages and returns only valid ones
|
||||||
*/
|
*/
|
||||||
filterValidMessages(messages: unknown[]): OpchanMessage[] {
|
async filterValidMessages(messages: unknown[]): Promise<OpchanMessage[]> {
|
||||||
const validMessages: OpchanMessage[] = [];
|
const validMessages: OpchanMessage[] = [];
|
||||||
const invalidCount = {
|
const invalidCount = {
|
||||||
missingFields: 0,
|
missingFields: 0,
|
||||||
@ -123,7 +123,7 @@ export class MessageValidator {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.delegationManager.verifyMessage(message as OpchanMessage)) {
|
if (!(await this.delegationManager.verify(message as OpchanMessage))) {
|
||||||
invalidCount.invalidSignature++;
|
invalidCount.invalidSignature++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -158,7 +158,7 @@ export class MessageValidator {
|
|||||||
/**
|
/**
|
||||||
* Strict validation that throws errors for invalid messages
|
* Strict validation that throws errors for invalid messages
|
||||||
*/
|
*/
|
||||||
validateMessage(message: unknown): OpchanMessage {
|
async validateMessage(message: unknown): Promise<OpchanMessage> {
|
||||||
if (!this.hasRequiredFields(message)) {
|
if (!this.hasRequiredFields(message)) {
|
||||||
const partialMsg = message as PartialMessage;
|
const partialMsg = message as PartialMessage;
|
||||||
throw new Error(
|
throw new Error(
|
||||||
@ -166,7 +166,7 @@ export class MessageValidator {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.delegationManager.verifyMessage(message as OpchanMessage)) {
|
if (!(await this.delegationManager.verify(message as OpchanMessage))) {
|
||||||
const partialMsg = message as PartialMessage;
|
const partialMsg = message as PartialMessage;
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Message validation failed: Invalid signature (messageId: ${partialMsg?.id})`
|
`Message validation failed: Invalid signature (messageId: ${partialMsg?.id})`
|
||||||
@ -223,12 +223,12 @@ export class MessageValidator {
|
|||||||
/**
|
/**
|
||||||
* Creates a validation report for debugging
|
* Creates a validation report for debugging
|
||||||
*/
|
*/
|
||||||
getValidationReport(message: unknown): {
|
async getValidationReport(message: unknown): Promise<{
|
||||||
isValid: boolean;
|
isValid: boolean;
|
||||||
hasRequiredFields: boolean;
|
hasRequiredFields: boolean;
|
||||||
hasValidSignature: boolean;
|
hasValidSignature: boolean;
|
||||||
errors: string[];
|
errors: string[];
|
||||||
} {
|
}> {
|
||||||
const errors: string[] = [];
|
const errors: string[] = [];
|
||||||
let hasRequiredFields = false;
|
let hasRequiredFields = false;
|
||||||
let hasValidSignature = false;
|
let hasValidSignature = false;
|
||||||
@ -242,7 +242,7 @@ export class MessageValidator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (hasRequiredFields) {
|
if (hasRequiredFields) {
|
||||||
hasValidSignature = this.delegationManager.verifyMessage(
|
hasValidSignature = await this.delegationManager.verify(
|
||||||
message as OpchanMessage
|
message as OpchanMessage
|
||||||
);
|
);
|
||||||
if (!hasValidSignature) {
|
if (!hasValidSignature) {
|
||||||
@ -271,11 +271,10 @@ export const messageValidator = new MessageValidator();
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Type guard function for convenient usage
|
* Type guard function for convenient usage
|
||||||
|
* Note: This is not a true type guard since it's async
|
||||||
*/
|
*/
|
||||||
export function isValidOpchanMessage(
|
export async function isValidOpchanMessage(message: unknown): Promise<boolean> {
|
||||||
message: unknown
|
return await messageValidator.isValidMessage(message);
|
||||||
): message is OpchanMessage {
|
|
||||||
return messageValidator.isValidMessage(message);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -4,11 +4,11 @@ import { MessageType } from '../../types/waku';
|
|||||||
* Content topics for different message types
|
* Content topics for different message types
|
||||||
*/
|
*/
|
||||||
export const CONTENT_TOPICS: Record<MessageType, string> = {
|
export const CONTENT_TOPICS: Record<MessageType, string> = {
|
||||||
[MessageType.CELL]: '/opchan-sds/1/cell/proto',
|
[MessageType.CELL]: '/opchan-sds-ab/1/cell/proto',
|
||||||
[MessageType.POST]: '/opchan-sds/1/post/proto',
|
[MessageType.POST]: '/opchan-sds-ab/1/post/proto',
|
||||||
[MessageType.COMMENT]: '/opchan-sds/1/comment/proto',
|
[MessageType.COMMENT]: '/opchan-ab-xyz/1/comment/proto',
|
||||||
[MessageType.VOTE]: '/opchan-sds/1/vote/proto',
|
[MessageType.VOTE]: '/opchan-sds-ab/1/vote/proto',
|
||||||
[MessageType.MODERATE]: '/opchan-sds/1/moderate/proto',
|
[MessageType.MODERATE]: '/opchan-sds-ab/1/moderate/proto',
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -33,8 +33,8 @@ export class CacheService {
|
|||||||
this.validator = new MessageValidator();
|
this.validator = new MessageValidator();
|
||||||
}
|
}
|
||||||
|
|
||||||
public updateCache(message: unknown): boolean {
|
public async updateCache(message: unknown): Promise<boolean> {
|
||||||
if (!this.validator.isValidMessage(message)) {
|
if (!(await this.validator.isValidMessage(message))) {
|
||||||
const partialMsg = message as {
|
const partialMsg = message as {
|
||||||
id?: unknown;
|
id?: unknown;
|
||||||
type?: unknown;
|
type?: unknown;
|
||||||
|
|||||||
@ -22,8 +22,8 @@ export class MessageService {
|
|||||||
|
|
||||||
private setupMessageHandling(): void {
|
private setupMessageHandling(): void {
|
||||||
if (this.reliableMessaging) {
|
if (this.reliableMessaging) {
|
||||||
this.reliableMessaging.onMessage(message => {
|
this.reliableMessaging.onMessage(async message => {
|
||||||
const isNew = this.cacheService.updateCache(message);
|
const isNew = await this.cacheService.updateCache(message);
|
||||||
if (isNew) {
|
if (isNew) {
|
||||||
this.messageReceivedCallbacks.forEach(callback => callback(message));
|
this.messageReceivedCallbacks.forEach(callback => callback(message));
|
||||||
}
|
}
|
||||||
@ -34,33 +34,43 @@ export class MessageService {
|
|||||||
public async sendMessage(
|
public async sendMessage(
|
||||||
message: OpchanMessage,
|
message: OpchanMessage,
|
||||||
statusCallback?: MessageStatusCallback
|
statusCallback?: MessageStatusCallback
|
||||||
): Promise<void> {
|
): Promise<{ success: boolean; message?: OpchanMessage; error?: string }> {
|
||||||
if (!this.reliableMessaging) {
|
if (!this.reliableMessaging) {
|
||||||
throw new Error('Reliable messaging not initialized');
|
return { success: false, error: 'Reliable messaging not initialized' };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.nodeManager.isReady) {
|
if (!this.nodeManager.isReady) {
|
||||||
throw new Error('Network not ready');
|
return { success: false, error: 'Network not ready' };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update cache optimistically
|
try {
|
||||||
this.cacheService.updateCache(message);
|
// Update cache optimistically
|
||||||
|
await this.cacheService.updateCache(message);
|
||||||
|
|
||||||
// Send via reliable messaging with status tracking
|
// Send via reliable messaging with status tracking
|
||||||
await this.reliableMessaging.sendMessage(message, {
|
await this.reliableMessaging.sendMessage(message, {
|
||||||
onSent: id => {
|
onSent: id => {
|
||||||
console.log(`Message ${id} sent`);
|
console.log(`Message ${id} sent`);
|
||||||
statusCallback?.onSent?.(id);
|
statusCallback?.onSent?.(id);
|
||||||
},
|
},
|
||||||
onAcknowledged: id => {
|
onAcknowledged: id => {
|
||||||
console.log(`Message ${id} acknowledged`);
|
console.log(`Message ${id} acknowledged`);
|
||||||
statusCallback?.onAcknowledged?.(id);
|
statusCallback?.onAcknowledged?.(id);
|
||||||
},
|
},
|
||||||
onError: (id, error) => {
|
onError: (id, error) => {
|
||||||
console.error(`Message ${id} failed:`, error);
|
console.error(`Message ${id} failed:`, error);
|
||||||
statusCallback?.onError?.(id, error);
|
statusCallback?.onError?.(id, error);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return { success: true, message };
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error sending message:', error);
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: error instanceof Error ? error.message : 'Unknown error',
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public onMessageReceived(callback: MessageReceivedCallback): () => void {
|
public onMessageReceived(callback: MessageReceivedCallback): () => void {
|
||||||
|
|||||||
@ -1,10 +1,14 @@
|
|||||||
import { UseAppKitAccountReturn } from '@reown/appkit/react';
|
import { UseAppKitAccountReturn } from '@reown/appkit/react';
|
||||||
import { AppKit } from '@reown/appkit';
|
import { AppKit } from '@reown/appkit';
|
||||||
import { getEnsName } from '@wagmi/core';
|
import {
|
||||||
|
getEnsName,
|
||||||
|
verifyMessage as verifyEthereumMessage,
|
||||||
|
} from '@wagmi/core';
|
||||||
import { ChainNamespace } from '@reown/appkit-common';
|
import { ChainNamespace } from '@reown/appkit-common';
|
||||||
import { config } from './config';
|
import { config } from './config';
|
||||||
import { Provider } from '@reown/appkit-controllers';
|
import { Provider } from '@reown/appkit-controllers';
|
||||||
import { WalletInfo, ActiveWallet } from './types';
|
import { WalletInfo, ActiveWallet } from './types';
|
||||||
|
import * as bitcoinMessage from 'bitcoinjs-message';
|
||||||
|
|
||||||
export class WalletManager {
|
export class WalletManager {
|
||||||
private static instance: WalletManager | null = null;
|
private static instance: WalletManager | null = null;
|
||||||
@ -167,6 +171,64 @@ export class WalletManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify a message signature against a wallet address
|
||||||
|
* @param message - The original message that was signed
|
||||||
|
* @param signature - The signature to verify
|
||||||
|
* @param walletAddress - The expected signer's address
|
||||||
|
* @param walletType - The type of wallet (bitcoin/ethereum)
|
||||||
|
* @returns Promise<boolean> - True if signature is valid
|
||||||
|
*/
|
||||||
|
static async verifySignature(
|
||||||
|
message: string,
|
||||||
|
signature: string,
|
||||||
|
walletAddress: string,
|
||||||
|
walletType: 'bitcoin' | 'ethereum'
|
||||||
|
): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
console.log('WalletManager.verifySignature - verifying signature:', {
|
||||||
|
message,
|
||||||
|
signature,
|
||||||
|
walletAddress,
|
||||||
|
walletType,
|
||||||
|
});
|
||||||
|
if (walletType === 'ethereum') {
|
||||||
|
return await verifyEthereumMessage(config, {
|
||||||
|
address: walletAddress as `0x${string}`,
|
||||||
|
message,
|
||||||
|
signature: signature as `0x${string}`,
|
||||||
|
});
|
||||||
|
} else if (walletType === 'bitcoin') {
|
||||||
|
console.log(
|
||||||
|
'WalletManager.verifySignature - verifying bitcoin signature:',
|
||||||
|
{
|
||||||
|
message,
|
||||||
|
walletAddress,
|
||||||
|
signature,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
const result = bitcoinMessage.verify(message, walletAddress, signature);
|
||||||
|
console.log(
|
||||||
|
'WalletManager.verifySignature - bitcoin signature result:',
|
||||||
|
result
|
||||||
|
);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.error(
|
||||||
|
'WalletManager.verifySignature - unknown wallet type:',
|
||||||
|
walletType
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(
|
||||||
|
'WalletManager.verifySignature - error verifying signature:',
|
||||||
|
error
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get comprehensive wallet info including ENS resolution for Ethereum
|
* Get comprehensive wallet info including ENS resolution for Ethereum
|
||||||
*/
|
*/
|
||||||
@ -208,6 +270,7 @@ export const walletManager = {
|
|||||||
hasInstance: WalletManager.hasInstance,
|
hasInstance: WalletManager.hasInstance,
|
||||||
clear: WalletManager.clear,
|
clear: WalletManager.clear,
|
||||||
resolveENS: WalletManager.resolveENS,
|
resolveENS: WalletManager.resolveENS,
|
||||||
|
verifySignature: WalletManager.verifySignature,
|
||||||
};
|
};
|
||||||
|
|
||||||
export * from './types';
|
export * from './types';
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import {
|
|||||||
ModerateMessage,
|
ModerateMessage,
|
||||||
} from '@/types/waku';
|
} from '@/types/waku';
|
||||||
import { EVerificationStatus } from './identity';
|
import { EVerificationStatus } from './identity';
|
||||||
|
import { DelegationProof } from '@/lib/delegation/types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Union type of all message types
|
* Union type of all message types
|
||||||
@ -92,6 +93,7 @@ export interface Comment extends CommentMessage {
|
|||||||
export interface SignedMessage {
|
export interface SignedMessage {
|
||||||
signature: string;
|
signature: string;
|
||||||
browserPubKey: string;
|
browserPubKey: string;
|
||||||
|
delegationProof?: DelegationProof; // Cryptographic proof that browser key was authorized
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user