feat: validate and dry-run transactions (#162)

* refactor: break down SubmitTx into separate files

* add validate transaction, start dry run

* add dry run

* fix worker getting stuck after forklift is disposed

* handle rejections in promiseWithTeardown
This commit is contained in:
Victor Oliva
2026-06-26 22:07:36 +02:00
committed by GitHub
parent 2e90d3f9b0
commit 61be47c722
13 changed files with 1253 additions and 181 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
{
"version": "0.1.0-autogenerated.8494276640227355333",
"version": "0.1.0-autogenerated.3203825568190824154",
"name": "@polkadot-api/descriptors",
"files": [
"dist"
+1 -1
View File
@@ -14,5 +14,5 @@ export const whitelist: WhitelistEntriesByChain = {
"query.Session.CurrentIndex",
"api.TransactionPaymentApi.query_info",
],
dotAh: ["query.*", "tx.*", "const.*"],
dotAh: ["query.*", "tx.*", "const.*", "api.*"],
}
+1
View File
@@ -19,6 +19,7 @@
"@monaco-editor/react": "^4.7.0",
"@paraspell/sdk": "^13.7.2",
"@polkadot-api/descriptors": "file:.papi/descriptors",
"@polkadot-api/forklift": "^0.3.0",
"@polkadot-api/json-rpc-provider-proxy": "^0.4.0",
"@polkadot-api/metadata-builders": "^0.14.3",
"@polkadot-api/observable-client": "^0.18.7",
+449 -14
View File
@@ -28,6 +28,9 @@ importers:
'@polkadot-api/descriptors':
specifier: file:.papi/descriptors
version: file:.papi/descriptors(polkadot-api@2.1.6(bufferutil@4.1.0)(esbuild@0.25.12)(rxjs@7.8.2))
'@polkadot-api/forklift':
specifier: ^0.3.0
version: 0.3.0(@react-rxjs/core@0.10.8(react@19.2.7)(rxjs@7.8.2))(bufferutil@4.1.0)(esbuild@0.25.12)(react@19.2.7)(typescript@6.0.3)
'@polkadot-api/json-rpc-provider-proxy':
specifier: ^0.4.0
version: 0.4.0
@@ -157,7 +160,7 @@ importers:
version: 10.0.1(eslint@10.5.0(jiti@2.7.0))
'@tailwindcss/vite':
specifier: ^4.3.1
version: 4.3.1(vite@8.0.16(@types/node@25.9.3)(esbuild@0.25.12)(jiti@2.7.0)(yaml@2.6.1))
version: 4.3.1(vite@8.0.16(@types/node@25.9.3)(esbuild@0.25.12)(jiti@2.7.0)(yaml@2.9.0))
'@types/node':
specifier: ^25.9.3
version: 25.9.3
@@ -172,7 +175,7 @@ importers:
version: 4.0.7
'@vitejs/plugin-react':
specifier: ^6.0.2
version: 6.0.2(vite@8.0.16(@types/node@25.9.3)(esbuild@0.25.12)(jiti@2.7.0)(yaml@2.6.1))
version: 6.0.2(vite@8.0.16(@types/node@25.9.3)(esbuild@0.25.12)(jiti@2.7.0)(yaml@2.9.0))
eslint:
specifier: ^10.5.0
version: 10.5.0(jiti@2.7.0)
@@ -199,7 +202,7 @@ importers:
version: 8.61.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3)
vite:
specifier: ^8.0.16
version: 8.0.16(@types/node@25.9.3)(esbuild@0.25.12)(jiti@2.7.0)(yaml@2.6.1)
version: 8.0.16(@types/node@25.9.3)(esbuild@0.25.12)(jiti@2.7.0)(yaml@2.9.0)
packages:
@@ -686,6 +689,10 @@ packages:
resolution: {integrity: sha512-VcTfNrzIf6acMSbBlmnyTWsR4KIMGc/QmIzGzIUne+Qj145H7BkYJ7Rvr7dG92tK+eT7Keack+CJME6pjPXcLQ==}
hasBin: true
'@polkadot-api/cli@0.21.6':
resolution: {integrity: sha512-1OQ2LvRSHkUcT4WRvLQFJ5PhLLN1Z1o6X1c7dguWW3eZ0bv7I9PWrbE5AyyXcy/jkceKCQ6HyISsfbBv8tIQmw==}
hasBin: true
'@polkadot-api/codegen@0.22.5':
resolution: {integrity: sha512-zwZJAlviI211zhj5i6oXkrr0crrbO3GZjBd2vC9AshY1pRmyiNLV9DZsXw4E6l4JQwdAAyNWtPdnvvB9T3TpKg==}
@@ -694,6 +701,12 @@ packages:
peerDependencies:
polkadot-api: '>=2.0.0'
'@polkadot-api/forklift@0.3.0':
resolution: {integrity: sha512-cMi/K5yR0RI55SaMS9l+xgshlhDyxg4cftl+Hgnmgdjxx9NKHTcXRt34HxAPTk2IMqAJ+0W2FPW1JSLt5tFLgA==}
hasBin: true
peerDependencies:
typescript: ^6.0.3
'@polkadot-api/ink-contracts@0.6.3':
resolution: {integrity: sha512-XqnM1VDzI5L62xgg+f8le2yEoz8QZbUKEfAfPnHMOgBj9tJiyF15FcOJmnLMO/vq3cGixqh18tzJekLG5YTxtA==}
@@ -712,6 +725,9 @@ packages:
'@polkadot-api/known-chains@0.11.5':
resolution: {integrity: sha512-KOVQRyZj43lr1W5w1lZpzzLUszjPPGV32USsGVxFdguWG7L/gH+nHyBPkyhw3GhN+8NMQD7ZCyuMuNKXbRYlDA==}
'@polkadot-api/known-chains@0.11.6':
resolution: {integrity: sha512-EEffAbSELaYgUjZN1oiPQuJLshI0kuepr+QcT3Z28CQFRfCfSvhLTI0YE4keJ43b8TnqK5VvE8g4fW/rZqTxoA==}
'@polkadot-api/ledger-signer@0.3.3':
resolution: {integrity: sha512-kb1AvGu0m0d0IDXvcXzPPwJ8+Lmx7wPQ/VzuBKPZyZWyKk6RM8Zdxnp+v15eHpdmrUvmz//BtPbId01wJmLD2Q==}
peerDependencies:
@@ -1616,139 +1632,277 @@ packages:
cpu: [arm]
os: [android]
'@rollup/rollup-android-arm-eabi@4.62.2':
resolution: {integrity: sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg==}
cpu: [arm]
os: [android]
'@rollup/rollup-android-arm64@4.62.0':
resolution: {integrity: sha512-M6s9cr10MibETyo8JsOkq+Lo1+lU6hcvb1MApnUql5qte/5hMEgzlN8/ReIKNfRV8rrqX50W1BX9zoUhC192RA==}
cpu: [arm64]
os: [android]
'@rollup/rollup-android-arm64@4.62.2':
resolution: {integrity: sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw==}
cpu: [arm64]
os: [android]
'@rollup/rollup-darwin-arm64@4.62.0':
resolution: {integrity: sha512-BqCoMoIbn0keKys+dEAdBa70EtOwV1bEsQCUgU9FdiZmmMge/Zk7LlkYGqbrdHR+Frnt0E1FOanly+rlwvvQzw==}
cpu: [arm64]
os: [darwin]
'@rollup/rollup-darwin-arm64@4.62.2':
resolution: {integrity: sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A==}
cpu: [arm64]
os: [darwin]
'@rollup/rollup-darwin-x64@4.62.0':
resolution: {integrity: sha512-SIMzST3VFNXDAbeIWDWiFCNM5qncUBDWaEV7NfE7oZbDt2mgfW4MvbKdbYiGOLoM32gbTv608UMd0XktEYSD7w==}
cpu: [x64]
os: [darwin]
'@rollup/rollup-darwin-x64@4.62.2':
resolution: {integrity: sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA==}
cpu: [x64]
os: [darwin]
'@rollup/rollup-freebsd-arm64@4.62.0':
resolution: {integrity: sha512-ezjfSQMP7ArdUsbBwbQIfwAlhE84I2iVnzQNCFSveqV42q+BmKlzVpf7mxv5EchLcoWU4y6/heFzVg1F+hodUQ==}
cpu: [arm64]
os: [freebsd]
'@rollup/rollup-freebsd-arm64@4.62.2':
resolution: {integrity: sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw==}
cpu: [arm64]
os: [freebsd]
'@rollup/rollup-freebsd-x64@4.62.0':
resolution: {integrity: sha512-9+qTWGW9AZRhnUgwtTwzNwcPlL87ngkeN0LA+q1bADvmY9aNvWaF2TFW8BZgnQPYxpDI7+rMVLivcd4V737TAQ==}
cpu: [x64]
os: [freebsd]
'@rollup/rollup-freebsd-x64@4.62.2':
resolution: {integrity: sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg==}
cpu: [x64]
os: [freebsd]
'@rollup/rollup-linux-arm-gnueabihf@4.62.0':
resolution: {integrity: sha512-T1dMEQhXA/jkJ/jyMIw9IovK8bSUq7A8kLIlvZTb/6YIVsp2zLavr4F3oyllHWo7eIVJRyE5n3tUjQJEbE1IuQ==}
cpu: [arm]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-arm-gnueabihf@4.62.2':
resolution: {integrity: sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg==}
cpu: [arm]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-arm-musleabihf@4.62.0':
resolution: {integrity: sha512-2as0LgT7qQpyceQq6VUJYnumUMUrgGQCWIiDIN9DE0/tglsk6o66uCB4f3djRawAltvfCNLyZZrsqbPA6inCsA==}
cpu: [arm]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-arm-musleabihf@4.62.2':
resolution: {integrity: sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA==}
cpu: [arm]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-arm64-gnu@4.62.0':
resolution: {integrity: sha512-bVURMg+6eNN9C/yc0aVjooZcwTTtYF4YW3xta5pP0//r3o1V8gXEHXWCndj47w/HhwsFroZrFhR+6uQP5T0n0g==}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-arm64-gnu@4.62.2':
resolution: {integrity: sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA==}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-arm64-musl@4.62.0':
resolution: {integrity: sha512-Ful8pM/2yYI83PViWdFdpZhdI8HJ5qsXANe5atypbHDf+KIBBDsZsbyy8hbXnULVvW9NsTh5DHwbcBftyLTfiw==}
cpu: [arm64]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-arm64-musl@4.62.2':
resolution: {integrity: sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ==}
cpu: [arm64]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-loong64-gnu@4.62.0':
resolution: {integrity: sha512-9Gp/DgrkzfUBmNPVTyPTvay+4xEP7M/clXpj3efXBcm6uTIVIgDg4rqUpqKXvLEuFRVuEpSAOkhgNeecvaZ4Cg==}
cpu: [loong64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-loong64-gnu@4.62.2':
resolution: {integrity: sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg==}
cpu: [loong64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-loong64-musl@4.62.0':
resolution: {integrity: sha512-m9tsJz54LUXkSYM8+8PG81B9IKK5r+2T0clMq4QrS16xFosufU7firBDAZEsDheDs7wTlP7h3++S7lMsU955HA==}
cpu: [loong64]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-loong64-musl@4.62.2':
resolution: {integrity: sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ==}
cpu: [loong64]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-ppc64-gnu@4.62.0':
resolution: {integrity: sha512-3UvJ5PNVU16aJf6M3tFI24pWzAl2/ynfbyRN3ICyQajK1lSkrnVYNnLz3v04J32qKa0FczJc22zeToc0lr2A3w==}
cpu: [ppc64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-ppc64-gnu@4.62.2':
resolution: {integrity: sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A==}
cpu: [ppc64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-ppc64-musl@4.62.0':
resolution: {integrity: sha512-vRWUAbYLGHBZS6Q8Msb2sfnf1fvJf+47t8l/TwOerM2qArzy+IeNMTHrYLHXh95h8MoatPHI5hhSZNs+mGXKPg==}
cpu: [ppc64]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-ppc64-musl@4.62.2':
resolution: {integrity: sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w==}
cpu: [ppc64]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-riscv64-gnu@4.62.0':
resolution: {integrity: sha512-c00T5SYENHAt86cfW47URaP3Us5vLC/4QO7GYud1G5VNRffCwwCuBspwqYrriuJB+5m0WFzClCn9wed0FBjKvg==}
cpu: [riscv64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-riscv64-gnu@4.62.2':
resolution: {integrity: sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg==}
cpu: [riscv64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-riscv64-musl@4.62.0':
resolution: {integrity: sha512-krrCDilhXOwFkSkO3Wm9I/f9H0L92XHHwy2fwxjukxIbh0dem8gZqOW5Y8BsHrpJv5qwlRBV+Wl4ZFyRWhUpwg==}
cpu: [riscv64]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-riscv64-musl@4.62.2':
resolution: {integrity: sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q==}
cpu: [riscv64]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-s390x-gnu@4.62.0':
resolution: {integrity: sha512-7pfYFSTc4/rUC/FtAI0Qp6QthDBCIi6/AuP1xYqFk5vanI6KnL5dWKP60OM/05LOsbwTmIcvr6eXC4CJuJ75IA==}
cpu: [s390x]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-s390x-gnu@4.62.2':
resolution: {integrity: sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg==}
cpu: [s390x]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-x64-gnu@4.62.0':
resolution: {integrity: sha512-7SDIalKeIpG0Ifogbbdn58HmSotYMlf23K3dCJEmiVd9Fg36Vmni82iPQec27N3wY4Bvbxftkxz6vSx9OcouTg==}
cpu: [x64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-x64-gnu@4.62.2':
resolution: {integrity: sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A==}
cpu: [x64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-x64-musl@4.62.0':
resolution: {integrity: sha512-eRZevouTH2i1HeAVLqJuLnt256krQkGY0TN6WsTmsIhuzbh457HuWDMakKwmi0Cjadux983CoSr8Lim2QhUIFw==}
cpu: [x64]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-x64-musl@4.62.2':
resolution: {integrity: sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg==}
cpu: [x64]
os: [linux]
libc: [musl]
'@rollup/rollup-openbsd-x64@4.62.0':
resolution: {integrity: sha512-3oVS7FLGa4U1qcvao9ylGxrjXZyUQqR8UwxEcnUEyPX53O/C/mKDZegNXTdHCP+h3e6ta/f1EN38Yif1mmZHYg==}
cpu: [x64]
os: [openbsd]
'@rollup/rollup-openbsd-x64@4.62.2':
resolution: {integrity: sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg==}
cpu: [x64]
os: [openbsd]
'@rollup/rollup-openharmony-arm64@4.62.0':
resolution: {integrity: sha512-yTB9TgfWj5wHe5QgktAgXTLLot1gvEjl1NiPPAUiCs4oPrIWFl5V4nC3GrkNdj9LaAU4s94nVrGbGOCqUpyWsg==}
cpu: [arm64]
os: [openharmony]
'@rollup/rollup-openharmony-arm64@4.62.2':
resolution: {integrity: sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA==}
cpu: [arm64]
os: [openharmony]
'@rollup/rollup-win32-arm64-msvc@4.62.0':
resolution: {integrity: sha512-5LOhoaesY3doG1c+ac/2JtgREpKoJr5bUHH8tKY0V8di7+uSV6BwLs2PlR0/yzefGOkR+wE7ZolZphHCsyG5Rw==}
cpu: [arm64]
os: [win32]
'@rollup/rollup-win32-arm64-msvc@4.62.2':
resolution: {integrity: sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg==}
cpu: [arm64]
os: [win32]
'@rollup/rollup-win32-ia32-msvc@4.62.0':
resolution: {integrity: sha512-yYkWHhmbhRTWTnWos5HC4GcPQfjlzzCNbM9e/+GXrLuaBXYA3qSDR9f0Vgufd5S8yX81U8jPKp7ZnAjZFMtRnw==}
cpu: [ia32]
os: [win32]
'@rollup/rollup-win32-ia32-msvc@4.62.2':
resolution: {integrity: sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q==}
cpu: [ia32]
os: [win32]
'@rollup/rollup-win32-x64-gnu@4.62.0':
resolution: {integrity: sha512-SoTb6lPg25xZlA2ibwQ++ahCCnH+FP0qmEuafMJ4gznZKOlXioKEAeJLgCrqjM98ACziXM9V1amFjICVL4IFoA==}
cpu: [x64]
os: [win32]
'@rollup/rollup-win32-x64-gnu@4.62.2':
resolution: {integrity: sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg==}
cpu: [x64]
os: [win32]
'@rollup/rollup-win32-x64-msvc@4.62.0':
resolution: {integrity: sha512-5L+T1fMX4RIEBoZzT0+sQ0PhTS36NULFmMXtl1TZo44TMAROIMHbZufSOjVWt/Y622BtxgxtaNOokbTDvfsrZA==}
cpu: [x64]
os: [win32]
'@rollup/rollup-win32-x64-msvc@4.62.2':
resolution: {integrity: sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA==}
cpu: [x64]
os: [win32]
'@rx-state/core@0.1.4':
resolution: {integrity: sha512-Z+3hjU2xh1HisLxt+W5hlYX/eGSDaXXP+ns82gq/PLZpkXLu0uwcNUh9RLY3Clq4zT+hSsA3vcpIGt6+UAb8rQ==}
peerDependencies:
@@ -2289,6 +2443,9 @@ packages:
'@types/node@25.9.3':
resolution: {integrity: sha512-603BddQMv3pUcr4U2dhujk83N2tTDVr/34wII2B6bJy6g+8WD6yUb11jszNs0gdi4PesVWl7ABt8nYMVpnLUcg==}
'@types/node@25.9.4':
resolution: {integrity: sha512-dszCsrKb5U7ZsVZBWiHFklTloVl0mSEnWH/iZXfZUlI4rzCUnsvGmgqfuVRHL54ugE7/wRuxEIXRa2iMZ+BG6g==}
'@types/normalize-package-data@2.4.4':
resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==}
@@ -2309,6 +2466,9 @@ packages:
'@types/trusted-types@2.0.7':
resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
'@types/ws@8.18.1':
resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==}
'@typescript-eslint/eslint-plugin@8.61.0':
resolution: {integrity: sha512-bFNvl9ZczlVb+wR2Akszf3gHfKVj/8WanXaGJ3UstTA7brNKg0cNdk6X1Psu5V7MZ2oQtzZKOEzIUehaoxbDGw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
@@ -3421,6 +3581,10 @@ packages:
resolution: {integrity: sha512-84cglkRILFxdtA8hAvLNdMrtBpPNBTrQ9/ulg0FA7xLMnD6mifv+enAIeRmvtv+WgdCE+LPGOfQmtJRrVaIVhQ==}
engines: {node: '>=20'}
ora@9.4.1:
resolution: {integrity: sha512-6VlU9MLXbjVQD04AZCMX28hVtA5bUoadvUqO76MUCVA0ilwJbMiHsITRPfyVm6p/BC0Av/BXMujx39WCe1LEqw==}
engines: {node: '>=20'}
ox@0.14.29:
resolution: {integrity: sha512-M5j87Ec4V99MQdRct/g09eWXW60g6zhHTUs1lr4deUtrPDnezBdCJTgKd7pxqTpSZBFveV0ALi9jMMuT1qKyNg==}
peerDependencies:
@@ -3516,6 +3680,10 @@ packages:
resolution: {integrity: sha512-eI9pKwWEix40kfvSzqEP6ldqOoBIN7dwD/o91TY5z8vQI12sAffpR/pOqAD1IVVwIVHDpHjkq0joBPdJD0rafA==}
hasBin: true
pino@10.3.1:
resolution: {integrity: sha512-r34yH/GlQpKZbU1BvFFqOjhISRo1MNx1tWYsYvmj6KIRHSPMT2+yHOEb1SG6NMvRoHRF0a07kCOox/9yakl1vg==}
hasBin: true
pino@9.14.0:
resolution: {integrity: sha512-8OEwKp5juEvb/MjpIc4hjqfgCNysrS94RIOMXYvpYCdm/jglrKEiAYmiumbmGhCvs+IcInsphYDFwqrjr7398w==}
hasBin: true
@@ -3530,6 +3698,12 @@ packages:
peerDependencies:
rxjs: '>=7.8.0'
polkadot-api@2.1.7:
resolution: {integrity: sha512-hSXzpbxtm4DkgBxC1m3ahqCiGTnOI75NmkWOJHlqgR3jmFYzPCMjuURpe8V1gKxzcDWMcffEmgwsM/4nm1Ox5Q==}
hasBin: true
peerDependencies:
rxjs: '>=7.8.0'
polkahub@0.7.0:
resolution: {integrity: sha512-qFSMsARaJmzjWZCF4L9EHNhKkZr995dUDw/tAYPKgwsayo8mOA+xCcYH7eJzoZOZsY2LAM4KLgM4acY1Ptt5bA==}
peerDependencies:
@@ -3689,6 +3863,9 @@ packages:
resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==}
engines: {node: '>= 12.13.0'}
real-require@1.0.0:
resolution: {integrity: sha512-P4nbQYQfePJxRSmY+v/KINxVucm4NF3p3s7pJveMTtom52FR4YGltUQLB8idDXwDDWW+eYrWDFbuzUnjoWHF7g==}
require-directory@2.1.1:
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
engines: {node: '>=0.10.0'}
@@ -3720,6 +3897,11 @@ packages:
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
hasBin: true
rollup@4.62.2:
resolution: {integrity: sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA==}
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
hasBin: true
rxjs@7.8.2:
resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==}
@@ -3865,6 +4047,10 @@ packages:
thread-stream@3.2.0:
resolution: {integrity: sha512-zLBvqpwr4Esa0kRjcrzGU6zL25lePWaCLMx0RQFrmteozIfeNdaMLpG5U7PeHzvlFkAWaRKA9/KVW4F60iB+qw==}
thread-stream@4.2.0:
resolution: {integrity: sha512-e2zZ96wSChazBsbENf/Pcm/4swHt2cEKQ92rhUjkL9GCKiTDJIaTBenjE/m9DXi0QBmTMDkFDdOomUy20A1tDQ==}
engines: {node: '>=20'}
tinyglobby@0.2.17:
resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==}
engines: {node: '>=12.0.0'}
@@ -4200,9 +4386,9 @@ packages:
yallist@3.1.1:
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
yaml@2.6.1:
resolution: {integrity: sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg==}
engines: {node: '>= 14'}
yaml@2.9.0:
resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==}
engines: {node: '>= 14.6'}
hasBin: true
yargs-parser@18.1.3:
@@ -4806,6 +4992,41 @@ snapshots:
- supports-color
- utf-8-validate
'@polkadot-api/cli@0.21.6(bufferutil@4.1.0)(esbuild@0.25.12)':
dependencies:
'@commander-js/extra-typings': 15.0.0(commander@15.0.0)
'@polkadot-api/codegen': 0.22.5
'@polkadot-api/ink-contracts': 0.6.3
'@polkadot-api/json-rpc-provider': 0.2.0
'@polkadot-api/known-chains': 0.11.6
'@polkadot-api/metadata-compatibility': 0.6.3
'@polkadot-api/observable-client': 0.18.7(rxjs@7.8.2)
'@polkadot-api/sm-provider': 0.3.6(@polkadot-api/smoldot@0.4.5(bufferutil@4.1.0))
'@polkadot-api/smoldot': 0.4.5(bufferutil@4.1.0)
'@polkadot-api/substrate-bindings': 0.20.3
'@polkadot-api/substrate-client': 0.7.0
'@polkadot-api/utils': 0.4.0
'@polkadot-api/wasm-executor': 0.2.3
'@polkadot-api/ws-middleware': 0.3.5(rxjs@7.8.2)
'@polkadot-api/ws-provider': 0.9.0(rxjs@7.8.2)
'@types/node': 25.9.4
commander: 15.0.0
execa: 9.6.1
fs.promises.exists: 1.1.4
ora: 9.4.1
read-pkg: 10.1.0
rollup: 4.62.2
rollup-plugin-esbuild: 6.2.1(esbuild@0.25.12)(rollup@4.62.2)
rxjs: 7.8.2
tsc-prog: 2.3.0(typescript@6.0.3)
typescript: 6.0.3
write-package: 7.2.0
transitivePeerDependencies:
- bufferutil
- esbuild
- supports-color
- utf-8-validate
'@polkadot-api/codegen@0.22.5':
dependencies:
'@polkadot-api/ink-contracts': 0.6.3
@@ -4818,6 +5039,35 @@ snapshots:
dependencies:
polkadot-api: 2.1.6(bufferutil@4.1.0)(esbuild@0.25.12)(rxjs@7.8.2)
'@polkadot-api/forklift@0.3.0(@react-rxjs/core@0.10.8(react@19.2.7)(rxjs@7.8.2))(bufferutil@4.1.0)(esbuild@0.25.12)(react@19.2.7)(typescript@6.0.3)':
dependencies:
'@acala-network/chopsticks-executor': 1.4.2
'@polkadot-api/metadata-builders': 0.14.3
'@polkadot-api/observable-client': 0.18.7(rxjs@7.8.2)
'@polkadot-api/substrate-bindings': 0.20.3
'@polkadot-api/substrate-client': 0.7.0
'@polkadot-api/tx-utils': 0.3.3
'@polkadot-api/ws-middleware': 0.3.5(rxjs@7.8.2)
'@polkadot-api/ws-provider': 0.9.0(rxjs@7.8.2)
'@react-rxjs/utils': 0.9.7(@react-rxjs/core@0.10.8(react@19.2.7)(rxjs@7.8.2))(react@19.2.7)(rxjs@7.8.2)
'@types/ws': 8.18.1
comlink: 4.4.2
commander: 15.0.0
pino: 10.3.1
pino-pretty: 13.1.3
polkadot-api: 2.1.7(bufferutil@4.1.0)(esbuild@0.25.12)(rxjs@7.8.2)
rxjs: 7.8.2
typescript: 6.0.3
ws: 8.21.0(bufferutil@4.1.0)
yaml: 2.9.0
transitivePeerDependencies:
- '@react-rxjs/core'
- bufferutil
- esbuild
- react
- supports-color
- utf-8-validate
'@polkadot-api/ink-contracts@0.6.3':
dependencies:
'@polkadot-api/metadata-builders': 0.14.3
@@ -4836,6 +5086,8 @@ snapshots:
'@polkadot-api/known-chains@0.11.5': {}
'@polkadot-api/known-chains@0.11.6': {}
'@polkadot-api/ledger-signer@0.3.3(@ledgerhq/hw-transport@6.35.4)':
dependencies:
'@ledgerhq/hw-transport': 6.35.4
@@ -6384,78 +6636,153 @@ snapshots:
'@rollup/rollup-android-arm-eabi@4.62.0':
optional: true
'@rollup/rollup-android-arm-eabi@4.62.2':
optional: true
'@rollup/rollup-android-arm64@4.62.0':
optional: true
'@rollup/rollup-android-arm64@4.62.2':
optional: true
'@rollup/rollup-darwin-arm64@4.62.0':
optional: true
'@rollup/rollup-darwin-arm64@4.62.2':
optional: true
'@rollup/rollup-darwin-x64@4.62.0':
optional: true
'@rollup/rollup-darwin-x64@4.62.2':
optional: true
'@rollup/rollup-freebsd-arm64@4.62.0':
optional: true
'@rollup/rollup-freebsd-arm64@4.62.2':
optional: true
'@rollup/rollup-freebsd-x64@4.62.0':
optional: true
'@rollup/rollup-freebsd-x64@4.62.2':
optional: true
'@rollup/rollup-linux-arm-gnueabihf@4.62.0':
optional: true
'@rollup/rollup-linux-arm-gnueabihf@4.62.2':
optional: true
'@rollup/rollup-linux-arm-musleabihf@4.62.0':
optional: true
'@rollup/rollup-linux-arm-musleabihf@4.62.2':
optional: true
'@rollup/rollup-linux-arm64-gnu@4.62.0':
optional: true
'@rollup/rollup-linux-arm64-gnu@4.62.2':
optional: true
'@rollup/rollup-linux-arm64-musl@4.62.0':
optional: true
'@rollup/rollup-linux-arm64-musl@4.62.2':
optional: true
'@rollup/rollup-linux-loong64-gnu@4.62.0':
optional: true
'@rollup/rollup-linux-loong64-gnu@4.62.2':
optional: true
'@rollup/rollup-linux-loong64-musl@4.62.0':
optional: true
'@rollup/rollup-linux-loong64-musl@4.62.2':
optional: true
'@rollup/rollup-linux-ppc64-gnu@4.62.0':
optional: true
'@rollup/rollup-linux-ppc64-gnu@4.62.2':
optional: true
'@rollup/rollup-linux-ppc64-musl@4.62.0':
optional: true
'@rollup/rollup-linux-ppc64-musl@4.62.2':
optional: true
'@rollup/rollup-linux-riscv64-gnu@4.62.0':
optional: true
'@rollup/rollup-linux-riscv64-gnu@4.62.2':
optional: true
'@rollup/rollup-linux-riscv64-musl@4.62.0':
optional: true
'@rollup/rollup-linux-riscv64-musl@4.62.2':
optional: true
'@rollup/rollup-linux-s390x-gnu@4.62.0':
optional: true
'@rollup/rollup-linux-s390x-gnu@4.62.2':
optional: true
'@rollup/rollup-linux-x64-gnu@4.62.0':
optional: true
'@rollup/rollup-linux-x64-gnu@4.62.2':
optional: true
'@rollup/rollup-linux-x64-musl@4.62.0':
optional: true
'@rollup/rollup-linux-x64-musl@4.62.2':
optional: true
'@rollup/rollup-openbsd-x64@4.62.0':
optional: true
'@rollup/rollup-openbsd-x64@4.62.2':
optional: true
'@rollup/rollup-openharmony-arm64@4.62.0':
optional: true
'@rollup/rollup-openharmony-arm64@4.62.2':
optional: true
'@rollup/rollup-win32-arm64-msvc@4.62.0':
optional: true
'@rollup/rollup-win32-arm64-msvc@4.62.2':
optional: true
'@rollup/rollup-win32-ia32-msvc@4.62.0':
optional: true
'@rollup/rollup-win32-ia32-msvc@4.62.2':
optional: true
'@rollup/rollup-win32-x64-gnu@4.62.0':
optional: true
'@rollup/rollup-win32-x64-gnu@4.62.2':
optional: true
'@rollup/rollup-win32-x64-msvc@4.62.0':
optional: true
'@rollup/rollup-win32-x64-msvc@4.62.2':
optional: true
'@rx-state/core@0.1.4(rxjs@7.8.2)':
dependencies:
rxjs: 7.8.2
@@ -7080,12 +7407,12 @@ snapshots:
'@tailwindcss/oxide-win32-arm64-msvc': 4.3.1
'@tailwindcss/oxide-win32-x64-msvc': 4.3.1
'@tailwindcss/vite@4.3.1(vite@8.0.16(@types/node@25.9.3)(esbuild@0.25.12)(jiti@2.7.0)(yaml@2.6.1))':
'@tailwindcss/vite@4.3.1(vite@8.0.16(@types/node@25.9.3)(esbuild@0.25.12)(jiti@2.7.0)(yaml@2.9.0))':
dependencies:
'@tailwindcss/node': 4.3.1
'@tailwindcss/oxide': 4.3.1
tailwindcss: 4.3.1
vite: 8.0.16(@types/node@25.9.3)(esbuild@0.25.12)(jiti@2.7.0)(yaml@2.6.1)
vite: 8.0.16(@types/node@25.9.3)(esbuild@0.25.12)(jiti@2.7.0)(yaml@2.9.0)
'@tanem/svg-injector@11.3.2':
dependencies:
@@ -7112,6 +7439,10 @@ snapshots:
dependencies:
undici-types: 7.24.6
'@types/node@25.9.4':
dependencies:
undici-types: 7.24.6
'@types/normalize-package-data@2.4.4': {}
'@types/prop-types@15.7.15': {}
@@ -7130,6 +7461,10 @@ snapshots:
'@types/trusted-types@2.0.7': {}
'@types/ws@8.18.1':
dependencies:
'@types/node': 25.9.3
'@typescript-eslint/eslint-plugin@8.61.0(@typescript-eslint/parser@8.61.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3)':
dependencies:
'@eslint-community/regexpp': 4.12.2
@@ -7221,10 +7556,10 @@ snapshots:
'@typescript-eslint/types': 8.61.0
eslint-visitor-keys: 5.0.1
'@vitejs/plugin-react@6.0.2(vite@8.0.16(@types/node@25.9.3)(esbuild@0.25.12)(jiti@2.7.0)(yaml@2.6.1))':
'@vitejs/plugin-react@6.0.2(vite@8.0.16(@types/node@25.9.3)(esbuild@0.25.12)(jiti@2.7.0)(yaml@2.9.0))':
dependencies:
'@rolldown/pluginutils': 1.0.1
vite: 8.0.16(@types/node@25.9.3)(esbuild@0.25.12)(jiti@2.7.0)(yaml@2.6.1)
vite: 8.0.16(@types/node@25.9.3)(esbuild@0.25.12)(jiti@2.7.0)(yaml@2.9.0)
'@wallet-standard/base@1.1.1': {}
@@ -8520,6 +8855,17 @@ snapshots:
stdin-discarder: 0.3.2
string-width: 8.2.1
ora@9.4.1:
dependencies:
chalk: 5.6.2
cli-cursor: 5.0.0
cli-spinners: 3.4.0
is-interactive: 2.0.0
is-unicode-supported: 2.1.0
log-symbols: 7.0.1
stdin-discarder: 0.3.2
string-width: 8.2.1
ox@0.14.29(typescript@6.0.3)(zod@3.22.4):
dependencies:
'@adraffy/ens-normalize': 1.11.1
@@ -8660,6 +9006,20 @@ snapshots:
sonic-boom: 4.2.1
thread-stream: 3.2.0
pino@10.3.1:
dependencies:
'@pinojs/redact': 0.4.0
atomic-sleep: 1.0.0
on-exit-leak-free: 2.1.2
pino-abstract-transport: 3.0.0
pino-std-serializers: 7.1.0
process-warning: 5.0.0
quick-format-unescaped: 4.0.4
real-require: 0.2.0
safe-stable-stringify: 2.5.0
sonic-boom: 4.2.1
thread-stream: 4.2.0
pino@9.14.0:
dependencies:
'@pinojs/redact': 0.4.0
@@ -8704,6 +9064,34 @@ snapshots:
- supports-color
- utf-8-validate
polkadot-api@2.1.7(bufferutil@4.1.0)(esbuild@0.25.12)(rxjs@7.8.2):
dependencies:
'@polkadot-api/cli': 0.21.6(bufferutil@4.1.0)(esbuild@0.25.12)
'@polkadot-api/ink-contracts': 0.6.3
'@polkadot-api/json-rpc-provider': 0.2.0
'@polkadot-api/known-chains': 0.11.6
'@polkadot-api/logs-provider': 0.2.0
'@polkadot-api/metadata-builders': 0.14.3
'@polkadot-api/metadata-compatibility': 0.6.3
'@polkadot-api/observable-client': 0.18.7(rxjs@7.8.2)
'@polkadot-api/pjs-signer': 0.7.3
'@polkadot-api/polkadot-signer': 0.1.6
'@polkadot-api/signer': 0.3.3
'@polkadot-api/sm-provider': 0.3.6(@polkadot-api/smoldot@0.4.5(bufferutil@4.1.0))
'@polkadot-api/smoldot': 0.4.5(bufferutil@4.1.0)
'@polkadot-api/substrate-bindings': 0.20.3
'@polkadot-api/substrate-client': 0.7.0
'@polkadot-api/utils': 0.4.0
'@polkadot-api/ws-middleware': 0.3.5(rxjs@7.8.2)
'@polkadot-api/ws-provider': 0.9.0(rxjs@7.8.2)
'@rx-state/core': 0.1.4(rxjs@7.8.2)
rxjs: 7.8.2
transitivePeerDependencies:
- bufferutil
- esbuild
- supports-color
- utf-8-validate
polkahub@0.7.0(@ledgerhq/hw-transport@6.35.4)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(bufferutil@4.1.0)(esbuild@0.25.12)(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(rxjs@7.8.2)(typescript@6.0.3)(use-sync-external-store@1.6.0(react@19.2.7))(zod@3.25.76):
dependencies:
'@polkahub/context': 0.6.0(@types/react@19.2.17)(bufferutil@4.1.0)(esbuild@0.25.12)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
@@ -8906,6 +9294,8 @@ snapshots:
real-require@0.2.0: {}
real-require@1.0.0: {}
require-directory@2.1.1: {}
require-main-filename@2.0.0: {}
@@ -8949,6 +9339,17 @@ snapshots:
transitivePeerDependencies:
- supports-color
rollup-plugin-esbuild@6.2.1(esbuild@0.25.12)(rollup@4.62.2):
dependencies:
debug: 4.4.3
es-module-lexer: 1.7.0
esbuild: 0.25.12
get-tsconfig: 4.14.0
rollup: 4.62.2
unplugin-utils: 0.2.5
transitivePeerDependencies:
- supports-color
rollup@4.62.0:
dependencies:
'@types/estree': 1.0.9
@@ -8980,6 +9381,37 @@ snapshots:
'@rollup/rollup-win32-x64-msvc': 4.62.0
fsevents: 2.3.3
rollup@4.62.2:
dependencies:
'@types/estree': 1.0.9
optionalDependencies:
'@rollup/rollup-android-arm-eabi': 4.62.2
'@rollup/rollup-android-arm64': 4.62.2
'@rollup/rollup-darwin-arm64': 4.62.2
'@rollup/rollup-darwin-x64': 4.62.2
'@rollup/rollup-freebsd-arm64': 4.62.2
'@rollup/rollup-freebsd-x64': 4.62.2
'@rollup/rollup-linux-arm-gnueabihf': 4.62.2
'@rollup/rollup-linux-arm-musleabihf': 4.62.2
'@rollup/rollup-linux-arm64-gnu': 4.62.2
'@rollup/rollup-linux-arm64-musl': 4.62.2
'@rollup/rollup-linux-loong64-gnu': 4.62.2
'@rollup/rollup-linux-loong64-musl': 4.62.2
'@rollup/rollup-linux-ppc64-gnu': 4.62.2
'@rollup/rollup-linux-ppc64-musl': 4.62.2
'@rollup/rollup-linux-riscv64-gnu': 4.62.2
'@rollup/rollup-linux-riscv64-musl': 4.62.2
'@rollup/rollup-linux-s390x-gnu': 4.62.2
'@rollup/rollup-linux-x64-gnu': 4.62.2
'@rollup/rollup-linux-x64-musl': 4.62.2
'@rollup/rollup-openbsd-x64': 4.62.2
'@rollup/rollup-openharmony-arm64': 4.62.2
'@rollup/rollup-win32-arm64-msvc': 4.62.2
'@rollup/rollup-win32-ia32-msvc': 4.62.2
'@rollup/rollup-win32-x64-gnu': 4.62.2
'@rollup/rollup-win32-x64-msvc': 4.62.2
fsevents: 2.3.3
rxjs@7.8.2:
dependencies:
tslib: 2.8.1
@@ -9102,6 +9534,10 @@ snapshots:
dependencies:
real-require: 0.2.0
thread-stream@4.2.0:
dependencies:
real-require: 1.0.0
tinyglobby@0.2.17:
dependencies:
fdir: 6.5.0(picomatch@4.0.4)
@@ -9262,7 +9698,7 @@ snapshots:
- utf-8-validate
- zod
vite@8.0.16(@types/node@25.9.3)(esbuild@0.25.12)(jiti@2.7.0)(yaml@2.6.1):
vite@8.0.16(@types/node@25.9.3)(esbuild@0.25.12)(jiti@2.7.0)(yaml@2.9.0):
dependencies:
lightningcss: 1.32.0
picomatch: 4.0.4
@@ -9274,7 +9710,7 @@ snapshots:
esbuild: 0.25.12
fsevents: 2.3.3
jiti: 2.7.0
yaml: 2.6.1
yaml: 2.9.0
web-streams-polyfill@3.3.3: {}
@@ -9337,8 +9773,7 @@ snapshots:
yallist@3.1.1: {}
yaml@2.6.1:
optional: true
yaml@2.9.0: {}
yargs-parser@18.1.3:
dependencies:
+224
View File
@@ -0,0 +1,224 @@
import { ActionButton } from "@/components/ActionButton"
import { JsonDisplay } from "@/components/JsonDisplay"
import {
Dialog,
DialogBody,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog"
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/components/ui/tooltip"
import { SystemEvent } from "@polkadot-api/observable-client"
import { state, useStateObservable } from "@react-rxjs/core"
import { createSignal } from "@react-rxjs/utils"
import { CheckCircle, CircleAlert, CircleX, Loader2, Play } from "lucide-react"
import { FC, ReactNode, useEffect, useState } from "react"
import { combineLatest, startWith, switchMap, takeUntil } from "rxjs"
import { txValidity$ } from "./Estimates"
import { transaction$, txOptions$ } from "./submit.state"
import { dryRun$ } from "./validate"
const [triggerDryRun$, dryRun] = createSignal()
const [cancelDryRun$, cancelDryRun] = createSignal()
const dryRunResult$ = state(
triggerDryRun$.pipe(
switchMap(() =>
combineLatest([transaction$, txOptions$]).pipe(
switchMap(([tx, txOptions], idx) => {
// any late change interrupts and cancels the dry run
if (idx > 0 || !tx) return [null]
return dryRun$(tx, txOptions).pipe(
startWith({
type: "running" as const,
value: undefined,
}),
)
}),
takeUntil(cancelDryRun$),
),
),
),
null,
)
export const DryRun = () => {
const validity = useStateObservable(txValidity$)
const [open, setOpen] = useState(false)
useEffect(() => {
const sub = dryRunResult$.subscribe()
return () => sub.unsubscribe()
}, [])
const disabled = validity?.type !== "valid"
const button = (
<ActionButton
className="flex w-full items-center justify-center gap-2 rounded-md border border-border bg-background py-2.5 text-sm font-semibold text-foreground hover:bg-foreground/5"
disabled={disabled}
type="button"
>
<Play className="h-4 w-4" />
Dry Run
</ActionButton>
)
return disabled ? (
<Tooltip>
<TooltipTrigger asChild>
<div className="w-full cursor-default">{button}</div>
</TooltipTrigger>
<TooltipContent>
Can only dry run transactions that have passed validation
</TooltipContent>
</Tooltip>
) : (
<Dialog
open={open}
onOpenChange={(open) => {
setOpen(open)
if (open) {
dryRun()
} else {
cancelDryRun()
}
}}
>
<DialogTrigger asChild>{button}</DialogTrigger>
<DialogContent className="max-w-3xl">
<DialogHeader>
<DialogTitle>Dry Run</DialogTitle>
</DialogHeader>
<DialogBody className="space-y-4">
<DryRunModalContent />
</DialogBody>
</DialogContent>
</Dialog>
)
}
const DryRunModalContent: FC = () => {
const result = useStateObservable(dryRunResult$)
if (!result || result.type === "no-signer") {
// Should be unreachable
return null
}
if (result.type === "running") {
return (
<div className="flex items-center gap-3 rounded-md border border-dashed border-border bg-background/60 p-4 text-sm text-muted-foreground">
<Loader2 className="h-4 w-4 animate-spin" />
Simulating
</div>
)
}
if (result.type === "invalid" || result.type === "error") {
return (
<div
className="rounded-md border border-red-500/30 bg-red-500/5 p-4 text-red-700 dark:text-red-300"
role="alert"
>
<div className="flex items-start gap-2">
<CircleAlert className="mt-0.5 h-4 w-4 shrink-0" />
<div className="min-w-0 space-y-2">
<div className="text-sm font-medium">
Rejected by transaction validation
</div>
<div className="text-sm">
<JsonDisplay src={result.value} />
</div>
</div>
</div>
</div>
)
}
const { ok, dispatchError, events } = result.value
const passed = ok && !dispatchError
return (
<div className="space-y-4">
<div
className={
passed
? "rounded-md border border-emerald-500/30 bg-emerald-500/5 p-4"
: "rounded-md border border-red-500/30 bg-red-500/5 p-4"
}
>
<div className="flex items-start gap-2">
{passed ? (
<CheckCircle className="mt-0.5 h-4 w-4 shrink-0 text-emerald-600 dark:text-emerald-300" />
) : (
<CircleX className="mt-0.5 h-4 w-4 shrink-0 text-red-600 dark:text-red-300" />
)}
<div>
<div className="text-sm font-medium">
{passed ? "Transaction succeeded" : "Transaction failed"}
</div>
</div>
</div>
</div>
{dispatchError ? (
<DryRunSection title="Dispatch Error">
<JsonDisplay src={dispatchError} />
</DryRunSection>
) : null}
<DryRunSection title="Events">
<ol className="divide-y divide-border rounded-md border border-border">
{events.map((event, index) => (
<DryRunEvent key={index} event={event} />
))}
</ol>
</DryRunSection>
</div>
)
}
const DryRunSection: FC<{ title: string; children: ReactNode }> = ({
title,
children,
}) => (
<section className="space-y-2">
<h3 className="text-xs font-medium uppercase tracking-wide text-muted-foreground">
{title}
</h3>
{children}
</section>
)
type FlattenedEvent = SystemEvent & SystemEvent["event"]
const DryRunEvent: FC<{ event: FlattenedEvent }> = ({ event }) => {
const [expanded, setExpanded] = useState(false)
const eventName = getEventName(event)
return (
<li>
<button
className="flex w-full items-center justify-between gap-3 px-3 py-2 text-left text-sm hover:bg-foreground/5"
type="button"
onClick={() => setExpanded((v) => !v)}
>
<span className="min-w-0 truncate font-mono">{eventName}</span>
</button>
{expanded ? (
<div className="max-h-80 overflow-auto border-t border-border px-3 py-2 bg-foreground/5">
<JsonDisplay src={event.value.value} />
</div>
) : null}
</li>
)
}
const getEventName = (event: FlattenedEvent) => {
const pallet = event.type
const name = event.value.type
return `${pallet}.${name}`
}
+142
View File
@@ -0,0 +1,142 @@
import { TokenAmount } from "@/components/TokenAmount"
import { client$ } from "@/state/chains/chain.state"
import {
getAccountGenericAddress,
getAccountPublicKey,
selectedAccount$,
} from "@/state/polkahub"
import { polkadot_people } from "@polkadot-api/descriptors"
import {
liftSuspense,
state,
SUSPENSE,
useStateObservable,
} from "@react-rxjs/core"
import { switchMapSuspended } from "@react-rxjs/utils"
import { CheckCircle, CircleX, Loader2 } from "lucide-react"
import { FC, ReactNode } from "react"
import { catchError, combineLatest, map, of, switchMap, timer } from "rxjs"
import { transaction$, txOptions$ } from "./submit.state"
import { validate$ } from "./validate"
const accountBalance$ = state(
combineLatest([selectedAccount$, client$]).pipe(
switchMapSuspended(([account, client]) =>
account
? client
.getTypedApi(polkadot_people)
.query.System.Account.getValue(getAccountGenericAddress(account))
: [],
),
liftSuspense(),
map((v) => (v === SUSPENSE ? null : v)),
map((v) => {
if (!v) return null
const { reserved, free, frozen } = v.data
const total = reserved + free
// TODO ED
const untouchable = total == 0n ? 0n : maxBigInt(frozen - reserved, 0n)
return free - untouchable
}),
),
null,
)
export const paymentInfo$ = state(
combineLatest([transaction$, selectedAccount$, txOptions$]).pipe(
switchMapSuspended(([tx, account, txOptions]) => {
if (!tx || !account) return [null]
// Adding a small delay for debouncing quick input changes
return timer(200).pipe(
switchMap(() =>
tx.getPaymentInfo(getAccountPublicKey(account), txOptions),
),
catchError((ex) => {
console.error(ex)
return of(null)
}),
)
}),
liftSuspense(),
map((v) => (v === SUSPENSE ? null : v)),
),
null,
)
export const txValidity$ = state(
combineLatest({
tx: transaction$,
txOptions: txOptions$,
}).pipe(
switchMapSuspended(({ tx, txOptions }) =>
tx ? timer(500).pipe(switchMap(() => validate$(tx, txOptions))) : [null],
),
liftSuspense(),
map((v) => (v === SUSPENSE ? null : v)),
),
null,
)
export const Estimates: FC = () => {
const paymentInfo = useStateObservable(paymentInfo$)
const balance = useStateObservable(accountBalance$)
const validity = useStateObservable(txValidity$)
const loader = <Loader2 className="h-4 w-4 animate-spin" />
const renderValidity = () => {
if (!validity) return loader
if (validity.type === "valid") {
return (
<div className="flex items-center gap-1 font-sans">
Valid
<CheckCircle size={20} className="text-green-600" />
</div>
)
}
if (validity.type === "invalid") {
return (
<div className="flex items-center gap-1 font-sans">
{`Invalid: ${validity.value}`}
<CircleX size={20} className="text-red-400" />
</div>
)
}
return "N/A"
}
return (
<section className="mx-4 space-y-3 border-t border-border py-4">
<h3 className="text-sm font-medium">Submission</h3>
<EstimateRow
label="Estimated fee"
value={
paymentInfo ? (
<TokenAmount>{paymentInfo.partial_fee}</TokenAmount>
) : (
loader
)
}
/>
<EstimateRow
label="Account spendable balance"
value={balance == null ? loader : <TokenAmount>{balance}</TokenAmount>}
/>
<EstimateRow label="Validity" value={renderValidity()} />
</section>
)
}
const EstimateRow: FC<{ label: string; value: ReactNode }> = ({
label,
value,
}) => (
<div className="flex items-start justify-between gap-3 py-1.5">
<span className="text-sm text-muted-foreground">{label}</span>
<span className="text-right font-mono text-sm">{value}</span>
</div>
)
const maxBigInt = (a: bigint, b: bigint) => (a > b ? a : b)
+24 -156
View File
@@ -1,6 +1,5 @@
import { ActionButton } from "@/components/ActionButton"
import { Spinner } from "@/components/Icons"
import { TokenAmount } from "@/components/TokenAmount"
import { Dialog, DialogTrigger } from "@/components/ui/dialog"
import {
Select,
@@ -9,15 +8,9 @@ import {
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
import { createState } from "@/lib/externalState"
import { PolkahubModalBasedManagers } from "@/pages/Accounts/Providers"
import { client$, unsafeApi$ } from "@/state/chains/chain.state"
import {
getAccountGenericAddress,
getAccountPublicKey,
selectedAccount$,
} from "@/state/polkahub"
import { polkadot_people } from "@polkadot-api/descriptors"
import { unsafeApi$ } from "@/state/chains/chain.state"
import { getAccountGenericAddress, selectedAccount$ } from "@/state/polkahub"
import {
Button,
DialogBody,
@@ -33,13 +26,8 @@ import {
useStateObservable,
withDefault,
} from "@react-rxjs/core"
import {
createSignal,
mergeWithKey,
switchMapSuspended,
} from "@react-rxjs/utils"
import { switchMapSuspended } from "@react-rxjs/utils"
import { ChevronLeft, Send, Settings, WalletCards } from "lucide-react"
import { TxOptions } from "polkadot-api"
import {
ModalContext,
PjsWalletButtons,
@@ -47,19 +35,23 @@ import {
useSelectedAccount,
} from "polkahub"
import { FC, forwardRef, ReactNode, useState } from "react"
import {
catchError,
combineLatest,
distinctUntilChanged,
map,
of,
scan,
switchMap,
timer,
} from "rxjs"
import { callData$ } from "../componentValue.state"
import { catchError, map, switchMap, timer } from "rxjs"
import { CustomSignedExt, customSignedExtensions$ } from "../CustomSignedExt"
import { trackTx } from "../ExtrinsicsWorkspaceEntry"
import { Estimates } from "./Estimates"
import { DryRun } from "./DryRun"
import {
blurNonce,
DEFAULT_MORTAL,
mortality$,
nonce$,
setMortality,
setNonce,
setTip,
tip$,
transaction$,
txOptions$,
} from "./submit.state"
import { SelectAccount } from "./SubmitTxForm"
const customExtensionsCount$ = state(
@@ -76,8 +68,9 @@ const customExtensionsCount$ = state(
null,
)
const [nonceChanged$, setNonce] = createSignal<string>()
const [nonceBlurred$, blurNonce] = createSignal()
// powers of 2 from 4 to 16 (incl)
const periodOptions = new Array(16 - 4 + 1).fill(0).map((_, i) => 1 << (i + 4))
const chainNonce$ = unsafeApi$.pipeState(
switchMapSuspended((api) =>
selectedAccount$.pipe(
@@ -100,107 +93,6 @@ const chainNonce$ = unsafeApi$.pipeState(
map((v) => (v === SUSPENSE ? null : (v as number))),
withDefault(null),
)
const isIntegerStr = (str: string) => /^\d+$/.test(str)
const nonce$ = state(
mergeWithKey({
nonceChanged$,
nonceBlurred$,
}).pipe(
scan(
(acc, v) =>
v.type === "nonceChanged$" ? v.payload : isIntegerStr(acc) ? acc : "",
"",
),
distinctUntilChanged(),
),
"",
)
type Mortality = NonNullable<TxOptions<any, any>["mortality"]>
const DEFAULT_MORTAL = {
mortal: true,
period: 64,
}
// powers of 2 from 4 to 16 (incl)
const periodOptions = new Array(16 - 4 + 1).fill(0).map((_, i) => 1 << (i + 4))
const [mortality$, setMortality] = createState<Mortality>(DEFAULT_MORTAL)
const [tip$, setTip] = createState("0")
const transaction$ = state(
combineLatest([unsafeApi$, callData$]).pipe(
switchMapSuspended(([unsafeApi, callData]) =>
callData ? unsafeApi.txFromCallData(callData) : [null],
),
liftSuspense(),
map((v) => (v === SUSPENSE ? null : v)),
),
null,
)
const txOptions$ = state(
combineLatest([
nonce$.pipe(map((v) => (isIntegerStr(v) ? Number(v) : null))),
mortality$,
tip$.pipe(map((v) => (isIntegerStr(v) ? BigInt(v) : null))),
customSignedExtensions$,
]).pipe(
map(([nonce, mortality, tip, signedExt]): TxOptions<any, any> => {
return {
mortality,
nonce: nonce ?? undefined,
tip: tip ?? undefined,
customSignedExtensions: signedExt,
}
}),
),
{} satisfies TxOptions<any, any>,
)
export const paymentInfo$ = state(
combineLatest([transaction$, selectedAccount$, txOptions$]).pipe(
switchMapSuspended(([tx, account, txOptions]) => {
if (!tx || !account) return [null]
// Adding a small delay for debouncing quick input changes
return timer(200).pipe(
switchMap(() =>
tx.getPaymentInfo(getAccountPublicKey(account), txOptions),
),
catchError((ex) => {
console.error(ex)
return of(null)
}),
)
}),
liftSuspense(),
map((v) => (v === SUSPENSE ? null : v)),
),
null,
)
const accountBalance$ = state(
combineLatest([selectedAccount$, client$]).pipe(
switchMapSuspended(([account, client]) =>
account
? client
.getTypedApi(polkadot_people)
.query.System.Account.getValue(getAccountGenericAddress(account))
: [],
),
liftSuspense(),
map((v) => (v === SUSPENSE ? null : v)),
map((v) => {
if (!v) return null
const { reserved, free, frozen } = v.data
const total = reserved + free
// TODO ED
const untouchable = total == 0n ? 0n : maxBigInt(frozen - reserved, 0n)
return free - untouchable
}),
),
null,
)
export const SubmitExtrinsic = forwardRef<HTMLElement>((_, ref) => {
const [account] = useSelectedAccount()
@@ -208,8 +100,6 @@ export const SubmitExtrinsic = forwardRef<HTMLElement>((_, ref) => {
const nonce = useStateObservable(nonce$)
const mortality = useStateObservable(mortality$)
const tip = useStateObservable(tip$)
const paymentInfo = useStateObservable(paymentInfo$)
const balance = useStateObservable(accountBalance$)
const tx = useStateObservable(transaction$)
const txOptions = useStateObservable(txOptions$)
const [isSigning, setIsSigning] = useState(false)
@@ -326,25 +216,11 @@ export const SubmitExtrinsic = forwardRef<HTMLElement>((_, ref) => {
<CustomSignedExtDialog />
</section>
<section className="mx-4 space-y-3 border-t border-border py-4">
<h3 className="text-sm font-medium">Fees</h3>
<FeeRow
label="Estimated fee"
value={
paymentInfo ? (
<TokenAmount>{paymentInfo.partial_fee}</TokenAmount>
) : (
"…"
)
}
/>
<FeeRow
label="Account spendable balance"
value={balance == null ? "…" : <TokenAmount>{balance}</TokenAmount>}
/>
</section>
<Estimates />
<section className="mx-4 space-y-3 border-t border-border py-4">
<DryRun />
<ActionButton
className="flex w-full items-center justify-center gap-2 rounded-md py-2.5 text-sm font-semibold"
disabled={!tx || !account?.signer || isSigning}
@@ -471,11 +347,3 @@ const SubmitRow: FC<{
{children}
</div>
)
const FeeRow: FC<{ label: string; value: ReactNode }> = ({ label, value }) => (
<div className="flex items-center justify-between gap-3 py-1.5">
<span className="text-sm text-muted-foreground">{label}</span>
<span className="text-right font-mono text-sm">{value}</span>
</div>
)
const maxBigInt = (a: bigint, b: bigint) => (a > b ? a : b)
@@ -0,0 +1,72 @@
import { createState } from "@/lib/externalState"
import { unsafeApi$ } from "@/state/chains/chain.state"
import { liftSuspense, state, SUSPENSE } from "@react-rxjs/core"
import {
createSignal,
mergeWithKey,
switchMapSuspended,
} from "@react-rxjs/utils"
import { TxOptions } from "polkadot-api"
import { combineLatest, distinctUntilChanged, map, scan } from "rxjs"
import { callData$ } from "../componentValue.state"
import { customSignedExtensions$ } from "../CustomSignedExt"
export const [nonceChanged$, setNonce] = createSignal<string>()
export const [nonceBlurred$, blurNonce] = createSignal()
const isIntegerStr = (str: string) => /^\d+$/.test(str)
export const nonce$ = state(
mergeWithKey({
nonceChanged$,
nonceBlurred$,
}).pipe(
scan(
(acc, v) =>
v.type === "nonceChanged$" ? v.payload : isIntegerStr(acc) ? acc : "",
"",
),
),
"",
)
type Mortality = NonNullable<TxOptions<any, any>["mortality"]>
export const DEFAULT_MORTAL = {
mortal: true,
period: 64,
}
export const [mortality$, setMortality] = createState<Mortality>(DEFAULT_MORTAL)
export const [tip$, setTip] = createState("0")
export const transaction$ = state(
combineLatest([unsafeApi$, callData$]).pipe(
switchMapSuspended(([unsafeApi, callData]) =>
callData ? unsafeApi.txFromCallData(callData) : [null],
),
liftSuspense(),
map((v) => (v === SUSPENSE ? null : v)),
),
null,
)
export const txOptions$ = state(
combineLatest([
nonce$.pipe(
distinctUntilChanged(),
map((v) => (isIntegerStr(v) ? Number(v) : null)),
),
mortality$,
tip$.pipe(map((v) => (isIntegerStr(v) ? BigInt(v) : null))),
customSignedExtensions$,
]).pipe(
map(([nonce, mortality, tip, signedExt]): TxOptions<any, any> => {
return {
mortality,
nonce: nonce ?? undefined,
tip: tip ?? undefined,
customSignedExtensions: signedExt,
}
}),
),
{} satisfies TxOptions<any, any>,
)
+320
View File
@@ -0,0 +1,320 @@
import { chainClient$ } from "@/state/chains/chain.state"
import { selectedAccount$ } from "@/state/polkahub"
import {
DotAh,
TransactionValidityTransactionSource,
} from "@polkadot-api/descriptors"
import { forklift, fromWorker } from "@polkadot-api/forklift"
import Worker from "@polkadot-api/forklift/worker?worker"
import { ChainHead$ } from "@polkadot-api/observable-client"
import { shareLatest } from "@react-rxjs/core"
import {
AccountId,
Binary,
createClient,
Enum,
HexString,
InvalidTxError,
PolkadotClient,
Transaction,
} from "polkadot-api"
import { getPolkadotSigner } from "polkadot-api/signer"
import { AccountAddress } from "polkahub"
import {
catchError,
combineLatest,
filter,
firstValueFrom,
lastValueFrom,
map,
Observable,
switchMap,
take,
TeardownLogic,
toArray,
} from "rxjs"
const promiseWithTeardown = <T>(
deferFn: () => Promise<{
value: T
teardown: TeardownLogic
}>,
) =>
new Observable<T>((obs) => {
deferFn().then(
({ value, teardown }) => {
obs.add(teardown)
obs.next(value)
},
(ex) => obs.error(ex),
)
})
const forkLift$ = chainClient$.pipe(
switchMap(({ client, chainHead }) =>
promiseWithTeardown(async () => {
const [forklift, worker] = await createTmpForklift(client, chainHead)
const forkClient = createClient(
// withLogsRecorder(console.log, forklift.serve),
forklift.serve,
)
return {
value: { forklift, forkClient },
teardown: () => {
forkClient.destroy()
forklift.destroy()
worker.terminate()
},
}
}),
),
shareLatest(),
)
const fakeSign$ = (tx: Transaction, txOptions: any) =>
combineLatest({
signer: selectedAccount$.pipe(
map((account) => (account ? createFakeSigner(account.address) : null)),
),
mockChain: forkLift$.pipe(
switchMap(async ({ forkClient }) => {
const unsafeApi = forkClient.getUnsafeApi<DotAh>()
return {
client: forkClient,
unsafeApi,
tx: await unsafeApi.txFromCallData(await tx.getEncodedData()),
}
}),
),
}).pipe(
switchMap(async ({ signer, mockChain: { tx, unsafeApi, client } }) => {
if (!signer) return null
const extrinsic = await tx.sign(signer, txOptions)
return { extrinsic, tx, unsafeApi, client }
}),
)
export const validate$ = (tx: Transaction, txOptions: any) =>
fakeSign$(tx, txOptions).pipe(
switchMap(async (evt) =>
evt
? evt.unsafeApi.apis.TaggedTransactionQueue.validate_transaction(
TransactionValidityTransactionSource.External(),
evt.extrinsic.slice(2),
(await evt.client.getFinalizedBlock()).hash,
)
: null,
),
map((evt) =>
!evt
? {
type: "no-signer" as const,
value: undefined,
}
: evt.success
? {
type: "valid" as const,
value: undefined,
}
: {
type: "invalid" as const,
value: evt.value.value.type,
},
),
catchError((ex) => {
console.error(ex)
return [
{
type: "error" as const,
value: ex.error,
},
]
}),
take(1),
)
export const dryRun$ = (tx: Transaction, txOptions: any) =>
fakeSign$(tx, txOptions).pipe(
switchMap((evt) =>
evt ? evt.client.submitAndWatch(evt.extrinsic) : [null],
),
map((evt) => {
if (!evt) {
return {
type: "no-signer" as const,
value: undefined,
}
}
if (evt.type === "txBestBlocksState" && evt.found) {
return {
type: "valid" as const,
value: {
ok: evt.ok,
dispatchError: evt.dispatchError,
events: evt.events,
},
}
}
return null
}),
catchError((ex) => {
if (ex instanceof InvalidTxError) {
return [
{
type: "invalid" as const,
value: ex.error,
},
]
}
console.error(ex)
return [
{
type: "error" as const,
value: ex.error,
},
]
}),
filter((v) => v !== null),
take(1),
)
const codeCache = new WeakMap<Uint8Array, Promise<Uint8Array>>()
const CODE_KEY = "0x3a636f6465"
const PARAMETERS_KEY =
"0xc63bdd4a39095ccf55623a6f2872bf8ac63bdd4a39095ccf55623a6f2872bf8a"
const createTmpForklift = async (
client: PolkadotClient,
chainHead: ChainHead$,
) => {
const [block] = await client.getBestBlocks()
const unhodl = client.hodlBlock(block.hash)
const metadata = await client.getMetadata(block.hash)
// forklift needs the code, which is a big-ass query (~4MB).
// PAPI doesn't have it, but we should prevent this query to be repeated by every block when the runtime is the same
if (!codeCache.has(metadata)) {
const codeP = firstValueFrom(
chainHead
.storage$(block.hash, "value", () => CODE_KEY)
.pipe(
map((v) => {
if (!v.value) {
throw new Error("Code not available")
}
return Binary.fromHex(v.value)
}),
),
)
codeCache.set(metadata, codeP)
}
// We can also optimise by pre-quering "Parameters.Parameters", which is often serialised and is mostly empty
const parametersP = firstValueFrom(
chainHead
.storage$(block.hash, "descendantsValues", () => PARAMETERS_KEY)
.pipe(
map((v) =>
Object.fromEntries(
v.value.map(({ key, value }) => [key, Binary.fromHex(value)]),
),
),
),
)
const [code, parameters] = await Promise.all([
codeCache.get(metadata)!,
parametersP,
])
const getBlock = async () => ({
blockHash: block.hash,
header: await client.getBlockHeader(block.hash),
body: await client.getBlockBody(block.hash),
})
const getStorage = async (key: HexString) => {
if (key === CODE_KEY) return code
if (key.startsWith(PARAMETERS_KEY)) return parameters[key] ?? null
return firstValueFrom(
chainHead
.storage$(block.hash, "value", () => key)
.pipe(map((v) => (v.value ? Binary.fromHex(v.value) : null))),
)
}
const getStorageBatch = async (keys: HexString[]) => {
if (!keys.length) return []
const result = new Array(keys.length)
const keyToIdx = Object.fromEntries(keys.map((key, i) => [key, i]))
const results = await lastValueFrom(
chainHead
.storageQueries$(
block.hash,
keys.map((key) => ({
type: "value",
key,
})),
)
.pipe(toArray()),
)
results.forEach(
(r) =>
(result[keyToIdx[r.key]] = r.value ? Binary.fromHex(r.value) : null),
)
return result
}
const getStorageDescendants = (prefix: HexString) => {
return firstValueFrom(
chainHead
.storage$(block.hash, "descendantsValues", () => prefix)
.pipe(
map((v) =>
Object.fromEntries(
v.value.map(({ key, value }) => [key, Binary.fromHex(value)]),
),
),
),
)
}
const getChainSpecData = client.getChainSpecData
const forkliftWorker = new Worker()
return [
forklift(
{
block: getBlock(),
getStorage,
getStorageBatch,
getStorageDescendants,
getChainSpecData,
destroy() {
unhodl()
},
},
{
mockSignatureHost: true,
disableOnIdle: true,
buildBlockMode: Enum("timer", 0),
executor: fromWorker(forkliftWorker),
},
),
forkliftWorker,
] as const
}
const decodeAddress = (address: AccountAddress) =>
address.startsWith("0x") ? Binary.fromHex(address) : AccountId().enc(address)!
export const createFakeSigner = (address: AccountAddress) =>
getPolkadotSigner(decodeAddress(address), "Sr25519", () => {
// From https://wiki.acala.network/build/sdks/homa
const signature = new Uint8Array(64)
signature.fill(0xcd)
signature.set([0xde, 0xad, 0xbe, 0xef])
return signature
})
+2 -2
View File
@@ -1,7 +1,7 @@
import { createMetadataEntryState } from "@/components/MetadataEntryInput"
import { pushWorkspaceEntry, WorkspaceEntryData } from "@/components/Workspace"
import { getHashParams } from "@/hashParams"
import { runtimeCtxAt$, unsafeApi$ } from "@/state/chains/chain.state"
import { genericUnsafeApi$, runtimeCtxAt$ } from "@/state/chains/chain.state"
import { RuntimeContext } from "@polkadot-api/observable-client"
import { state } from "@react-rxjs/core"
import { ServerCog } from "lucide-react"
@@ -108,7 +108,7 @@ export const runtimeCallToWorkspaceEntry = async (
query: RuntimeCallQuery,
): Promise<WorkspaceEntryData<RuntimeCallWorkspaceContext>> => {
const [unsafeApi, ctx] = await firstValueFrom(
combineLatest([unsafeApi$, runtimeCtxAt$(query.blockHash)]),
combineLatest([genericUnsafeApi$, runtimeCtxAt$(query.blockHash)]),
)
const query$ = from(
+2 -2
View File
@@ -5,10 +5,10 @@ import { getHashParams } from "@/hashParams"
import {
chainClient$,
client$,
genericUnsafeApi$,
runtimeCtx$,
runtimeCtxAt$,
selectedChainChanged$,
unsafeApi$,
} from "@/state/chains/chain.state"
import {
BlockInfo,
@@ -180,7 +180,7 @@ export const storageSubscriptionToWorkspaceEntry = async ({
const [ctx, unsafeApi] = await firstValueFrom(
combineLatest([
blockHash ? runtimeCtxAt$(blockHash) : runtimeCtx$,
unsafeApi$,
genericUnsafeApi$,
]),
)
const entry = getEntry(ctx, getStorageItem(ctx, pallet, item)!.item.type)
+2 -2
View File
@@ -1,7 +1,7 @@
import { createMetadataEntryState } from "@/components/MetadataEntryInput"
import { pushWorkspaceEntry, WorkspaceEntryData } from "@/components/Workspace"
import { getHashParams } from "@/hashParams"
import { runtimeCtxAt$, unsafeApi$ } from "@/state/chains/chain.state"
import { genericUnsafeApi$, runtimeCtxAt$ } from "@/state/chains/chain.state"
import { RuntimeContext } from "@polkadot-api/observable-client"
import { shareLatest, state } from "@react-rxjs/core"
import { SquareFunction } from "lucide-react"
@@ -110,7 +110,7 @@ export const viewFnCallToWorkspaceEntry = async (
call: ViewFnCall,
): Promise<WorkspaceEntryData<ViewFnWorkspaceContext>> => {
const [unsafeApi, ctx] = await firstValueFrom(
combineLatest([unsafeApi$, runtimeCtxAt$(call.blockHash)]),
combineLatest([genericUnsafeApi$, runtimeCtxAt$(call.blockHash)]),
)
const query$ = from(
+13 -3
View File
@@ -1,5 +1,6 @@
import { createChopsticksProvider } from "@/chopsticks/chopsticks"
import { getHashParams, setHashParams } from "@/hashParams"
import { DotAh } from "@polkadot-api/descriptors"
import { getDynamicBuilder, getLookupFn } from "@polkadot-api/metadata-builders"
import type {
ChainHead$,
@@ -10,10 +11,16 @@ import {
HexString,
unifyMetadata,
} from "@polkadot-api/substrate-bindings"
import { liftSuspense, sinkSuspense, state, SUSPENSE } from "@react-rxjs/core"
import {
liftSuspense,
sinkSuspense,
state,
StateObservable,
SUSPENSE,
} from "@react-rxjs/core"
import { createSignal } from "@react-rxjs/utils"
import { get, update } from "idb-keyval"
import { createClient } from "polkadot-api"
import { ChainDefinition, createClient, TypedApi } from "polkadot-api"
import { withLogsRecorder } from "polkadot-api/logs-provider"
import { fromHex, toHex } from "polkadot-api/utils"
import {
@@ -249,8 +256,11 @@ export const canSetStorage$ = state(
)
export const unsafeApi$ = chainClient$.pipeState(
map(({ client }) => client.getUnsafeApi()),
map(({ client }) => client.getUnsafeApi<DotAh>()),
)
export const genericUnsafeApi$ = unsafeApi$ as StateObservable<
TypedApi<ChainDefinition, false>
>
const uncachedRuntimeCtx$ = chainClient$.pipeState(
switchMap(({ chainHead }) => chainHead.runtime$),