From 8179726622785b1fd7b5225a23885a734b967eb1 Mon Sep 17 00:00:00 2001 From: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> Date: Thu, 12 Sep 2024 22:55:58 +0200 Subject: [PATCH] Split too large txs (#122) Co-authored-by: Nikos Kontakis --- package.json | 4 +- pnpm-lock.yaml | 253 +++++++++++------- src/components/LocksCard.tsx | 7 +- src/components/MyDelegations.tsx | 5 +- src/components/ui/button.tsx | 20 +- src/hooks/useGetDelegateTx.tsx | 87 +++--- src/hooks/useTestTx.tsx | 67 +++++ src/pages/Delegate/MultiTransactionDialog.tsx | 165 ++++++++++++ src/pages/Delegate/TooLargeDialog.tsx | 44 +++ src/pages/Delegate/index.tsx | 115 +++++--- 10 files changed, 588 insertions(+), 179 deletions(-) create mode 100644 src/hooks/useTestTx.tsx create mode 100644 src/pages/Delegate/MultiTransactionDialog.tsx create mode 100644 src/pages/Delegate/TooLargeDialog.tsx diff --git a/package.json b/package.json index 93f101c..d404668 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,8 @@ }, "dependencies": { "@polkadot-api/descriptors": "file:.papi/descriptors", + "@polkadot-labs/hdkd": "^0.0.8", + "@polkadot-labs/hdkd-helpers": "^0.0.8", "@polkadot-ui/react": "0.0.1-alpha.15", "@polkadot-ui/utils": "0.0.1-alpha.5", "@radix-ui/react-dialog": "^1.1.1", @@ -40,7 +42,7 @@ "dot-connect": "^0.6.0", "lucide-react": "^0.437.0", "next-themes": "^0.3.0", - "polkadot-api": "^1.1.0", + "polkadot-api": "^1.2.0", "react": "^18.3.1", "react-dom": "^18.3.1", "react-icons": "^5.3.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 02c6ec5..4256b67 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,10 +10,16 @@ importers: dependencies: '@polkadot-api/descriptors': specifier: file:.papi/descriptors - version: file:.papi/descriptors(polkadot-api@1.1.0(jiti@1.21.6)(postcss@8.4.42)(rxjs@7.8.1)(smoldot@2.0.30)(yaml@2.5.1)) + version: file:.papi/descriptors(polkadot-api@1.2.0(jiti@1.21.6)(postcss@8.4.42)(rxjs@7.8.1)(smoldot@2.0.30)(yaml@2.5.1)) + '@polkadot-labs/hdkd': + specifier: ^0.0.8 + version: 0.0.8 + '@polkadot-labs/hdkd-helpers': + specifier: ^0.0.8 + version: 0.0.8 '@polkadot-ui/react': specifier: 0.0.1-alpha.15 - version: 0.0.1-alpha.15(@noble/hashes@1.5.0)(@polkadot-api/substrate-bindings@0.6.3)(@polkadot-ui/assets@0.0.1-alpha.2)(@polkadot-ui/core@0.0.1-alpha.2)(@polkadot-ui/utils@0.0.1-alpha.5(@polkadot/keyring@13.0.2(@polkadot/util-crypto@13.0.2(@polkadot/util@13.0.2))(@polkadot/util@13.0.2))(@polkadot/util@13.0.2))(polkadot-api@1.1.0(jiti@1.21.6)(postcss@8.4.42)(rxjs@7.8.1)(smoldot@2.0.30)(yaml@2.5.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 0.0.1-alpha.15(@noble/hashes@1.5.0)(@polkadot-api/substrate-bindings@0.7.0)(@polkadot-ui/assets@0.0.1-alpha.2)(@polkadot-ui/core@0.0.1-alpha.2)(@polkadot-ui/utils@0.0.1-alpha.5(@polkadot/keyring@13.0.2(@polkadot/util-crypto@13.0.2(@polkadot/util@13.0.2))(@polkadot/util@13.0.2))(@polkadot/util@13.0.2))(polkadot-api@1.2.0(jiti@1.21.6)(postcss@8.4.42)(rxjs@7.8.1)(smoldot@2.0.30)(yaml@2.5.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@polkadot-ui/utils': specifier: 0.0.1-alpha.5 version: 0.0.1-alpha.5(@polkadot/keyring@13.0.2(@polkadot/util-crypto@13.0.2(@polkadot/util@13.0.2))(@polkadot/util@13.0.2))(@polkadot/util@13.0.2) @@ -52,10 +58,10 @@ importers: version: 1.1.2(@types/react-dom@18.3.0)(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@reactive-dot/core': specifier: ^0.10.0 - version: 0.10.0(polkadot-api@1.1.0(jiti@1.21.6)(postcss@8.4.42)(rxjs@7.8.1)(smoldot@2.0.30)(yaml@2.5.1)) + version: 0.10.0(polkadot-api@1.2.0(jiti@1.21.6)(postcss@8.4.42)(rxjs@7.8.1)(smoldot@2.0.30)(yaml@2.5.1)) '@reactive-dot/react': specifier: ^0.10.0 - version: 0.10.0(@types/react@18.3.5)(polkadot-api@1.1.0(jiti@1.21.6)(postcss@8.4.42)(rxjs@7.8.1)(smoldot@2.0.30)(yaml@2.5.1))(react@18.3.1) + version: 0.10.0(@types/react@18.3.5)(polkadot-api@1.2.0(jiti@1.21.6)(postcss@8.4.42)(rxjs@7.8.1)(smoldot@2.0.30)(yaml@2.5.1))(react@18.3.1) '@tanstack/react-table': specifier: ^8.20.5 version: 8.20.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -70,7 +76,7 @@ importers: version: 3.3.3 dot-connect: specifier: ^0.6.0 - version: 0.6.0(@reactive-dot/core@0.10.0(polkadot-api@1.1.0(jiti@1.21.6)(postcss@8.4.42)(rxjs@7.8.1)(smoldot@2.0.30)(yaml@2.5.1)))(@types/react@18.3.5)(react@18.3.1) + version: 0.6.0(@reactive-dot/core@0.10.0(polkadot-api@1.2.0(jiti@1.21.6)(postcss@8.4.42)(rxjs@7.8.1)(smoldot@2.0.30)(yaml@2.5.1)))(@types/react@18.3.5)(react@18.3.1) lucide-react: specifier: ^0.437.0 version: 0.437.0(react@18.3.1) @@ -78,8 +84,8 @@ importers: specifier: ^0.3.0 version: 0.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) polkadot-api: - specifier: ^1.1.0 - version: 1.1.0(jiti@1.21.6)(postcss@8.4.42)(rxjs@7.8.1)(smoldot@2.0.30)(yaml@2.5.1) + specifier: ^1.2.0 + version: 1.2.0(jiti@1.21.6)(postcss@8.4.42)(rxjs@7.8.1)(smoldot@2.0.30)(yaml@2.5.1) react: specifier: ^18.3.1 version: 18.3.1 @@ -663,12 +669,12 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} - '@polkadot-api/cli@0.8.0': - resolution: {integrity: sha512-IZGsvbMUgKL2gkVkp1ziMmtyLAN8vnGaV+Nxl5+fsalEHG+1BYK69KYk3hDzGrca3uT3rN3/CYdiVd5/Kywd4A==} + '@polkadot-api/cli@0.8.1': + resolution: {integrity: sha512-PsC6KIEqvwNpKvYc+c1ESMn+DoYWxOqks0Cqrak8NxV/cXl7WCT6/SNcGc1iu7vvZcoy2FNPWTmO0ioZieo/hw==} hasBin: true - '@polkadot-api/codegen@0.9.0': - resolution: {integrity: sha512-ERtZAfgKBp9iQ7JZD7ag+TO0G/Dt/RVoHeE3V6n1hNQKNxIOy7w12fw7yMPEieS5walvI+e8otsnB+GpoE/5FQ==} + '@polkadot-api/codegen@0.10.0': + resolution: {integrity: sha512-zsE8cVaRvJeo+d7eR338114L4NrmdU15dX9kBatYBbXY/3wH+19jHHxijE3QqoPWxKFX77TrQaf9035hcf7Bkw==} '@polkadot-api/descriptors@file:.papi/descriptors': resolution: {directory: .papi/descriptors, type: directory} @@ -684,29 +690,29 @@ packages: '@polkadot-api/json-rpc-provider@0.0.3': resolution: {integrity: sha512-493nHN9RnLxNPMxQ3oekEFnRomAqWWh3j88FhANoPu5iINBG73l4ccB9QBPVs3JWXq4SwfLVmb6L15ryM69g/g==} - '@polkadot-api/known-chains@0.5.0': - resolution: {integrity: sha512-l13QuHNu+FcWSo1t/QbgBdVR1ztpYQYjDlJQ2X+b2LAodeusElvDQjFi2Fnl0TDX4fza5BA/YQsShwkXcslvYA==} + '@polkadot-api/known-chains@0.5.1': + resolution: {integrity: sha512-+UySd2KY7OkBifdGJHgJCJtHp8VjWlwzdRkJA7uNlpxu0vcP2aUhgGu9kqssVZytlq5xHzY/RJ6Tgf/lCKC/WA==} - '@polkadot-api/logs-provider@0.0.4': - resolution: {integrity: sha512-9MGjXJmujtFA9HsgS4TejTDSD3unyOgMBSbYmz9iWcddCmw+0M5png+F2VW90Ua76kLzWnJ3V1mqFkTapgIipg==} + '@polkadot-api/logs-provider@0.0.5': + resolution: {integrity: sha512-BGjFD6X4XWOSZ0TNRXt0LyiLVkZWlSIuRreQsB2tAeo19/S9cZSKyPlP/FOFe9HXLZhKscLB35TqXH86q9oh3w==} - '@polkadot-api/metadata-builders@0.6.0': - resolution: {integrity: sha512-Q+7oinRbGuWDaj1Xl4PYGcQMy/RIhPErHgDbqR2rFSDt1vjhd6l1XMBWq2uKpwHS4ZNWEvy3r6/I1QbDTLIj2w==} + '@polkadot-api/metadata-builders@0.7.0': + resolution: {integrity: sha512-TYbcgVz3f8fTn3lddT3NPvKA4/ELT3GifxMFSTXH4rennzx63yN1yhQHsbEOCW315LVL1gBQU0T7db3s3Dj1+g==} - '@polkadot-api/metadata-compatibility@0.1.3': - resolution: {integrity: sha512-27VTDfxlP5jBkGjJY+dNutT18q6XK1E9kI5ayfmm5sEZivvsS7oKJ34Pb5WTRoVI+V0YZIJtHjxN+K2zehn3yA==} + '@polkadot-api/metadata-compatibility@0.1.4': + resolution: {integrity: sha512-O4JIJAIY0lAfTpcHPYhhrvr9TGRQiZ4tAbj8Yu+3TchLdeoCo88ozpOXeyEh+IWTo1KZSFYEBvR/CAeb1Nf76Q==} - '@polkadot-api/observable-client@0.5.2': - resolution: {integrity: sha512-E1hRi2YkcOAHXpXT4HpzowjZoctrhuM/8rbuDQju+Xin/PbXpnzol5UOMyToXdyZM4vKa4tc89PJB0TkoANLbA==} + '@polkadot-api/observable-client@0.5.3': + resolution: {integrity: sha512-ySVwu0+IitjUboU/2JoIhsurombDhRa2U2XozqoSv+jKaOdWwc5EITpnfm1ioCS4XkINUluQx2LenERZuhV8/Q==} peerDependencies: '@polkadot-api/substrate-client': 0.2.1 rxjs: '>=7.8.0' - '@polkadot-api/pjs-signer@0.4.1': - resolution: {integrity: sha512-v3/EMHxrUqGRRWdgvqADV8fR6aiN2OP2gCvuT9Nvb8alQugZjmjDPl6Szar2jpAQmxSefYpVbCdNU+PmryqQqg==} + '@polkadot-api/pjs-signer@0.4.2': + resolution: {integrity: sha512-YsN666209dTIdM2JX2L3RMUX3YAYKMaoEE8oguTXh/2t78gRDMj7Oq02QwatcmR4XyZZStGUy30ye9mcnsEnag==} - '@polkadot-api/polkadot-sdk-compat@2.0.0': - resolution: {integrity: sha512-rxBDZyFLSH5kCe2TSUm3qSenVqoj3kx+djUYH3Cj0H032Lra/VOh4E9WLkGiPhF1fZsROGO2JQS2KFRXZovlkw==} + '@polkadot-api/polkadot-sdk-compat@2.1.0': + resolution: {integrity: sha512-AH8nEwJsHj8Sns1GHBtbXvfWz4FR33tVfcXG8/AkpnaAMkGfCZKcL3z7xwpkhOywl4IRySATyEpuKFCsQAKRKw==} '@polkadot-api/polkadot-signer@0.1.4': resolution: {integrity: sha512-s7YfsE2wtt5J0+x+C4Ey/aRdmsZxp85SXay4614nJeHRndLVrGVMXby/BeZ6JchbyeyYg2+urmtfCvqMDiSS7g==} @@ -725,6 +731,9 @@ packages: '@polkadot-api/substrate-bindings@0.6.3': resolution: {integrity: sha512-Z/9B5Rhr478YgFLpo32sr8cY/zUeHj9DZnxX76OTJM+Kiq2rXfyYAONr8vAleJXxP7y1mUDBeII4nS9+uBbFXA==} + '@polkadot-api/substrate-bindings@0.7.0': + resolution: {integrity: sha512-cTzrPUAV+/iC2fa3JEGBilnZXttHS87PeaWTWGL/4hP/cXL+Xz9EAjJwGrp7MhwnSgAVBMD2l/ZoQvI4Bbm5jw==} + '@polkadot-api/substrate-client@0.2.1': resolution: {integrity: sha512-YyHn0JzqY75PHs6Y/fEMsMYTH6s4QWpGFqR+O14Qu7reo++a0sTl+pHfXDF16CIq4aGlQJ5wJGHtzWpgnW+INA==} @@ -737,6 +746,15 @@ packages: '@polkadot-api/ws-provider@0.2.0': resolution: {integrity: sha512-R4B+tUR1H9j6AH2p4Dvv5+aV4/DC/TpKOqB1C5ON2Qvlc0TrH7x4HrZLn82Kq7iwUtnxkrU6esze2UgDGoVKPw==} + '@polkadot-labs/hdkd-helpers@0.0.8': + resolution: {integrity: sha512-nTJOinTKuINHwKsUXR+Q1Hld0DU+EYVxcfQqiJz9PH8L+48K3gPfpAHDApIuOW6Uq6yVb5/pgcDPNCaJS5nYsg==} + + '@polkadot-labs/hdkd@0.0.8': + resolution: {integrity: sha512-gpcHtJ28FfugQ7uRj+EQzdhbtm1qchcW9hC1Gsf+IvMiB//Dx/K3tPS9uKtPDu7OoX4p/McpPcLm7IomdlYJbg==} + + '@polkadot-labs/schnorrkel-wasm@0.0.5': + resolution: {integrity: sha512-oUKF4Qu+V1bPxEjq3kmzI3FZrMIr1kK/4cxntoHSBDTZn/Ymab9LXVhRNrbVend5JrwDANePcYqbK5Fdn9NGhQ==} + '@polkadot-ui/assets@0.0.1-alpha.2': resolution: {integrity: sha512-E6RDLMP1SJs62YWRY4VJ8nPpYbjZGIgGQiZZO/rDfhRmm7qPPcmUfYXeI/4FQ21psjLVB6iBxbFS8FsqyFWp6w==} @@ -1478,8 +1496,8 @@ packages: '@types/node@22.5.1': resolution: {integrity: sha512-KkHsxej0j9IW1KKOOAA/XBA0z08UFSrRQHErzEfA3Vgq57eXIMYboIlHJuYIfd+lwCQjtKqUu3UnmKbtUc9yRw==} - '@types/node@22.5.3': - resolution: {integrity: sha512-njripolh85IA9SQGTAqbmnNZTdxv7X/4OYGPz8tgy5JDr8MP+uDBa921GpYEoDDnwm0Hmn5ZPeJgiiSTPoOzkQ==} + '@types/node@22.5.4': + resolution: {integrity: sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==} '@types/normalize-package-data@2.4.4': resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} @@ -1803,6 +1821,15 @@ packages: supports-color: optional: true + debug@4.3.7: + resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + decamelize@1.2.0: resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} engines: {node: '>=0.10.0'} @@ -2210,8 +2237,8 @@ packages: resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==} engines: {node: '>=12'} - is-unicode-supported@2.0.0: - resolution: {integrity: sha512-FRdAyx5lusK1iHG0TWpVtk9+1i+GjrzRffhDg4ovQ7mcidMQ6mj+MhKPmvh7Xwyv5gIS06ns49CA7Sqg7lC22Q==} + is-unicode-supported@2.1.0: + resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} engines: {node: '>=18'} isexe@2.0.0: @@ -2385,6 +2412,9 @@ packages: ms@2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} @@ -2524,6 +2554,9 @@ packages: picocolors@1.0.1: resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} + picocolors@1.1.0: + resolution: {integrity: sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==} + picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} @@ -2552,8 +2585,8 @@ packages: resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==} engines: {node: '>=10.13.0'} - polkadot-api@1.1.0: - resolution: {integrity: sha512-WIfOS1xlqL6jYAOKvO7DVGFg4Hd1rVJ0N4tu3G2eQMzQq2yMrtVPA6QOKVlan6xtj9xfhXNmSTbMtsgFqLYmyA==} + polkadot-api@1.2.0: + resolution: {integrity: sha512-ySI9syyD7Og6q/q9zVX3v6kqDrQtiwbAik+ZbStTa4IHnCXdQFn9gF81pEhRDPPU07Ei4mBTa6JNSv74+g37aw==} hasBin: true peerDependencies: rxjs: '>=7.8.0' @@ -2861,8 +2894,8 @@ packages: react: ^18.0.0 react-dom: ^18.0.0 - sort-keys@5.0.0: - resolution: {integrity: sha512-Pdz01AvCAottHTPQGzndktFNdbRA75BgOfeT1hH+AMnJFv8lynkPi42rfeEhpx1saTEI3YNMWxfqu0sFD1G8pw==} + sort-keys@5.1.0: + resolution: {integrity: sha512-aSbHV0DaBcr7u0PVHXzM6NbZNAtrr9sF6+Qfs9UUVG7Ll3jQ6hHi8F/xqIIcn2rvIVbr0v/2zyjSdwSV47AgLQ==} engines: {node: '>=12'} source-map-js@1.2.0: @@ -3035,8 +3068,8 @@ packages: resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} engines: {node: '>=10'} - type-fest@4.26.0: - resolution: {integrity: sha512-OduNjVJsFbifKb57UqZ2EMP1i4u64Xwow3NYXUtBbD4vIwJdQd4+xl8YDou1dlm4DVrtwT/7Ky8z8WyCULVfxw==} + type-fest@4.26.1: + resolution: {integrity: sha512-yOGpmOAL7CkKe/91I5O3gPICmJNLJ1G4zFYVAsRHg7M64biSnPtRj0WNQt++bRkjYOqjWXrhnUw1utzmVErAdg==} engines: {node: '>=16'} typescript@5.5.4: @@ -3615,18 +3648,18 @@ snapshots: '@pkgjs/parseargs@0.11.0': optional: true - '@polkadot-api/cli@0.8.0(jiti@1.21.6)(postcss@8.4.42)(smoldot@2.0.30)(yaml@2.5.1)': + '@polkadot-api/cli@0.8.1(jiti@1.21.6)(postcss@8.4.42)(smoldot@2.0.30)(yaml@2.5.1)': dependencies: '@commander-js/extra-typings': 12.1.0(commander@12.1.0) - '@polkadot-api/codegen': 0.9.0 + '@polkadot-api/codegen': 0.10.0 '@polkadot-api/json-rpc-provider': 0.0.3 - '@polkadot-api/known-chains': 0.5.0 - '@polkadot-api/metadata-compatibility': 0.1.3 - '@polkadot-api/observable-client': 0.5.2(@polkadot-api/substrate-client@0.2.1)(rxjs@7.8.1) - '@polkadot-api/polkadot-sdk-compat': 2.0.0 + '@polkadot-api/known-chains': 0.5.1 + '@polkadot-api/metadata-compatibility': 0.1.4 + '@polkadot-api/observable-client': 0.5.3(@polkadot-api/substrate-client@0.2.1)(rxjs@7.8.1) + '@polkadot-api/polkadot-sdk-compat': 2.1.0 '@polkadot-api/sm-provider': 0.1.1(smoldot@2.0.30) '@polkadot-api/smoldot': 0.3.2 - '@polkadot-api/substrate-bindings': 0.6.3 + '@polkadot-api/substrate-bindings': 0.7.0 '@polkadot-api/substrate-client': 0.2.1 '@polkadot-api/utils': 0.1.1 '@polkadot-api/wasm-executor': 0.1.1 @@ -3654,16 +3687,16 @@ snapshots: - utf-8-validate - yaml - '@polkadot-api/codegen@0.9.0': + '@polkadot-api/codegen@0.10.0': dependencies: - '@polkadot-api/metadata-builders': 0.6.0 - '@polkadot-api/metadata-compatibility': 0.1.3 - '@polkadot-api/substrate-bindings': 0.6.3 + '@polkadot-api/metadata-builders': 0.7.0 + '@polkadot-api/metadata-compatibility': 0.1.4 + '@polkadot-api/substrate-bindings': 0.7.0 '@polkadot-api/utils': 0.1.1 - '@polkadot-api/descriptors@file:.papi/descriptors(polkadot-api@1.1.0(jiti@1.21.6)(postcss@8.4.42)(rxjs@7.8.1)(smoldot@2.0.30)(yaml@2.5.1))': + '@polkadot-api/descriptors@file:.papi/descriptors(polkadot-api@1.2.0(jiti@1.21.6)(postcss@8.4.42)(rxjs@7.8.1)(smoldot@2.0.30)(yaml@2.5.1))': dependencies: - polkadot-api: 1.1.0(jiti@1.21.6)(postcss@8.4.42)(rxjs@7.8.1)(smoldot@2.0.30)(yaml@2.5.1) + polkadot-api: 1.2.0(jiti@1.21.6)(postcss@8.4.42)(rxjs@7.8.1)(smoldot@2.0.30)(yaml@2.5.1) '@polkadot-api/json-rpc-provider-proxy@0.2.0': {} @@ -3671,38 +3704,38 @@ snapshots: '@polkadot-api/json-rpc-provider@0.0.3': {} - '@polkadot-api/known-chains@0.5.0': {} + '@polkadot-api/known-chains@0.5.1': {} - '@polkadot-api/logs-provider@0.0.4': + '@polkadot-api/logs-provider@0.0.5': dependencies: '@polkadot-api/json-rpc-provider': 0.0.3 - '@polkadot-api/metadata-builders@0.6.0': + '@polkadot-api/metadata-builders@0.7.0': dependencies: - '@polkadot-api/substrate-bindings': 0.6.3 + '@polkadot-api/substrate-bindings': 0.7.0 '@polkadot-api/utils': 0.1.1 - '@polkadot-api/metadata-compatibility@0.1.3': + '@polkadot-api/metadata-compatibility@0.1.4': dependencies: - '@polkadot-api/metadata-builders': 0.6.0 - '@polkadot-api/substrate-bindings': 0.6.3 + '@polkadot-api/metadata-builders': 0.7.0 + '@polkadot-api/substrate-bindings': 0.7.0 - '@polkadot-api/observable-client@0.5.2(@polkadot-api/substrate-client@0.2.1)(rxjs@7.8.1)': + '@polkadot-api/observable-client@0.5.3(@polkadot-api/substrate-client@0.2.1)(rxjs@7.8.1)': dependencies: - '@polkadot-api/metadata-builders': 0.6.0 - '@polkadot-api/substrate-bindings': 0.6.3 + '@polkadot-api/metadata-builders': 0.7.0 + '@polkadot-api/substrate-bindings': 0.7.0 '@polkadot-api/substrate-client': 0.2.1 '@polkadot-api/utils': 0.1.1 rxjs: 7.8.1 - '@polkadot-api/pjs-signer@0.4.1': + '@polkadot-api/pjs-signer@0.4.2': dependencies: - '@polkadot-api/metadata-builders': 0.6.0 + '@polkadot-api/metadata-builders': 0.7.0 '@polkadot-api/polkadot-signer': 0.1.4 - '@polkadot-api/substrate-bindings': 0.6.3 + '@polkadot-api/substrate-bindings': 0.7.0 '@polkadot-api/utils': 0.1.1 - '@polkadot-api/polkadot-sdk-compat@2.0.0': + '@polkadot-api/polkadot-sdk-compat@2.1.0': dependencies: '@polkadot-api/json-rpc-provider': 0.0.3 @@ -3735,6 +3768,13 @@ snapshots: '@scure/base': 1.1.7 scale-ts: 1.6.0 + '@polkadot-api/substrate-bindings@0.7.0': + dependencies: + '@noble/hashes': 1.5.0 + '@polkadot-api/utils': 0.1.1 + '@scure/base': 1.1.8 + scale-ts: 1.6.0 + '@polkadot-api/substrate-client@0.2.1': dependencies: '@polkadot-api/json-rpc-provider': 0.0.3 @@ -3753,18 +3793,33 @@ snapshots: - bufferutil - utf-8-validate + '@polkadot-labs/hdkd-helpers@0.0.8': + dependencies: + '@noble/curves': 1.6.0 + '@noble/hashes': 1.5.0 + '@polkadot-labs/schnorrkel-wasm': 0.0.5 + '@scure/base': 1.1.8 + scale-ts: 1.6.0 + + '@polkadot-labs/hdkd@0.0.8': + dependencies: + '@polkadot-labs/hdkd-helpers': 0.0.8 + '@polkadot-labs/schnorrkel-wasm': 0.0.5 + + '@polkadot-labs/schnorrkel-wasm@0.0.5': {} + '@polkadot-ui/assets@0.0.1-alpha.2': {} '@polkadot-ui/core@0.0.1-alpha.2': {} - '@polkadot-ui/react@0.0.1-alpha.15(@noble/hashes@1.5.0)(@polkadot-api/substrate-bindings@0.6.3)(@polkadot-ui/assets@0.0.1-alpha.2)(@polkadot-ui/core@0.0.1-alpha.2)(@polkadot-ui/utils@0.0.1-alpha.5(@polkadot/keyring@13.0.2(@polkadot/util-crypto@13.0.2(@polkadot/util@13.0.2))(@polkadot/util@13.0.2))(@polkadot/util@13.0.2))(polkadot-api@1.1.0(jiti@1.21.6)(postcss@8.4.42)(rxjs@7.8.1)(smoldot@2.0.30)(yaml@2.5.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@polkadot-ui/react@0.0.1-alpha.15(@noble/hashes@1.5.0)(@polkadot-api/substrate-bindings@0.7.0)(@polkadot-ui/assets@0.0.1-alpha.2)(@polkadot-ui/core@0.0.1-alpha.2)(@polkadot-ui/utils@0.0.1-alpha.5(@polkadot/keyring@13.0.2(@polkadot/util-crypto@13.0.2(@polkadot/util@13.0.2))(@polkadot/util@13.0.2))(@polkadot/util@13.0.2))(polkadot-api@1.2.0(jiti@1.21.6)(postcss@8.4.42)(rxjs@7.8.1)(smoldot@2.0.30)(yaml@2.5.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@noble/hashes': 1.5.0 - '@polkadot-api/substrate-bindings': 0.6.3 + '@polkadot-api/substrate-bindings': 0.7.0 '@polkadot-ui/assets': 0.0.1-alpha.2 '@polkadot-ui/core': 0.0.1-alpha.2 '@polkadot-ui/utils': 0.0.1-alpha.5(@polkadot/keyring@13.0.2(@polkadot/util-crypto@13.0.2(@polkadot/util@13.0.2))(@polkadot/util@13.0.2))(@polkadot/util@13.0.2) - polkadot-api: 1.1.0(jiti@1.21.6)(postcss@8.4.42)(rxjs@7.8.1)(smoldot@2.0.30)(yaml@2.5.1) + polkadot-api: 1.2.0(jiti@1.21.6)(postcss@8.4.42)(rxjs@7.8.1)(smoldot@2.0.30)(yaml@2.5.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -4295,14 +4350,14 @@ snapshots: '@radix-ui/rect@1.1.0': {} - '@reactive-dot/core@0.10.0(polkadot-api@1.1.0(jiti@1.21.6)(postcss@8.4.42)(rxjs@7.8.1)(smoldot@2.0.30)(yaml@2.5.1))': + '@reactive-dot/core@0.10.0(polkadot-api@1.2.0(jiti@1.21.6)(postcss@8.4.42)(rxjs@7.8.1)(smoldot@2.0.30)(yaml@2.5.1))': dependencies: '@reactive-dot/utils': 0.8.0 - polkadot-api: 1.1.0(jiti@1.21.6)(postcss@8.4.42)(rxjs@7.8.1)(smoldot@2.0.30)(yaml@2.5.1) + polkadot-api: 1.2.0(jiti@1.21.6)(postcss@8.4.42)(rxjs@7.8.1)(smoldot@2.0.30)(yaml@2.5.1) - '@reactive-dot/react@0.10.0(@types/react@18.3.5)(polkadot-api@1.1.0(jiti@1.21.6)(postcss@8.4.42)(rxjs@7.8.1)(smoldot@2.0.30)(yaml@2.5.1))(react@18.3.1)': + '@reactive-dot/react@0.10.0(@types/react@18.3.5)(polkadot-api@1.2.0(jiti@1.21.6)(postcss@8.4.42)(rxjs@7.8.1)(smoldot@2.0.30)(yaml@2.5.1))(react@18.3.1)': dependencies: - '@reactive-dot/core': 0.10.0(polkadot-api@1.1.0(jiti@1.21.6)(postcss@8.4.42)(rxjs@7.8.1)(smoldot@2.0.30)(yaml@2.5.1)) + '@reactive-dot/core': 0.10.0(polkadot-api@1.2.0(jiti@1.21.6)(postcss@8.4.42)(rxjs@7.8.1)(smoldot@2.0.30)(yaml@2.5.1)) jotai: 2.9.3(@types/react@18.3.5)(react@18.3.1) jotai-scope: 0.7.2(jotai@2.9.3(@types/react@18.3.5)(react@18.3.1))(react@18.3.1) react: 18.3.1 @@ -4483,7 +4538,7 @@ snapshots: '@types/bn.js@5.1.5': dependencies: - '@types/node': 22.5.3 + '@types/node': 22.5.4 '@types/estree@1.0.5': {} @@ -4493,7 +4548,7 @@ snapshots: dependencies: undici-types: 6.19.8 - '@types/node@22.5.3': + '@types/node@22.5.4': dependencies: undici-types: 6.19.8 @@ -4820,6 +4875,10 @@ snapshots: dependencies: ms: 2.1.2 + debug@4.3.7: + dependencies: + ms: 2.1.3 + decamelize@1.2.0: {} deep-is@0.1.4: {} @@ -4849,11 +4908,11 @@ snapshots: no-case: 3.0.4 tslib: 2.7.0 - dot-connect@0.6.0(@reactive-dot/core@0.10.0(polkadot-api@1.1.0(jiti@1.21.6)(postcss@8.4.42)(rxjs@7.8.1)(smoldot@2.0.30)(yaml@2.5.1)))(@types/react@18.3.5)(react@18.3.1): + dot-connect@0.6.0(@reactive-dot/core@0.10.0(polkadot-api@1.2.0(jiti@1.21.6)(postcss@8.4.42)(rxjs@7.8.1)(smoldot@2.0.30)(yaml@2.5.1)))(@types/react@18.3.5)(react@18.3.1): dependencies: '@lit-labs/preact-signals': 1.0.2 '@lit/react': 1.0.5(@types/react@18.3.5) - '@reactive-dot/core': 0.10.0(polkadot-api@1.1.0(jiti@1.21.6)(postcss@8.4.42)(rxjs@7.8.1)(smoldot@2.0.30)(yaml@2.5.1)) + '@reactive-dot/core': 0.10.0(polkadot-api@1.2.0(jiti@1.21.6)(postcss@8.4.42)(rxjs@7.8.1)(smoldot@2.0.30)(yaml@2.5.1)) lit: 3.2.0 qrcode: 1.5.4 optionalDependencies: @@ -5067,7 +5126,7 @@ snapshots: figures@6.1.0: dependencies: - is-unicode-supported: 2.0.0 + is-unicode-supported: 2.1.0 file-entry-cache@6.0.1: dependencies: @@ -5281,7 +5340,7 @@ snapshots: is-unicode-supported@1.3.0: {} - is-unicode-supported@2.0.0: {} + is-unicode-supported@2.1.0: {} isexe@2.0.0: {} @@ -5428,6 +5487,8 @@ snapshots: ms@2.1.2: {} + ms@2.1.3: {} + mz@2.7.0: dependencies: any-promise: 1.3.0 @@ -5499,7 +5560,7 @@ snapshots: cli-cursor: 5.0.0 cli-spinners: 2.9.2 is-interactive: 2.0.0 - is-unicode-supported: 2.0.0 + is-unicode-supported: 2.1.0 log-symbols: 6.0.0 stdin-discarder: 0.2.2 string-width: 7.2.0 @@ -5540,7 +5601,7 @@ snapshots: dependencies: '@babel/code-frame': 7.24.7 index-to-position: 0.1.2 - type-fest: 4.26.0 + type-fest: 4.26.1 parse-ms@4.0.0: {} @@ -5563,6 +5624,8 @@ snapshots: picocolors@1.0.1: {} + picocolors@1.1.0: {} + picomatch@2.3.1: {} pify@2.3.0: {} @@ -5581,22 +5644,22 @@ snapshots: pngjs@5.0.0: {} - polkadot-api@1.1.0(jiti@1.21.6)(postcss@8.4.42)(rxjs@7.8.1)(smoldot@2.0.30)(yaml@2.5.1): + polkadot-api@1.2.0(jiti@1.21.6)(postcss@8.4.42)(rxjs@7.8.1)(smoldot@2.0.30)(yaml@2.5.1): dependencies: - '@polkadot-api/cli': 0.8.0(jiti@1.21.6)(postcss@8.4.42)(smoldot@2.0.30)(yaml@2.5.1) + '@polkadot-api/cli': 0.8.1(jiti@1.21.6)(postcss@8.4.42)(smoldot@2.0.30)(yaml@2.5.1) '@polkadot-api/json-rpc-provider': 0.0.3 - '@polkadot-api/known-chains': 0.5.0 - '@polkadot-api/logs-provider': 0.0.4 - '@polkadot-api/metadata-builders': 0.6.0 - '@polkadot-api/metadata-compatibility': 0.1.3 - '@polkadot-api/observable-client': 0.5.2(@polkadot-api/substrate-client@0.2.1)(rxjs@7.8.1) - '@polkadot-api/pjs-signer': 0.4.1 - '@polkadot-api/polkadot-sdk-compat': 2.0.0 + '@polkadot-api/known-chains': 0.5.1 + '@polkadot-api/logs-provider': 0.0.5 + '@polkadot-api/metadata-builders': 0.7.0 + '@polkadot-api/metadata-compatibility': 0.1.4 + '@polkadot-api/observable-client': 0.5.3(@polkadot-api/substrate-client@0.2.1)(rxjs@7.8.1) + '@polkadot-api/pjs-signer': 0.4.2 + '@polkadot-api/polkadot-sdk-compat': 2.1.0 '@polkadot-api/polkadot-signer': 0.1.4 '@polkadot-api/signer': 0.1.4 '@polkadot-api/sm-provider': 0.1.1(smoldot@2.0.30) '@polkadot-api/smoldot': 0.3.2 - '@polkadot-api/substrate-bindings': 0.6.3 + '@polkadot-api/substrate-bindings': 0.7.0 '@polkadot-api/substrate-client': 0.2.1 '@polkadot-api/utils': 0.1.1 '@polkadot-api/ws-provider': 0.2.0 @@ -5745,7 +5808,7 @@ snapshots: '@types/normalize-package-data': 2.4.4 normalize-package-data: 6.0.2 parse-json: 8.1.0 - type-fest: 4.26.0 + type-fest: 4.26.1 unicorn-magic: 0.1.0 readdirp@3.6.0: @@ -5848,7 +5911,7 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - sort-keys@5.0.0: + sort-keys@5.1.0: dependencies: is-plain-obj: 4.1.0 @@ -6011,12 +6074,12 @@ snapshots: cac: 6.7.14 chokidar: 3.6.0 consola: 3.2.3 - debug: 4.3.6 + debug: 4.3.7 esbuild: 0.23.1 execa: 5.1.1 globby: 11.1.0 joycon: 3.1.1 - picocolors: 1.0.1 + picocolors: 1.1.0 postcss-load-config: 6.0.1(jiti@1.21.6)(postcss@8.4.42)(yaml@2.5.1) resolve-from: 5.0.0 rollup: 4.21.2 @@ -6038,7 +6101,7 @@ snapshots: type-fest@0.20.2: {} - type-fest@4.26.0: {} + type-fest@4.26.1: {} typescript@5.5.4: {} @@ -6150,15 +6213,15 @@ snapshots: dependencies: detect-indent: 7.0.1 is-plain-obj: 4.1.0 - sort-keys: 5.0.0 + sort-keys: 5.1.0 write-file-atomic: 5.0.1 write-package@7.1.0: dependencies: deepmerge-ts: 7.1.0 read-pkg: 9.0.1 - sort-keys: 5.0.0 - type-fest: 4.26.0 + sort-keys: 5.1.0 + type-fest: 4.26.1 write-json-file: 6.0.0 ws@8.18.0: {} diff --git a/src/components/LocksCard.tsx b/src/components/LocksCard.tsx index 61338af..cc2e788 100644 --- a/src/components/LocksCard.tsx +++ b/src/components/LocksCard.tsx @@ -12,9 +12,7 @@ import { Title } from './ui/title' import { ContentReveal } from './ui/content-reveal' import { BadgeCent, Clock2, Info, LockKeyholeOpen, Vote } from 'lucide-react' import { Badge } from './ui/badge' -import { dot } from '@polkadot-api/descriptors' import { useAccounts } from '@/contexts/AccountsContext' -import { TypedApi } from 'polkadot-api' import { DelegationLock, LockType, @@ -108,10 +106,7 @@ export const LocksCard = () => { if (!unVoteTxs || !unlockTxs) return - // We need this to make TS happy for now - const dotApi = api as TypedApi - - dotApi.tx.Utility.batch({ calls: [...unVoteTxs, ...unlockTxs] }) + api.tx.Utility.batch({ calls: [...unVoteTxs, ...unlockTxs] }) .signSubmitAndWatch(selectedAccount.polkadotSigner) .subscribe({ next: (event) => { diff --git a/src/components/MyDelegations.tsx b/src/components/MyDelegations.tsx index 2e83f99..1d1d990 100644 --- a/src/components/MyDelegations.tsx +++ b/src/components/MyDelegations.tsx @@ -9,8 +9,7 @@ import { useNetwork } from '@/contexts/NetworkContext' import { AddressDisplay } from './ui/address-display' import { Button } from './ui/button' import { useAccounts } from '@/contexts/AccountsContext' -import { Transaction, TypedApi } from 'polkadot-api' -import { dot } from '@polkadot-api/descriptors' +import { Transaction } from 'polkadot-api' import { DelegationByAmountConviction } from './DelegationByAmountConviction' export const MyDelegations = () => { @@ -59,7 +58,7 @@ export const MyDelegations = () => { const batchTx = tracks.map( (t) => api.tx.ConvictionVoting.undelegate({ class: t }).decodedCall, ) - tx = (api as TypedApi).tx.Utility.batch({ calls: batchTx }) + tx = api.tx.Utility.batch({ calls: batchTx }) } tx.signSubmitAndWatch(selectedAccount.polkadotSigner).subscribe({ diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx index 792f54c..6ae507d 100644 --- a/src/components/ui/button.tsx +++ b/src/components/ui/button.tsx @@ -4,6 +4,7 @@ import { Slot } from '@radix-ui/react-slot' import { cva, type VariantProps } from 'class-variance-authority' import { cn } from '@/lib/utils' +import { LoaderCircle } from 'lucide-react' const buttonVariants = cva( 'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50', @@ -38,17 +39,32 @@ export interface ButtonProps extends React.ButtonHTMLAttributes, VariantProps { asChild?: boolean + loading?: boolean } const Button = React.forwardRef( - ({ className, variant, size, asChild = false, ...props }, ref) => { + ( + { + className, + variant, + size, + asChild = false, + children, + loading = false, + ...props + }, + ref, + ) => { const Comp = asChild ? Slot : 'button' return ( + > + {loading && } + {children} + ) }, ) diff --git a/src/hooks/useGetDelegateTx.tsx b/src/hooks/useGetDelegateTx.tsx index cc96979..803abe3 100644 --- a/src/hooks/useGetDelegateTx.tsx +++ b/src/hooks/useGetDelegateTx.tsx @@ -1,69 +1,74 @@ import { useAccounts } from '@/contexts/AccountsContext' import { useLocks } from '@/contexts/LocksContext' import { useNetwork } from '@/contexts/NetworkContext' -import { dot, MultiAddress, VotingConviction } from '@polkadot-api/descriptors' -import { TypedApi } from 'polkadot-api' +import { MultiAddress, VotingConviction } from '@polkadot-api/descriptors' +import { Transaction } from 'polkadot-api' import { useCallback } from 'react' interface Params { - target: string + delegateAddress: string amount: bigint tracks: number[] conviction: VotingConviction } +export interface DelegateTxs { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + removeVotesTxs?: Transaction[] + // eslint-disable-next-line @typescript-eslint/no-explicit-any + removeDelegationsTxs?: Transaction[] + // eslint-disable-next-line @typescript-eslint/no-explicit-any + delegationTxs?: Transaction[] +} + export const useGetDelegateTx = () => { const { api } = useNetwork() const { selectedAccount } = useAccounts() const { delegations, voteLocks: locks } = useLocks() - const getDelegationTx = useCallback( - ({ target, amount, tracks, conviction }: Params) => { - if (!api || !selectedAccount) return + const getDelegationTxs = useCallback( + ({ delegateAddress, amount, tracks, conviction }: Params): DelegateTxs => { + if (!api || !selectedAccount) + return { + removeVotesTxs: [], + removeDelegationsTxs: [], + delegationTxs: [], + } - const txs: Array< - | ReturnType - | ReturnType - | ReturnType - > = [] - - // first we remove all ongoing votes - locks + const removeVotesTxs = locks .filter(({ isOngoing }) => !!isOngoing) - .forEach(({ refId, trackId }) => { - txs.push( - api.tx.ConvictionVoting.remove_vote({ - index: refId, - class: trackId, - }), - ) - }) - - // then we remove all ongoing delegations - Object.values(delegations || {}).forEach((d) => { - d.map(({ trackId }) => { - txs.push(api.tx.ConvictionVoting.undelegate({ class: trackId })) - }) - }) - - // then we delegate for the selected tracks - tracks.forEach((trackId) => { - txs.push( - api.tx.ConvictionVoting.delegate({ + .map(({ refId, trackId }) => + api.tx.ConvictionVoting.remove_vote({ + index: refId, class: trackId, - conviction, - to: MultiAddress.Id(target), - balance: amount, }), ) - }) - return (api as TypedApi).tx.Utility.batch_all({ - calls: txs.map((tx) => tx.decodedCall), + const removeDelegationsTxs: ReturnType< + typeof api.tx.ConvictionVoting.undelegate + >[] = [] + + Object.values(delegations || {}).forEach((delegation) => { + delegation.map(({ trackId }) => { + removeDelegationsTxs.push( + api.tx.ConvictionVoting.undelegate({ class: trackId }), + ) + }) }) + + const delegationTxs = tracks.map((trackId) => + api.tx.ConvictionVoting.delegate({ + class: trackId, + conviction, + to: MultiAddress.Id(delegateAddress), + balance: amount, + }), + ) + + return { removeVotesTxs, removeDelegationsTxs, delegationTxs } }, [api, delegations, locks, selectedAccount], ) - return getDelegationTx + return getDelegationTxs } diff --git a/src/hooks/useTestTx.tsx b/src/hooks/useTestTx.tsx new file mode 100644 index 0000000..03d04e0 --- /dev/null +++ b/src/hooks/useTestTx.tsx @@ -0,0 +1,67 @@ +import { Binary, Transaction } from 'polkadot-api' +import { sr25519CreateDerive } from '@polkadot-labs/hdkd' +import { + DEV_PHRASE, + entropyToMiniSecret, + mnemonicToEntropy, +} from '@polkadot-labs/hdkd-helpers' +import { getPolkadotSigner } from 'polkadot-api/signer' +import { useNetwork } from '@/contexts/NetworkContext' +import { useCallback } from 'react' + +export const useTestTx = () => { + const { api } = useNetwork() + const derive = sr25519CreateDerive( + entropyToMiniSecret(mnemonicToEntropy(DEV_PHRASE)), + ) + const aliceKeyPair = derive('//Alice') + const aliceSigner = getPolkadotSigner( + aliceKeyPair.publicKey, + 'Sr25519', + aliceKeyPair.sign, + ) + + const isExhaustsResources = useCallback( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + async (tx: Transaction) => { + if (!api) return null + + // create the signed extrinsic + const signedTx = await tx.sign(aliceSigner) + // see what the result of this extrinsic would be against the current best-block + const dryRunResult = await api.apis.BlockBuilder.apply_extrinsic( + Binary.fromOpaqueHex(signedTx), + { at: 'best' }, + ) + // `dryRunResult` is a strongly typed object, so if `success` is false, then + // the value will have a strongly typed enum with the reason why it didn't succeed + console.log('dryRunResult', dryRunResult) + // In your case it would print: + // { + // success: false, + // value: { + // type: "Invalid", + // value: { + // type: "ExhaustsResources", + // value: undefined, + // }, + // }, + // } + + // Therefore, you could first check whether the dryRun worked before + // broadcasting the transaction + if ( + !dryRunResult.success && + dryRunResult.value.type === 'Invalid' && + dryRunResult.value.value.type === 'ExhaustsResources' + ) { + return true + } + + return false + }, + [api, aliceSigner], + ) + + return { isExhaustsResources } +} diff --git a/src/pages/Delegate/MultiTransactionDialog.tsx b/src/pages/Delegate/MultiTransactionDialog.tsx new file mode 100644 index 0000000..b23b85d --- /dev/null +++ b/src/pages/Delegate/MultiTransactionDialog.tsx @@ -0,0 +1,165 @@ +import { Button } from '@/components/ui/button' +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, +} from '@/components/ui/dialog' +import { useAccounts } from '@/contexts/AccountsContext' +import { useNetwork } from '@/contexts/NetworkContext' +import { DelegateTxs } from '@/hooks/useGetDelegateTx' +import { useTestTx } from '@/hooks/useTestTx' +import { useState } from 'react' +import { TooLargeDialog } from './TooLargeDialog' + +interface Props { + isOpen: boolean + onOpenChange: (isOpen: boolean) => void + delegateTxs: DelegateTxs + onProcessFinished: () => void +} + +type Step = 1 | 2 + +export const MultiTransactionDialog = ({ + isOpen, + onOpenChange, + delegateTxs, + onProcessFinished, +}: Props) => { + const [step, setStep] = useState(1) + const { api } = useNetwork() + const [isTxInitiated, setIsTxInitiated] = useState(false) + const { isExhaustsResources } = useTestTx() + const { selectedAccount } = useAccounts() + const [waitingForFinalization, setWaitingForFinalization] = useState(false) + const [promptForHelpCallData, setPromptForHelpCallData] = useState('') + + const onSign = () => { + step === 1 && onSignStep1() + step === 2 && onSignStep2() + } + + const onSignStep1 = async () => { + if (!api || !selectedAccount) return + + const step1Txs = api.tx.Utility.batch_all({ + calls: [ + ...(delegateTxs.removeDelegationsTxs || []), + ...(delegateTxs.removeVotesTxs || []), + ].map((tx) => tx.decodedCall), + }) + + if (!step1Txs) return + + // check if we have an exhausted limit on the 1st tx + const isExhaustsRessouces = await isExhaustsResources(step1Txs) + + // this is too big of a batch we need to split it up more + if (isExhaustsRessouces) { + const callData = await step1Txs.getEncodedData() + setPromptForHelpCallData(callData.asHex()) + return + } + + setIsTxInitiated(true) + ;(await step1Txs) + .signSubmitAndWatch(selectedAccount?.polkadotSigner) + .subscribe((event) => { + console.info(event) + + if (event.type === 'txBestBlocksState' && event.found) { + if (event.dispatchError) { + console.error('Tx error', event) + setIsTxInitiated(false) + + return + } + + setStep(2) + setIsTxInitiated(false) + } + }) + } + + const onSignStep2 = async () => { + if (!api || !selectedAccount) return + setIsTxInitiated(true) + + const step2Txs = api.tx.Utility.batch_all({ + calls: (delegateTxs.delegationTxs || []).map((tx) => tx.decodedCall), + }) + + if (!step2Txs) { + setIsTxInitiated(false) + return + } + + // check if we have an exhausted limit on the 2nd tx + const isExhaustsRessouces = await isExhaustsResources(step2Txs) + + // this is too big of a batch we need to split it up more + if (isExhaustsRessouces) { + const callData = await step2Txs.getEncodedData() + setPromptForHelpCallData(callData.asHex()) + return + } + + await step2Txs + .signSubmitAndWatch(selectedAccount?.polkadotSigner, { at: 'best' }) + .subscribe((event) => { + console.info(event) + + if (event.type === 'txBestBlocksState' && event.found) { + if (event.dispatchError) { + console.error('Tx error', event) + setIsTxInitiated(false) + } + setWaitingForFinalization(true) + } + + if (event.type === 'finalized') { + onProcessFinished() + setIsTxInitiated(false) + setWaitingForFinalization(false) + } + }) + } + + if (promptForHelpCallData) + return ( + + ) + + return ( + + + + Step {step} + +
+ {step === 1 && + 'The delegation process is in 2 parts, first please sign a transaction to remove current votes and delegations'} + {step === 2 && 'Second please sign a transaction to delegate'} +
+
+ +
+
+
+
+
+ ) +} diff --git a/src/pages/Delegate/TooLargeDialog.tsx b/src/pages/Delegate/TooLargeDialog.tsx new file mode 100644 index 0000000..790fa8e --- /dev/null +++ b/src/pages/Delegate/TooLargeDialog.tsx @@ -0,0 +1,44 @@ +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, +} from '@/components/ui/dialog' + +interface Props { + isOpen: boolean + onOpenChange: (isOpen: boolean) => void + callData: string +} + +export const TooLargeDialog = ({ isOpen, onOpenChange, callData }: Props) => { + return ( + + + + Tx too large + +
+ We encountered an error, the tx you want to submit is too large + for the blockchain to accept it. Please get in touch with the + Delegit team to get help{' '} + + on GitHub. + {' '} + Sharing the transaction information bellow would help us: +
+                {callData}
+              
+
+
+
+
+
+ ) +} diff --git a/src/pages/Delegate/index.tsx b/src/pages/Delegate/index.tsx index 8bae781..f078fa5 100644 --- a/src/pages/Delegate/index.tsx +++ b/src/pages/Delegate/index.tsx @@ -13,8 +13,10 @@ import { ArrowLeft } from 'lucide-react' import { msgs } from '@/lib/constants' import { evalUnits, planckToUnit } from '@polkadot-ui/utils' import { useLocks } from '@/contexts/LocksContext' -import { useGetDelegateTx } from '@/hooks/useGetDelegateTx' +import { DelegateTxs, useGetDelegateTx } from '@/hooks/useGetDelegateTx' import { AlertNote } from '@/components/Alert' +import { useTestTx } from '@/hooks/useTestTx' +import { MultiTransactionDialog } from './MultiTransactionDialog' export const Delegate = () => { const { api, assetInfo } = useNetwork() @@ -38,6 +40,10 @@ export const Delegate = () => { const getDelegateTx = useGetDelegateTx() const navigate = useNavigate() const { search } = useLocation() + const { isExhaustsResources } = useTestTx() + const [isMultiTxDialogOpen, setIsMultiTxDialogOpen] = useState(false) + const [delegateTxs, setDelegateTxs] = useState({} as DelegateTxs) + const { refreshLocks } = useLocks() const { display: convictionTimeDisplay, multiplier: convictionMultiplier } = getConvictionLockTimeDisplay(convictionNo) @@ -96,44 +102,84 @@ export const Delegate = () => { setAmountVisible(e.target.value) } + const onChangeSplitTransactionDialog = (isOpen: boolean) => { + setIsMultiTxDialogOpen(isOpen) + setIsTxInitiated(false) + } + + const onProcessFinished = () => { + refreshLocks() + navigate(`/${search}`) + setIsTxInitiated(false) + onChangeSplitTransactionDialog(false) + } + const onSign = async () => { - if (selectedAccount && amount) { - const allTracks = await api.constants.Referenda.Tracks() - .then((tracks) => { - return tracks.map(([track]) => track) - }) - .catch(console.error) - - const tx = getDelegateTx({ - target: delegate.address, - conviction: conviction, - amount, - tracks: allTracks || [], + if (!selectedAccount || !amount) return + setIsTxInitiated(true) + + const allTracks = await api.constants.Referenda.Tracks() + .then((tracks) => { + return tracks.map(([track]) => track) + }) + .catch((e) => { + console.error(e) + setIsTxInitiated(false) }) - if (!tx) return + const { + delegationTxs = [], + removeDelegationsTxs = [], + removeVotesTxs = [], + } = getDelegateTx({ + delegateAddress: delegate.address, + conviction: conviction, + amount, + tracks: allTracks || [], + }) - setIsTxInitiated(true) - ;(await tx) - .signSubmitAndWatch(selectedAccount?.polkadotSigner) - .subscribe((event) => { - console.info(event) + setDelegateTxs({ + removeVotesTxs, + removeDelegationsTxs, + delegationTxs, + }) - if (event.type === 'txBestBlocksState' && event.found) { - if (event.dispatchError) { - console.error('Tx error', event) - setIsTxInitiated(false) - } - } + const allTxs = api.tx.Utility.batch_all({ + calls: [...delegationTxs, ...removeDelegationsTxs, ...removeVotesTxs].map( + (tx) => tx.decodedCall, + ), + }) - if (event.type === 'finalized') { - navigate(`/${search}`) - setIsTxInitiated(false) - } - }) - } else { + if (!allTxs) { + setIsTxInitiated(false) + return + } + + // check if we have an exhausted limit on the whole tx + const isExhaustsRessouces = await isExhaustsResources(allTxs) + + // this is too big of a batch we need to split it up + if (isExhaustsRessouces) { + setIsMultiTxDialogOpen(true) return } + + await allTxs + .signSubmitAndWatch(selectedAccount?.polkadotSigner) + .subscribe((event) => { + console.info(event) + + if (event.type === 'txBestBlocksState' && event.found) { + if (event.dispatchError) { + console.error('Tx error', event) + setIsTxInitiated(false) + } + } + + if (event.type === 'finalized') { + onProcessFinished() + } + }) } return ( @@ -199,9 +245,16 @@ export const Delegate = () => { + ) }