self applause added as page with batteries included

Signed-off-by: Uncle Fatso <uncle.fatso@ghostchain.io>
This commit is contained in:
Uncle Fatso 2025-11-11 17:53:08 +03:00
parent b5ab8a6b81
commit 7bb4a473ba
Signed by: f4ts0
GPG Key ID: 565F4F2860226EBB
14 changed files with 989 additions and 6 deletions

View File

@ -1,6 +1,6 @@
{
"name": "ghost-lite",
"version": "0.1.7",
"version": "0.1.8",
"description": "Web application for Ghost and Casper chain.",
"author": "Uncle f4ts0 <f4ts0@ghostchain.io>",
"maintainers": [
@ -28,6 +28,7 @@
"@polkadot-api/utils": "~0.1.2",
"@polkadot-api/view-builder": "~0.4.3",
"@polkadot-labs/hdkd-helpers": "^0.0.11",
"@polkadot/util-crypto": "13.5.5",
"@radix-ui/react-accordion": "^1.2.3",
"@radix-ui/react-checkbox": "^1.3.3",
"@radix-ui/react-select": "^2.1.6",
@ -47,9 +48,11 @@
"react-icons": "^5.5.0",
"react-router-dom": "^7.7.0",
"rxjs": "^7.8.1",
"scale-ts": "1.6.1",
"swr": "^2.2.5",
"tailwind-merge": "^3.3.1",
"typescript": "^5.6.2"
"typescript": "^5.6.2",
"viem": "2.31.0"
},
"devDependencies": {
"@tailwindcss/postcss": "^4.1.11",

View File

@ -32,6 +32,9 @@ importers:
'@polkadot-labs/hdkd-helpers':
specifier: ^0.0.11
version: 0.0.11
'@polkadot/util-crypto':
specifier: 13.5.5
version: 13.5.5(@polkadot/util@13.5.8)
'@radix-ui/react-accordion':
specifier: ^1.2.3
version: 1.2.11(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@ -89,6 +92,9 @@ importers:
rxjs:
specifier: ^7.8.1
version: 7.8.2
scale-ts:
specifier: 1.6.1
version: 1.6.1
swr:
specifier: ^2.2.5
version: 2.3.4(react@18.3.1)
@ -98,6 +104,9 @@ importers:
typescript:
specifier: ^5.6.2
version: 5.6.2
viem:
specifier: 2.31.0
version: 2.31.0(typescript@5.6.2)
devDependencies:
'@tailwindcss/postcss':
specifier: ^4.1.11
@ -150,6 +159,9 @@ importers:
packages:
'@adraffy/ens-normalize@1.11.1':
resolution: {integrity: sha512-nhCBV3quEgesuf7c7KYfperqSS14T8bYuvJ8PcLJp6znkZpFc0AuW4qBtr8eKVyPPe/8RSr7sglCWPU5eaxwKQ==}
'@alloc/quick-lru@5.2.0':
resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
engines: {node: '>=10'}
@ -1179,10 +1191,18 @@ packages:
'@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1':
resolution: {integrity: sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==}
'@noble/ciphers@1.3.0':
resolution: {integrity: sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==}
engines: {node: ^14.21.3 || >=16}
'@noble/curves@1.8.2':
resolution: {integrity: sha512-vnI7V6lFNe0tLAuJMu+2sX+FcL14TaCWy1qiczg1VwRmPrpQCdq5ESXQMqUc2tluRNf6irBXrWbl1mGN8uaU/g==}
engines: {node: ^14.21.3 || >=16}
'@noble/curves@1.9.1':
resolution: {integrity: sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA==}
engines: {node: ^14.21.3 || >=16}
'@noble/curves@1.9.2':
resolution: {integrity: sha512-HxngEd2XUcg9xi20JkwlLCtYwfoFw4JGkuZpT+WlsPD4gB/cxkvTD8fSsoAnphGZhFdZYKeQIPCuFlWPm1uE0g==}
engines: {node: ^14.21.3 || >=16}
@ -1343,6 +1363,102 @@ packages:
'@polkadot-labs/hdkd-helpers@0.0.11':
resolution: {integrity: sha512-qPlWqC3NNV/2NYc5GEy+Ovi4UBAgkMGvMfyiYuj2BQN4lW59Q1T9coNx0Yp6XzsnJ1ddaF9PWaUtxj3LdM0IDw==}
'@polkadot/networks@13.5.5':
resolution: {integrity: sha512-gTaKVSDRxjNAQ/oFMA83DXOo8A+/LP4XePbEHxNCku/Ox5R3IYGKTeZhlHgYtUZvdZgK+miyroEyz1Eq6Z9p+Q==}
engines: {node: '>=18'}
'@polkadot/util-crypto@13.5.5':
resolution: {integrity: sha512-LAHarViiPwjrXl05fXOV5pW6jvK8A0Y6uIJnttSSERjTKqG5O4VtgRAcqLXShTp1rEVE5T4DaIX5xZd7azBHyg==}
engines: {node: '>=18'}
peerDependencies:
'@polkadot/util': 13.5.5
'@polkadot/util@13.5.5':
resolution: {integrity: sha512-O3sGI8vWmv5o1cd8fDkc+cZGpUsG+ZUFAOitgv6bRt5llaBqS5VpTrUANEjfgUMgUuTn7Y2cPKGDLItYr5WnUg==}
engines: {node: '>=18'}
'@polkadot/util@13.5.8':
resolution: {integrity: sha512-5xEfNoum/Ct+gYWN3AYvBQ8vq8KiS4HsY3BexPUPXvSXSx3Id/JYA5oFLYnkuRp8hwoQGjX0wqUJ6Hp8D8LHKw==}
engines: {node: '>=18'}
'@polkadot/wasm-bridge@7.5.1':
resolution: {integrity: sha512-E+N3CSnX3YaXpAmfIQ+4bTyiAqJQKvVcMaXjkuL8Tp2zYffClWLG5e+RY15Uh+EWfUl9If4y6cLZi3D5NcpAGQ==}
engines: {node: '>=18'}
peerDependencies:
'@polkadot/util': '*'
'@polkadot/x-randomvalues': '*'
'@polkadot/wasm-crypto-asmjs@7.5.1':
resolution: {integrity: sha512-jAg7Uusk+xeHQ+QHEH4c/N3b1kEGBqZb51cWe+yM61kKpQwVGZhNdlWetW6U23t/BMyZArIWMsZqmK/Ij0PHog==}
engines: {node: '>=18'}
peerDependencies:
'@polkadot/util': '*'
'@polkadot/wasm-crypto-init@7.5.1':
resolution: {integrity: sha512-Obu4ZEo5jYO6sN31eqCNOXo88rPVkP9TrUOyynuFCnXnXr8V/HlmY/YkAd9F87chZnkTJRlzak17kIWr+i7w3A==}
engines: {node: '>=18'}
peerDependencies:
'@polkadot/util': '*'
'@polkadot/x-randomvalues': '*'
'@polkadot/wasm-crypto-wasm@7.5.1':
resolution: {integrity: sha512-S2yQSGbOGTcaV6UdipFVyEGanJvG6uD6Tg7XubxpiGbNAblsyYKeFcxyH1qCosk/4qf+GIUwlOL4ydhosZflqg==}
engines: {node: '>=18'}
peerDependencies:
'@polkadot/util': '*'
'@polkadot/wasm-crypto@7.5.1':
resolution: {integrity: sha512-acjt4VJ3w19v7b/SIPsV/5k9s6JsragHKPnwoZ0KTfBvAFXwzz80jUzVGxA06SKHacfCUe7vBRlz7M5oRby1Pw==}
engines: {node: '>=18'}
peerDependencies:
'@polkadot/util': '*'
'@polkadot/x-randomvalues': '*'
'@polkadot/wasm-util@7.5.1':
resolution: {integrity: sha512-sbvu71isFhPXpvMVX+EkRnUg/+54Tx7Sf9BEMqxxoPj7cG1I/MKeDEwbQz6MaU4gm7xJqvEWCAemLFcXfHQ/2A==}
engines: {node: '>=18'}
peerDependencies:
'@polkadot/util': '*'
'@polkadot/x-bigint@13.5.5':
resolution: {integrity: sha512-SAd7Lfdgp6mz+utkoML8MN9FqTMCuPfk7v5rLJnm9vHgXw5uYnycbjH5Uc7ZgQIQWtMXJV3thrlltMan5DUXtA==}
engines: {node: '>=18'}
'@polkadot/x-bigint@13.5.8':
resolution: {integrity: sha512-4ltTNgFDZoPnuQBrP7Z3m3imQ3xKb7jKScAT/Gy89h9siLYyJdZ+qawZfO1cll6fqYlka+k7USqGeyOEqoyCfg==}
engines: {node: '>=18'}
'@polkadot/x-global@13.5.5':
resolution: {integrity: sha512-fw+VM191bodacSeieMm8Vmrym4jjevX08IINDcQTd1gIOjtE5CriJhwfBbAF4WnlTp/11jhhbX4/SvWMubXAzQ==}
engines: {node: '>=18'}
'@polkadot/x-global@13.5.8':
resolution: {integrity: sha512-KDK3CEG/RvfCu3w4HZ/iv6c49XrN5Hz/3mXUQdLfR+TFKADdNCoIhMZ9f7vHYgdnB9tlY9s6Dn2svY99h1wRiw==}
engines: {node: '>=18'}
'@polkadot/x-randomvalues@13.5.5':
resolution: {integrity: sha512-W0AoNgr/NEVsHWegJUjUyI9Q1IoTHILIb/bkjyTcXTU3+2YFxP12ophhsI1dMaNbXqFotNyts7mNOsTVDnQNXA==}
engines: {node: '>=18'}
peerDependencies:
'@polkadot/util': 13.5.5
'@polkadot/wasm-util': '*'
'@polkadot/x-textdecoder@13.5.5':
resolution: {integrity: sha512-KkZ1rqdJZ8tsRY0D5pLqfU8B/BrSQVEPMKHj4s/oc8dTrikfEUC+ELaH2jdrUqsZX6K/OTHjaF0J31YZcr7rCg==}
engines: {node: '>=18'}
'@polkadot/x-textdecoder@13.5.8':
resolution: {integrity: sha512-Uzz6spRDzzQDQBN6PNpjz0HVp2kqhQVJRh1ShLP9rBg+nH4we9VGriWGG5stkgNKjRGT0Z7cvx0FupRQoNDU4A==}
engines: {node: '>=18'}
'@polkadot/x-textencoder@13.5.5':
resolution: {integrity: sha512-yEgUUojBb4goYf4V5I7urdJ+W+1aI13U1kZmUwMc+/G2YQz8pX3s/Tyb/iuxU5MlFh0AZZXP5NqUnFol+vwNEg==}
engines: {node: '>=18'}
'@polkadot/x-textencoder@13.5.8':
resolution: {integrity: sha512-2jcVte6mUy+GXjpZsS7dFca8C2r8EGgaG5o7mVQZ+PjauD06O/UP2g48UuDJHGe1QCJN0f0WaoD+RNw9tOF2yQ==}
engines: {node: '>=18'}
'@radix-ui/number@1.1.1':
resolution: {integrity: sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==}
@ -1780,6 +1896,12 @@ packages:
'@scure/base@1.2.6':
resolution: {integrity: sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==}
'@scure/bip32@1.7.0':
resolution: {integrity: sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw==}
'@scure/bip39@1.6.0':
resolution: {integrity: sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A==}
'@sec-ant/readable-stream@0.4.1':
resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==}
@ -1804,6 +1926,9 @@ packages:
peerDependencies:
smoldot: ^2
'@substrate/ss58-registry@1.51.0':
resolution: {integrity: sha512-TWDurLiPxndFgKjVavCniytBIw+t4ViOi7TYp9h/D0NMmkEc9klFTo+827eyEJ0lELpqO207Ey7uGxUa+BS1jQ==}
'@swc/core-darwin-arm64@1.12.11':
resolution: {integrity: sha512-J19Jj9Y5x/N0loExH7W0OI9OwwoVyxutDdkyq1o/kgXyBqmmzV7Y/Q9QekI2Fm/qc5mNeAdP7aj4boY4AY/JPw==}
engines: {node: '>=10'}
@ -1970,6 +2095,9 @@ packages:
'@total-typescript/tsconfig@1.0.4':
resolution: {integrity: sha512-fO4ctMPGz1kOFOQ4RCPBRBfMy3gDn+pegUfrGyUFRMv/Rd0ZM3/SHH3hFCYG4u6bPLG8OlmOGcBLDexvyr3A5w==}
'@types/bn.js@5.2.0':
resolution: {integrity: sha512-DLbJ1BPqxvQhIGbeu8VbUC1DiAiahHtAYvA0ZEAa4P31F7IaArc8z3C3BRQdWX4mtLQuABG4yzp76ZrS02Ui1Q==}
'@types/estree@1.0.8':
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
@ -2162,6 +2290,17 @@ packages:
'@zag-js/utils@0.48.0':
resolution: {integrity: sha512-VxNfAY3qMBm+VEsbM9+GmXTV9Ks2MxrLcXIcTK4qaGBl0y+DZZHN+b8DVMTLZpmkQK6wkJnkEnFC1Sv9dsQYkA==}
abitype@1.0.8:
resolution: {integrity: sha512-ZeiI6h3GnW06uYDLx0etQtX/p8E24UaHHBj57RSjK7YBFe7iuVn07EDpOeP451D06sF27VOz9JJPlIKJmXgkEg==}
peerDependencies:
typescript: '>=5.0.4'
zod: ^3 >=3.22.0
peerDependenciesMeta:
typescript:
optional: true
zod:
optional: true
acorn-jsx@5.3.2:
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
peerDependencies:
@ -2295,6 +2434,9 @@ packages:
balanced-match@1.0.2:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
bn.js@5.2.2:
resolution: {integrity: sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw==}
brace-expansion@1.1.12:
resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==}
@ -2710,6 +2852,9 @@ packages:
resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
engines: {node: '>=0.10.0'}
eventemitter3@5.0.1:
resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==}
execa@9.6.0:
resolution: {integrity: sha512-jpWzZ1ZhwUmeWRhS7Qv3mhpOhLfwI+uAX4e5fOcXqwMR7EcJ0pj2kV1CVzHVMX/LphnKWD3LObjZCoJ71lKpHw==}
engines: {node: ^18.19.0 || >=20.5.0}
@ -3064,6 +3209,11 @@ packages:
isexe@2.0.0:
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
isows@1.0.7:
resolution: {integrity: sha512-I1fSfDCZL5P0v33sVqeTDSpcstAg/N+wF5HS033mogOVIp4B+oHC7oOCsA3axAbBSGTJ8QubbNmnIRN/h8U7hg==}
peerDependencies:
ws: '*'
iterator.prototype@1.1.5:
resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==}
engines: {node: '>= 0.4'}
@ -3380,6 +3530,14 @@ packages:
resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==}
engines: {node: '>= 0.4'}
ox@0.7.1:
resolution: {integrity: sha512-+k9fY9PRNuAMHRFIUbiK9Nt5seYHHzSQs9Bj+iMETcGtlpS7SmBzcGSVUQO3+nqGLEiNK4598pHNFlVRaZbRsg==}
peerDependencies:
typescript: '>=5.4.0'
peerDependenciesMeta:
typescript:
optional: true
p-limit@3.1.0:
resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
engines: {node: '>=10'}
@ -4056,6 +4214,14 @@ packages:
validate-npm-package-license@3.0.4:
resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==}
viem@2.31.0:
resolution: {integrity: sha512-U7OMQ6yqK+bRbEIarf2vqxL7unSEQvNxvML/1zG7suAmKuJmipqdVTVJGKBCJiYsm/EremyO2FS4dHIPpGv+eA==}
peerDependencies:
typescript: '>=5.0.4'
peerDependenciesMeta:
typescript:
optional: true
vite-tsconfig-paths@5.1.4:
resolution: {integrity: sha512-cYj0LRuLV2c2sMqhqhGpaO3LretdtMn/BVX4cPLanIZuwwrkVl+lK84E/miEXkCHWXuq65rhNN4rXsBcOB3S4w==}
peerDependencies:
@ -4149,6 +4315,18 @@ packages:
resolution: {integrity: sha512-DqUx8GI3r9BFWwU2DPKddL1E7xWfbFED82mLVhGXKlFEPe8IkBftzO7WfNwHtk7oGDHDeuH/o8VMpzzfMwmLUA==}
engines: {node: '>=18'}
ws@8.18.2:
resolution: {integrity: sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==}
engines: {node: '>=10.0.0'}
peerDependencies:
bufferutil: ^4.0.1
utf-8-validate: '>=5.0.2'
peerDependenciesMeta:
bufferutil:
optional: true
utf-8-validate:
optional: true
ws@8.18.3:
resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==}
engines: {node: '>=10.0.0'}
@ -4182,6 +4360,8 @@ packages:
snapshots:
'@adraffy/ens-normalize@1.11.1': {}
'@alloc/quick-lru@5.2.0': {}
'@ampproject/remapping@2.3.0':
@ -5267,10 +5447,16 @@ snapshots:
dependencies:
eslint-scope: 5.1.1
'@noble/ciphers@1.3.0': {}
'@noble/curves@1.8.2':
dependencies:
'@noble/hashes': 1.7.2
'@noble/curves@1.9.1':
dependencies:
'@noble/hashes': 1.8.0
'@noble/curves@1.9.2':
dependencies:
'@noble/hashes': 1.8.0
@ -5556,6 +5742,134 @@ snapshots:
micro-sr25519: 0.1.3
scale-ts: 1.6.1
'@polkadot/networks@13.5.5':
dependencies:
'@polkadot/util': 13.5.5
'@substrate/ss58-registry': 1.51.0
tslib: 2.8.1
'@polkadot/util-crypto@13.5.5(@polkadot/util@13.5.8)':
dependencies:
'@noble/curves': 1.9.2
'@noble/hashes': 1.8.0
'@polkadot/networks': 13.5.5
'@polkadot/util': 13.5.8
'@polkadot/wasm-crypto': 7.5.1(@polkadot/util@13.5.8)(@polkadot/x-randomvalues@13.5.5(@polkadot/util@13.5.8)(@polkadot/wasm-util@7.5.1(@polkadot/util@13.5.8)))
'@polkadot/wasm-util': 7.5.1(@polkadot/util@13.5.8)
'@polkadot/x-bigint': 13.5.5
'@polkadot/x-randomvalues': 13.5.5(@polkadot/util@13.5.8)(@polkadot/wasm-util@7.5.1(@polkadot/util@13.5.8))
'@scure/base': 1.2.6
tslib: 2.8.1
'@polkadot/util@13.5.5':
dependencies:
'@polkadot/x-bigint': 13.5.5
'@polkadot/x-global': 13.5.5
'@polkadot/x-textdecoder': 13.5.5
'@polkadot/x-textencoder': 13.5.5
'@types/bn.js': 5.2.0
bn.js: 5.2.2
tslib: 2.8.1
'@polkadot/util@13.5.8':
dependencies:
'@polkadot/x-bigint': 13.5.8
'@polkadot/x-global': 13.5.8
'@polkadot/x-textdecoder': 13.5.8
'@polkadot/x-textencoder': 13.5.8
'@types/bn.js': 5.2.0
bn.js: 5.2.2
tslib: 2.8.1
'@polkadot/wasm-bridge@7.5.1(@polkadot/util@13.5.8)(@polkadot/x-randomvalues@13.5.5(@polkadot/util@13.5.8)(@polkadot/wasm-util@7.5.1(@polkadot/util@13.5.8)))':
dependencies:
'@polkadot/util': 13.5.8
'@polkadot/wasm-util': 7.5.1(@polkadot/util@13.5.8)
'@polkadot/x-randomvalues': 13.5.5(@polkadot/util@13.5.8)(@polkadot/wasm-util@7.5.1(@polkadot/util@13.5.8))
tslib: 2.8.1
'@polkadot/wasm-crypto-asmjs@7.5.1(@polkadot/util@13.5.8)':
dependencies:
'@polkadot/util': 13.5.8
tslib: 2.8.1
'@polkadot/wasm-crypto-init@7.5.1(@polkadot/util@13.5.8)(@polkadot/x-randomvalues@13.5.5(@polkadot/util@13.5.8)(@polkadot/wasm-util@7.5.1(@polkadot/util@13.5.8)))':
dependencies:
'@polkadot/util': 13.5.8
'@polkadot/wasm-bridge': 7.5.1(@polkadot/util@13.5.8)(@polkadot/x-randomvalues@13.5.5(@polkadot/util@13.5.8)(@polkadot/wasm-util@7.5.1(@polkadot/util@13.5.8)))
'@polkadot/wasm-crypto-asmjs': 7.5.1(@polkadot/util@13.5.8)
'@polkadot/wasm-crypto-wasm': 7.5.1(@polkadot/util@13.5.8)
'@polkadot/wasm-util': 7.5.1(@polkadot/util@13.5.8)
'@polkadot/x-randomvalues': 13.5.5(@polkadot/util@13.5.8)(@polkadot/wasm-util@7.5.1(@polkadot/util@13.5.8))
tslib: 2.8.1
'@polkadot/wasm-crypto-wasm@7.5.1(@polkadot/util@13.5.8)':
dependencies:
'@polkadot/util': 13.5.8
'@polkadot/wasm-util': 7.5.1(@polkadot/util@13.5.8)
tslib: 2.8.1
'@polkadot/wasm-crypto@7.5.1(@polkadot/util@13.5.8)(@polkadot/x-randomvalues@13.5.5(@polkadot/util@13.5.8)(@polkadot/wasm-util@7.5.1(@polkadot/util@13.5.8)))':
dependencies:
'@polkadot/util': 13.5.8
'@polkadot/wasm-bridge': 7.5.1(@polkadot/util@13.5.8)(@polkadot/x-randomvalues@13.5.5(@polkadot/util@13.5.8)(@polkadot/wasm-util@7.5.1(@polkadot/util@13.5.8)))
'@polkadot/wasm-crypto-asmjs': 7.5.1(@polkadot/util@13.5.8)
'@polkadot/wasm-crypto-init': 7.5.1(@polkadot/util@13.5.8)(@polkadot/x-randomvalues@13.5.5(@polkadot/util@13.5.8)(@polkadot/wasm-util@7.5.1(@polkadot/util@13.5.8)))
'@polkadot/wasm-crypto-wasm': 7.5.1(@polkadot/util@13.5.8)
'@polkadot/wasm-util': 7.5.1(@polkadot/util@13.5.8)
'@polkadot/x-randomvalues': 13.5.5(@polkadot/util@13.5.8)(@polkadot/wasm-util@7.5.1(@polkadot/util@13.5.8))
tslib: 2.8.1
'@polkadot/wasm-util@7.5.1(@polkadot/util@13.5.8)':
dependencies:
'@polkadot/util': 13.5.8
tslib: 2.8.1
'@polkadot/x-bigint@13.5.5':
dependencies:
'@polkadot/x-global': 13.5.5
tslib: 2.8.1
'@polkadot/x-bigint@13.5.8':
dependencies:
'@polkadot/x-global': 13.5.8
tslib: 2.8.1
'@polkadot/x-global@13.5.5':
dependencies:
tslib: 2.8.1
'@polkadot/x-global@13.5.8':
dependencies:
tslib: 2.8.1
'@polkadot/x-randomvalues@13.5.5(@polkadot/util@13.5.8)(@polkadot/wasm-util@7.5.1(@polkadot/util@13.5.8))':
dependencies:
'@polkadot/util': 13.5.8
'@polkadot/wasm-util': 7.5.1(@polkadot/util@13.5.8)
'@polkadot/x-global': 13.5.5
tslib: 2.8.1
'@polkadot/x-textdecoder@13.5.5':
dependencies:
'@polkadot/x-global': 13.5.5
tslib: 2.8.1
'@polkadot/x-textdecoder@13.5.8':
dependencies:
'@polkadot/x-global': 13.5.8
tslib: 2.8.1
'@polkadot/x-textencoder@13.5.5':
dependencies:
'@polkadot/x-global': 13.5.5
tslib: 2.8.1
'@polkadot/x-textencoder@13.5.8':
dependencies:
'@polkadot/x-global': 13.5.8
tslib: 2.8.1
'@radix-ui/number@1.1.1': {}
'@radix-ui/primitive@1.1.2': {}
@ -5917,6 +6231,17 @@ snapshots:
'@scure/base@1.2.6': {}
'@scure/bip32@1.7.0':
dependencies:
'@noble/curves': 1.9.2
'@noble/hashes': 1.8.0
'@scure/base': 1.2.6
'@scure/bip39@1.6.0':
dependencies:
'@noble/hashes': 1.8.0
'@scure/base': 1.2.6
'@sec-ant/readable-stream@0.4.1': {}
'@sindresorhus/merge-streams@4.0.0': {}
@ -5949,6 +6274,8 @@ snapshots:
rxjs: 7.8.2
smoldot: 2.0.36
'@substrate/ss58-registry@1.51.0': {}
'@swc/core-darwin-arm64@1.12.11':
optional: true
@ -6075,6 +6402,10 @@ snapshots:
'@total-typescript/tsconfig@1.0.4': {}
'@types/bn.js@5.2.0':
dependencies:
'@types/node': 24.0.15
'@types/estree@1.0.8': {}
'@types/json-schema@7.0.15': {}
@ -6337,6 +6668,10 @@ snapshots:
'@zag-js/utils@0.48.0': {}
abitype@1.0.8(typescript@5.6.2):
optionalDependencies:
typescript: 5.6.2
acorn-jsx@5.3.2(acorn@8.15.0):
dependencies:
acorn: 8.15.0
@ -6517,6 +6852,8 @@ snapshots:
balanced-match@1.0.2: {}
bn.js@5.2.2: {}
brace-expansion@1.1.12:
dependencies:
balanced-match: 1.0.2
@ -7146,6 +7483,8 @@ snapshots:
esutils@2.0.3: {}
eventemitter3@5.0.1: {}
execa@9.6.0:
dependencies:
'@sindresorhus/merge-streams': 4.0.0
@ -7511,6 +7850,10 @@ snapshots:
isexe@2.0.0: {}
isows@1.0.7(ws@8.18.2):
dependencies:
ws: 8.18.2
iterator.prototype@1.1.5:
dependencies:
define-data-property: 1.1.4
@ -7811,6 +8154,21 @@ snapshots:
object-keys: 1.1.1
safe-push-apply: 1.0.0
ox@0.7.1(typescript@5.6.2):
dependencies:
'@adraffy/ens-normalize': 1.11.1
'@noble/ciphers': 1.3.0
'@noble/curves': 1.9.2
'@noble/hashes': 1.8.0
'@scure/bip32': 1.7.0
'@scure/bip39': 1.6.0
abitype: 1.0.8(typescript@5.6.2)
eventemitter3: 5.0.1
optionalDependencies:
typescript: 5.6.2
transitivePeerDependencies:
- zod
p-limit@3.1.0:
dependencies:
yocto-queue: 0.1.0
@ -8559,6 +8917,23 @@ snapshots:
spdx-correct: 3.2.0
spdx-expression-parse: 3.0.1
viem@2.31.0(typescript@5.6.2):
dependencies:
'@noble/curves': 1.9.1
'@noble/hashes': 1.8.0
'@scure/bip32': 1.7.0
'@scure/bip39': 1.6.0
abitype: 1.0.8(typescript@5.6.2)
isows: 1.0.7(ws@8.18.2)
ox: 0.7.1(typescript@5.6.2)
ws: 8.18.2
optionalDependencies:
typescript: 5.6.2
transitivePeerDependencies:
- bufferutil
- utf-8-validate
- zod
vite-tsconfig-paths@5.1.4(typescript@5.6.2)(vite@5.4.19(@types/node@24.0.15)(lightningcss@1.30.1)):
dependencies:
debug: 4.4.1
@ -8669,6 +9044,8 @@ snapshots:
type-fest: 4.41.0
write-json-file: 6.0.0
ws@8.18.2: {}
ws@8.18.3: {}
yallist@3.1.1: {}

View File

@ -14,6 +14,8 @@ export const Header = () => {
return "Address Book"
case "nominations":
return "Nominations"
case "applause":
return "Self Applause"
default:
return "Health Check";
}

View File

@ -1,4 +1,4 @@
import { HeartPulse, SendToBack, Book, Users } from "lucide-react"
import { HeartPulse, SendToBack, Book, Users, Handshake } from "lucide-react"
import { FaGithub } from "react-icons/fa"
import { Link, useLocation } from "react-router-dom"
import { useEffect } from "react"
@ -124,6 +124,12 @@ export const Sidebar = () => {
<span className={`md:block hidden ${cName("title", currentPath, "nominations")}`} >Nominations</span>
</div>
</Link>
<Link to="/applause" className="relative">
<div className={cName("item", currentPath, "applause")}>
<Handshake className={cName("icon", currentPath, "applause")} />
<span className={`md:block hidden ${cName("title", currentPath, "applause")}`} >Self Applause</span>
</div>
</Link>
</ul>
<div className="w-full text-center flex-grow flex flex-col justify-end">

View File

@ -13,6 +13,7 @@ const HealthCheck = lazy(() => import("./HealthCheck").then(module => ({ default
const Transactions = lazy(() => import("./Transactions").then(module => ({ default: module.Transactions })))
const Nominations = lazy(() => import("./Nominations").then(module => ({ default: module.Nominations })))
const AddressBook = lazy(() => import("./AddressBook").then(module => ({ default: module.AddressBook })))
const SelfApplause = lazy(() => import("./SelfApplause").then(module => ({ default: module.SelfApplause })))
export const App = () => {
return (
@ -29,6 +30,7 @@ export const App = () => {
<Route path="/health" element={<HealthCheck />} />
<Route path="/transactions" element={<Transactions />} />
<Route path="/book" element={<AddressBook />} />
<Route path="/applause" element={<SelfApplause />} />
<Route path="/nominations" element={<Nominations />} />
<Route path="*" element={<Navigate to="/health" replace />} />
</Routes>

View File

@ -0,0 +1,287 @@
import { useState, useEffect, useMemo } from "react"
import { useSearchParams } from "react-router-dom"
import { Hand, BadgeAlert, BadgeCheck, ArrowBigRightDash } from "lucide-react"
import { u64, u128 } from "scale-ts"
import { keccak256 } from "viem"
import { decodeAddress } from "@polkadot/util-crypto"
import { Row } from "./Row"
import { Sender } from "./Accounts"
import { Input } from "../components/ui/input"
import { Button } from "../components/ui/button"
// http://localhost:5173/#/applause?networkId=11155111&sessionIndex=591&receiver=sfGvT6dSpR1Sodpu7XPy2oa4Pbu9UaKjJ6mnLP8JmdSktnde7&amount=0.010000000&transactionHash=0x29e80e04eef8db2eda43e36465fb3adda79b19eb22d695303686b64bd1055a21
import {
useChainSpecV1,
useSystemAccount,
useUnstableProvider,
useApplauseThreshold,
useCurrentIndex,
useAuthorities,
useClapsInSession,
useReceivedClaps,
useApplausesForTransaction,
useSelfApplauseCalldata,
useTransactionStatusProvider,
SessionAuthorityInfo
} from "../hooks"
export const SelfApplause = () => {
const [searchParams] = useSearchParams()
const [networkId, setNetworkId] = useState("")
const [sessionIndex, setSessionIndex] = useState("")
const [transactionHash, setTransactionHash] = useState("")
const [receiver, setReceiver] = useState("")
const [bridgedAmount, setBridgedAmount] = useState("")
const { account, accounts, connectAccount } = useUnstableProvider()
const chainSpecV1 = useChainSpecV1()
const tokenDecimals: number = chainSpecV1?.properties?.tokenDecimals ?? 0
const tokenSymbol: string = chainSpecV1?.properties?.tokenSymbol ?? ""
const ss58Format: number = chainSpecV1?.properties?.ss58Format ?? 1995
const nextSessionIndex = useMemo(() => {
const number = +sessionIndex
return isNaN(number) ? undefined : number + 1
}, [sessionIndex])
const amountConverted = useMemo(() => {
const amountNum = parseFloat(bridgedAmount);
if (isNaN(amountNum)) {
return 0n;
}
return BigInt(Math.floor(amountNum * Math.pow(10, tokenDecimals)))
}, [bridgedAmount, tokenDecimals])
const hashedArguments = useMemo(() => {
try {
const amountEncoded = u128.enc(amountConverted)
const networkIdEncoded = u64.enc(BigInt(networkId))
const addressEncoded = decodeAddress(receiver, false, ss58Format)
const clapArgsStr = new Uint8Array([
...addressEncoded,
...amountEncoded,
...networkIdEncoded
])
return keccak256(clapArgsStr)
} catch {
return undefined
}
}, [receiver, amountConverted, ss58Format, networkId])
const {
isSubmittingTransaction,
handleTransaction,
renderStatus,
} = useTransactionStatusProvider()
const appluseThreshold = useApplauseThreshold()
const currentSession = useCurrentIndex()
const authorities = useAuthorities({ currentSession: Number(sessionIndex) })
const authoritiesNext = useAuthorities({ currentSession: nextSessionIndex })
const clapsInSession = useClapsInSession({
currentSession: Number(sessionIndex)
})
const clapsInNextSession = useClapsInSession({
currentSession: nextSessionIndex
})
const receivedClaps = useReceivedClaps({
currentSession: Number(sessionIndex),
txHash: transactionHash,
argsHash: hashedArguments
})
const receivedClapsNext = useReceivedClaps({
currentSession: nextSessionIndex,
txHash: transactionHash,
argsHash: hashedArguments
})
const transactionApplaused = useApplausesForTransaction({
currentSession: Number(sessionIndex),
txHash: transactionHash,
argsHash: hashedArguments
})
useEffect(() => {
setNetworkId(searchParams.get("networkId") ?? "")
setSessionIndex(searchParams.get("sessionIndex") ?? "")
setTransactionHash(searchParams.get("transactionHash") ?? "")
setReceiver(searchParams.get("receiver") ?? "")
setBridgedAmount(searchParams.get("amount") ?? "")
}, [searchParams])
const senderAccount = useSystemAccount({
account: account
? account.address
: undefined
})
const disabledInSession = useMemo(() => {
return clapsInSession?.filter((info: SessionAuthorityInfo) => info.disabled) ?? []
}, [clapsInSession])
const totalDisabled = useMemo(() => {
let numberOfDisabled = clapsInSession?.filter((info: SessionAuthorityInfo) => info.disabled).length ?? 0
clapsInNextSession?.forEach((info: SessionAuthorityInfo, index: number) => {
if (info.disabled) {
const authority = authoritiesNext?.at(index)
if (authority) {
const myIndex = authorities?.indexOf(authority)
if (myIndex) {
numberOfDisabled += 1
}
}
}
})
return numberOfDisabled
}, [clapsInSession, clapsInNextSession, authoritiesNext, authorities])
const totalReceivedClaps = useMemo(() => {
let totalClaps = receivedClaps?.length ?? 0
for (const index of (receivedClapsNext ?? [])) {
const authority = authoritiesNext[index];
if (authority) {
const myIndex = authorities?.indexOf(authority)
if (myIndex) {
totalClaps += 1
}
}
}
return totalClaps
}, [authorities, receivedClaps, receivedClapsNext, authoritiesNext])
const applausePossible = useMemo(() => {
const value = totalReceivedClaps * 100 / (authorities?.length ?? 0) - totalDisabled
return value > appluseThreshold
}, [authorities, totalReceivedClaps, totalDisabled, appluseThreshold])
const loadedCorrectly = useMemo(() => {
return clapsInSession && authorities && disabledInSession
}, [clapsInSession, authorities, disabledInSession])
const applyDecimals = (value = 0n, decimals = 0, tokenSymbol = "CSPR") => {
if (!value) return `0 ${tokenSymbol}`
const numberValue = Number(value) / Math.pow(10, decimals)
const formatter = new Intl.NumberFormat("en-US", {
minimumFractionDigits: 6,
maximumFractionDigits: 6,
})
return `${formatter.format(numberValue)} ${tokenSymbol}`
}
const calldata = useSelfApplauseCalldata(
Number(networkId),
Number(sessionIndex),
transactionHash,
receiver,
amountConverted
)
const handleOnSelfApplause = () => handleTransaction({ calldata: calldata, txName: "self_applause" })
return (
<div className="sm:w-[500px] w-[85%] h-fit flex flex-col flex-1 gap-2 justify-center self-center rounded py-2">
<div className="bg-muted p-4 rounded flex flex-col gap-2">
<Sender
account={account?.address ?? ""}
accounts={accounts?.map(acc => acc?.address ?? "") ?? []}
senderAccount={senderAccount}
senderBalance={applyDecimals(senderAccount?.data.free ?? 0n, tokenDecimals, tokenSymbol)}
tokenDecimals={tokenDecimals}
tokenSymbol={tokenSymbol}
connectAccount={connectAccount}
applyDecimals={applyDecimals}
/>
<Row title={"Network Id"} element={<Input
onChange={(e) => setNetworkId(e.target.value)}
value={networkId}
aria-label="NetworkId"
type="text"
className="sm:w-[300px] w-full"
placeholder={"0"}
/>} />
<Row title={"Session Index"} element={<Input
onChange={(e) => setSessionIndex(e.target.value)}
value={sessionIndex}
aria-label="SessionIndex"
type="text"
className="sm:w-[300px] w-full"
placeholder={"0"}
/>} />
<Row title={"Transaction Hash"} element={<Input
onChange={(e) => setTransactionHash(e.target.value)}
value={transactionHash}
aria-label="TransactionHash"
type="text"
className="sm:w-[300px] w-full"
placeholder={"0x..."}
/>} />
<Row title={"Receiver Address"} element={<Input
onChange={(e) => setReceiver(e.target.value)}
value={receiver}
aria-label="ReceiverAddress"
type="text"
className="sm:w-[300px] w-full"
placeholder={"sf..."}
/>} />
<Row title={"Bridged Amount"} element={<Input
onChange={(e) => setBridgedAmount(e.target.value)}
value={bridgedAmount}
aria-label="BridgedAmount"
type="text"
className="sm:w-[300px] w-full"
placeholder={"0"}
/>} />
{loadedCorrectly &&
<div className="flex justify-around items-center mt-4 mb-2">
<div className="flex flex-col justify-center items-center hover:text-secondary-foreground cursor-pointer">
<div className="text-xs">
Actual
</div>
<BadgeAlert className="w-8 h-8 inline-block" />
<div className="text-xs">
{clapsInSession.length} / {authorities.length - disabledInSession.length}
</div>
</div>
<ArrowBigRightDash className="w-6 h-6 inline-block" />
<div className="flex flex-col justify-center items-center hover:text-secondary-foreground cursor-pointer">
<div className="text-xs">
Possible
</div>
<BadgeCheck className="w-8 h-8 inline-block" />
<div className="text-xs">
{totalReceivedClaps} / {authorities ? authorities.length - totalDisabled : 0}
</div>
</div>
</div>
}
<Button
type="button"
variant="secondary"
className="text-sm p-4 w-full mt-4"
onClick={handleOnSelfApplause}
disabled={
isSubmittingTransaction || transactionApplaused ||
!loadedCorrectly || !account || !applausePossible ||
currentSession < (nextSessionIndex ?? 0)
}
>
<Hand className="w-4 h-4 inline-block mr-2" />
{transactionApplaused
? "Already applaused"
: !applausePossible
? "Impossible to applause"
: currentSession < (nextSessionIndex ?? 0)
? "Not ready yet"
: "Try applause"
}
</Button>
{renderStatus()}
</div>
</div>
)
}

View File

@ -18,3 +18,8 @@ export * from "./useNominations"
export * from "./useLedger"
export * from "./usePayee"
export * from "./useSlasingSpans"
export * from "./useApplausesForTransaction"
export * from "./useAuthorities"
export * from "./useClapsInSession"
export * from "./useReceivedClaps"
export * from "./useCurrentIndex"

View File

@ -0,0 +1,54 @@
import useSWRSubscription from "swr/subscription"
import { getDynamicBuilder, getLookupFn } from "@polkadot-api/metadata-builders"
import type { BlockInfo } from "@polkadot-api/observable-client"
import { distinct, filter, map, mergeMap } from "rxjs"
import { fromHex } from "@polkadot-api/utils";
import { useUnstableProvider } from "./useUnstableProvider"
import { useMetadata } from "./useMetadata"
interface ApplausesForTransactionInterface {
currentSession?: Number;
txHash?: string;
argsHash?: string;
}
export const useApplausesForTransaction = ({ currentSession, txHash, argsHash }: ApplausesForTransactionInterface) => {
const { chainHead$, chainId } = useUnstableProvider()
const metadata = useMetadata()
const { data: applausesForTransaction } = useSWRSubscription(
chainHead$ && txHash && argsHash && currentSession && chainId && metadata
? ["applausesForTransaction", chainHead$, txHash, argsHash, currentSession, chainId, metadata]
: null,
([_, chainHead$, txHash, argsHash, currentSession, chainId, metadata], { next }) => {
const { finalized$, storage$ } = chainHead$
const subscription = finalized$.pipe(
filter(Boolean),
mergeMap((blockInfo: BlockInfo) => {
const builder = getDynamicBuilder(getLookupFn(metadata))
const applausesForTransaction = builder.buildStorage("GhostSlowClaps", "ApplausesForTransaction")
return storage$(blockInfo?.hash, "value", () =>
applausesForTransaction?.keys.enc(
currentSession,
{ asBytes: () => fromHex(txHash) },
{ asBytes: () => fromHex(argsHash) },
)
).pipe(
filter(Boolean),
distinct(),
map((value: string) => applausesForTransaction?.value.dec(value) as boolean)
)
}),
)
.subscribe({
next(applausesForTransaction: boolean) {
next(null, applausesForTransaction)
},
error: next,
})
return () => subscription.unsubscribe()
}
)
return applausesForTransaction
}

View File

@ -0,0 +1,46 @@
import useSWRSubscription from "swr/subscription"
import { getDynamicBuilder, getLookupFn } from "@polkadot-api/metadata-builders"
import type { BlockInfo } from "@polkadot-api/observable-client"
import { distinct, filter, map, mergeMap } from "rxjs"
import { useUnstableProvider } from "./useUnstableProvider"
import { useMetadata } from "./useMetadata"
interface AuthoritiesInterface {
currentSession?: Number;
}
export const useAuthorities = ({ currentSession }: AuthoritiesInterface) => {
const { chainHead$, chainId } = useUnstableProvider()
const metadata = useMetadata()
const { data: slowClapAuthorities } = useSWRSubscription(
chainHead$ && chainId && metadata
? ["slowClapAuthorities", chainHead$, currentSession, chainId, metadata]
: null,
([_, chainHead$, currentSession, chainId, metadata], { next }) => {
const { finalized$, storage$ } = chainHead$
const subscription = finalized$.pipe(
filter(Boolean),
mergeMap((blockInfo: BlockInfo) => {
const builder = getDynamicBuilder(getLookupFn(metadata))
const slowClapAuthorities = builder.buildStorage("GhostSlowClaps", "Authorities")
return storage$(blockInfo?.hash, "value", () =>
slowClapAuthorities?.keys.enc(currentSession)
).pipe(
filter(Boolean),
distinct(),
map((value: string) => slowClapAuthorities?.value.dec(value) as any)
)
}),
)
.subscribe({
next(slowClapAuthorities: any) {
next(null, slowClapAuthorities)
},
error: next,
})
return () => subscription.unsubscribe()
}
)
return slowClapAuthorities?.map((authority: any) => authority?.asHex())
}

View File

@ -1,8 +1,8 @@
import useSWR from "swr"
import { getDynamicBuilder, getLookupFn } from "@polkadot-api/metadata-builders"
import { mergeUint8, toHex } from "@polkadot-api/utils"
import { type SS58String, Enum } from "@polkadot-api/substrate-bindings"
import { mergeUint8, toHex, fromHex } from "@polkadot-api/utils"
import { type SS58String, Binary, Enum } from "@polkadot-api/substrate-bindings"
import { useUnstableProvider } from "./useUnstableProvider"
import { useMetadata } from "./useMetadata"
@ -56,7 +56,7 @@ export const useTransferCalldata = (destination: SS58String | undefined, amount:
const metadata = useMetadata()
const { data: calldata } = useSWR(
client && chainId && destination && amount && metadata
? ["metadata", client, chainId, metadata, destination, amount]
? ["transfer_allow_death", client, chainId, metadata, destination, amount]
: null,
([_, client, _chainId, metadata, destination, amount]) => {
const builder = getDynamicBuilder(getLookupFn(metadata))
@ -227,3 +227,39 @@ export const usePayeeCalldata = (expectedPayee: string | undefined, destinationR
)
return calldata
}
export const useSelfApplauseCalldata = (
networkId?: Number,
sessionIndex?: Number,
transactionHash?: string,
receiver?: string,
amount?: bigint
) => {
const { client, chainId } = useUnstableProvider()
const metadata = useMetadata()
const { data: calldata } = useSWR(
client && chainId && networkId && sessionIndex && transactionHash && receiver && amount && metadata
? ["self_applause", client, chainId, metadata, networkId, sessionIndex, transactionHash, receiver, amount]
: null,
([_, client, _chainId, metadata, networkId, sessionIndex, transactionHash, receiver, amount]) => {
const builder = getDynamicBuilder(getLookupFn(metadata))
const { codec, location } = builder.buildCall("GhostSlowClaps", "self_applause")
const txHash = new Binary(fromHex(transactionHash))
return toHex(
mergeUint8(
new Uint8Array(location),
codec.enc({
network_id: BigInt(networkId?.toString() ?? "0"),
prev_session_index: sessionIndex,
transaction_hash: txHash,
receiver: receiver,
amount: amount,
}),
)
)
}
)
return calldata
}

View File

@ -0,0 +1,51 @@
import useSWRSubscription from "swr/subscription"
import { getDynamicBuilder, getLookupFn } from "@polkadot-api/metadata-builders"
import type { BlockInfo } from "@polkadot-api/observable-client"
import { distinct, filter, map, mergeMap } from "rxjs"
import { useUnstableProvider } from "./useUnstableProvider"
import { useMetadata } from "./useMetadata"
export type SessionAuthorityInfo = {
claps: number
disabled: boolean
}
interface ClapsInSessionInterface {
currentSession?: Number;
}
export const useClapsInSession = ({ currentSession }: ClapsInSessionInterface) => {
const { chainHead$, chainId } = useUnstableProvider()
const metadata = useMetadata()
const { data: clapsInSession } = useSWRSubscription(
chainHead$ && chainId && metadata
? ["clapsInSession", chainHead$, currentSession, chainId, metadata]
: null,
([_, chainHead$, currentSession, chainId, metadata], { next }) => {
const { finalized$, storage$ } = chainHead$
const subscription = finalized$.pipe(
filter(Boolean),
mergeMap((blockInfo: BlockInfo) => {
const builder = getDynamicBuilder(getLookupFn(metadata))
const clapsInSession = builder.buildStorage("GhostSlowClaps", "ClapsInSession")
return storage$(blockInfo?.hash, "value", () =>
clapsInSession?.keys.enc(currentSession)
).pipe(
filter(Boolean),
distinct(),
map((value: string) => clapsInSession?.value.dec(value) as SessionAuthorityInfo)
)
}),
)
.subscribe({
next(clapsInSession: SessionAuthorityInfo) {
next(null, clapsInSession)
},
error: next,
})
return () => subscription.unsubscribe()
}
)
return clapsInSession
}

View File

@ -21,3 +21,21 @@ export const useExistentialDeposit = () => {
)
return existentialDeposit
}
export const useApplauseThreshold = () => {
const { chainId } = useUnstableProvider()
const metadata = useMetadata()
const { data: existentialDeposit } = useSWR(
chainId && metadata
? ["ApplauseThreshold", chainId, metadata]
: null,
([_, chainId, metadata]) => {
const builder = getDynamicBuilder(getLookupFn(metadata))
const codec = builder.buildConstant("GhostSlowClaps", "ApplauseThreshold")
const constants = metadata?.pallets?.find(obj => obj.name === "GhostSlowClaps")?.constants
const value = constants?.find(obj => obj.name === "ApplauseThreshold")?.value
return value ? codec.dec(value) : undefined
}
)
return existentialDeposit
}

View File

@ -0,0 +1,42 @@
import useSWRSubscription from "swr/subscription"
import { getDynamicBuilder, getLookupFn } from "@polkadot-api/metadata-builders"
import type { BlockInfo } from "@polkadot-api/observable-client"
import { distinct, filter, map, mergeMap } from "rxjs"
import { useUnstableProvider } from "./useUnstableProvider"
import { useMetadata } from "./useMetadata"
export const useCurrentIndex = () => {
const { chainHead$, chainId } = useUnstableProvider()
const metadata = useMetadata()
const { data: currentIndex } = useSWRSubscription(
chainHead$ && chainId && metadata
? ["currentIndex", chainHead$, chainId, metadata]
: null,
([_, chainHead$, chainId, metadata], { next }) => {
const { finalized$, storage$ } = chainHead$
const subscription = finalized$.pipe(
filter(Boolean),
mergeMap((blockInfo: BlockInfo) => {
const builder = getDynamicBuilder(getLookupFn(metadata))
const currentIndex = builder.buildStorage("Session", "CurrentIndex")
return storage$(blockInfo?.hash, "value", () =>
currentIndex?.keys.enc()
).pipe(
filter(Boolean),
distinct(),
map((value: string) => currentIndex?.value.dec(value) as Number)
)
}),
)
.subscribe({
next(currentIndex: Number) {
next(null, currentIndex)
},
error: next,
})
return () => subscription.unsubscribe()
}
)
return currentIndex
}

View File

@ -0,0 +1,54 @@
import useSWRSubscription from "swr/subscription"
import { getDynamicBuilder, getLookupFn } from "@polkadot-api/metadata-builders"
import type { BlockInfo } from "@polkadot-api/observable-client"
import { distinct, filter, map, mergeMap } from "rxjs"
import { fromHex } from "@polkadot-api/utils";
import { useUnstableProvider } from "./useUnstableProvider"
import { useMetadata } from "./useMetadata"
interface ReceivedClapsInterface {
currentSession?: Number;
txHash?: string;
argsHash?: string;
}
export const useReceivedClaps = ({ currentSession, txHash, argsHash }: ReceivedClapsInterface) => {
const { chainHead$, chainId } = useUnstableProvider()
const metadata = useMetadata()
const { data: receivedClaps } = useSWRSubscription(
chainHead$ && txHash && argsHash && currentSession && chainId && metadata
? ["receivedClaps", chainHead$, txHash, argsHash, currentSession, chainId, metadata]
: null,
([_, chainHead$, txHash, argsHash, currentSession, chainId, metadata], { next }) => {
const { finalized$, storage$ } = chainHead$
const subscription = finalized$.pipe(
filter(Boolean),
mergeMap((blockInfo: BlockInfo) => {
const builder = getDynamicBuilder(getLookupFn(metadata))
const receivedClaps = builder.buildStorage("GhostSlowClaps", "ReceivedClaps")
return storage$(blockInfo?.hash, "value", () =>
receivedClaps?.keys.enc(
currentSession,
{ asBytes: () => fromHex(txHash) },
{ asBytes: () => fromHex(argsHash) },
)
).pipe(
filter(Boolean),
distinct(),
map((value: string) => receivedClaps?.value.dec(value) as Number[])
)
}),
)
.subscribe({
next(receivedClaps: Number[]) {
next(null, receivedClaps)
},
error: next,
})
return () => subscription.unsubscribe()
}
)
return receivedClaps
}