From c38955b1ec3f886fe4f005ddbb465507d4ac413d Mon Sep 17 00:00:00 2001
From: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com>
Date: Sun, 15 Dec 2024 19:35:09 +0100
Subject: [PATCH 1/4] Support calls with no params (#608)
---
packages/ui/src/components/CallInfo.tsx | 2 +-
packages/ui/src/utils/jsonPrint.ts | 28 +++++++++++++++----------
2 files changed, 18 insertions(+), 12 deletions(-)
diff --git a/packages/ui/src/components/CallInfo.tsx b/packages/ui/src/components/CallInfo.tsx
index 88f28568..48397a2c 100644
--- a/packages/ui/src/components/CallInfo.tsx
+++ b/packages/ui/src/components/CallInfo.tsx
@@ -252,7 +252,7 @@ const CallInfo = ({
() => aggregatedData.callData && getDecodeUrl(aggregatedData.callData),
[aggregatedData, getDecodeUrl]
)
- const hasArgs = useMemo(() => decodedCall && Object.keys(decodedCall).length > 0, [decodedCall])
+ const hasArgs = useMemo(() => decodedCall && decodedCall?.value?.value, [decodedCall])
return (
- json5
- .stringify(e, {
- replacer: (_, v) =>
- typeof v === 'bigint' ? v.toString() : v instanceof Binary ? v.asText() : v,
- space: 4
- })
- // remove { and }
- .slice(1, -1)
- // remove trailing comma if any
- .replace(/,\s*$/, '')
+export const JSONprint = (e: unknown) => {
+ if (e === null || e === undefined) {
+ return ''
+ }
+ return (
+ json5
+ .stringify(e, {
+ replacer: (_, v) =>
+ typeof v === 'bigint' ? v.toString() : v instanceof Binary ? v.asText() : v,
+ space: 4
+ })
+ // remove { and }
+ .slice(1, -1)
+ // remove trailing comma if any
+ .replace(/,\s*$/, '')
+ )
+}
From 3c4e0627c984793ad92b90306bd90a0253f2dc41 Mon Sep 17 00:00:00 2001
From: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com>
Date: Tue, 17 Dec 2024 12:09:47 +0100
Subject: [PATCH 2/4] Various enhancements (#610)
---
.../ui/cypress/tests/multisig-creation.cy.ts | 3 +-
packages/ui/package.json | 2 +-
packages/ui/src/components/CallInfo.tsx | 56 +++++++++++++------
.../src/components/EasySetup/FromCallData.tsx | 16 +++++-
.../components/library/TextFieldStyled.tsx | 3 +-
packages/ui/src/hooks/useGetMultisigTx.tsx | 8 +--
.../src/pages/Creation/WithProxySelection.tsx | 21 +++++--
packages/ui/src/pages/Creation/index.tsx | 6 --
packages/ui/src/utils/getPapiHowLink.ts | 1 +
yarn.lock | 12 ++--
10 files changed, 84 insertions(+), 44 deletions(-)
create mode 100644 packages/ui/src/utils/getPapiHowLink.ts
diff --git a/packages/ui/cypress/tests/multisig-creation.cy.ts b/packages/ui/cypress/tests/multisig-creation.cy.ts
index 345e7fa6..720a5822 100644
--- a/packages/ui/cypress/tests/multisig-creation.cy.ts
+++ b/packages/ui/cypress/tests/multisig-creation.cy.ts
@@ -99,6 +99,8 @@ describe('Multisig creation', () => {
// Step 2
newMultisigPage.step2.thresholdInput().type('2')
newMultisigPage.step2.nameInput().type(multisigName)
+ newMultisigPage.step2.checkboxUsePureProxy().click()
+ newMultisigPage.step2.checkboxUsePureProxy().should('be.checked')
newMultisigPage.nextButton().should('contain', 'Next').click()
// Step 3
@@ -136,7 +138,6 @@ describe('Multisig creation', () => {
// Step 2
newMultisigPage.step2.thresholdInput().type('3')
newMultisigPage.step2.nameInput().type(multisigName)
- newMultisigPage.step2.checkboxUsePureProxy().click()
newMultisigPage.step2.checkboxUsePureProxy().should('not.be.checked')
newMultisigPage.nextButton().should('contain', 'Next').click()
diff --git a/packages/ui/package.json b/packages/ui/package.json
index 63b7f4fb..3aed2285 100644
--- a/packages/ui/package.json
+++ b/packages/ui/package.json
@@ -16,7 +16,7 @@
"@polkadot/react-identicon": "^3.11.3",
"@polkadot/util-crypto": "^13.2.3",
"@reactive-dot/core": "^0.27.1",
- "@reactive-dot/react": "^0.27.1",
+ "@reactive-dot/react": "^0.28.0",
"@tanstack/react-query": "^5.62.2",
"@walletconnect/web3wallet": "^1.16.1",
"dayjs": "^1.11.13",
diff --git a/packages/ui/src/components/CallInfo.tsx b/packages/ui/src/components/CallInfo.tsx
index 48397a2c..ff45a0e2 100644
--- a/packages/ui/src/components/CallInfo.tsx
+++ b/packages/ui/src/components/CallInfo.tsx
@@ -33,7 +33,7 @@ interface CreateTreeParams {
chainInfo?: ChainInfoHuman
}
-const isWhiteListedCall = (type: string, value: string) => {
+const isWhiteListedCall = (extrinsicName: string) => {
return [
'Balances.transfer',
'Balances.transfer_keep_alive',
@@ -57,12 +57,18 @@ const isWhiteListedCall = (type: string, value: string) => {
'ConvictionVoting.vote',
'ConvictionVoting.remove_vote',
'ConvictionVoting.undelegate',
- 'ConvictionVoting.unlock'
- ].includes(`${type}.${value}`)
+ 'ConvictionVoting.unlock',
+ // Hydration
+ 'Tokens.transfer'
+ ].includes(extrinsicName)
}
-const isBatchedCall = (type: string, value: string) => {
- return ['Utility.batch', 'Utility.batch_all', 'Utility.force_batch'].includes(`${type}.${value}`)
+const isPreventBalanceFormat = (extrinsicName: string) => {
+ return ['Tokens.transfer'].includes(extrinsicName)
+}
+
+const isBatchedCall = (extrinsicName: string) => {
+ return ['Utility.batch', 'Utility.batch_all', 'Utility.force_batch'].includes(extrinsicName)
}
const formatBalance = (amount: bigint, label: string, chainInfo: ChainInfoHuman, id: string) => (
@@ -74,11 +80,23 @@ const formatBalance = (amount: bigint, label: string, chainInfo: ChainInfoHuman,
)
-const eachFieldRendered = (value: Record
, chainInfo: ChainInfoHuman, id: string) => {
+interface EachFieldRenderedParams {
+ value: Record
+ chainInfo: ChainInfoHuman
+ id: string
+ preventBalanceFormating?: boolean
+}
+const eachFieldRendered = ({
+ value,
+ chainInfo,
+ id,
+ preventBalanceFormating = false
+}: EachFieldRenderedParams) => {
// for transfer, nomination, staking, bounties
- const bigIntKey = ['value', 'fee', 'max_additional', 'balance'].find(
- (key) => typeof value[key] === 'bigint'
- )
+ // We should make sure this is not done for hydration
+ const bigIntKey =
+ !preventBalanceFormating &&
+ ['value', 'fee', 'max_additional', 'balance'].find((key) => typeof value[key] === 'bigint')
if (bigIntKey) {
return formatBalance(value[bigIntKey], bigIntKey, chainInfo, id)
@@ -162,7 +180,10 @@ const preparedCall = ({
}: PreparedCallParams) => {
if (!decodedCall) return
- if (isBatchedCall(decodedCall.type, decodedCall.value.type)) {
+ const extrinsicName = getExtrinsicName(decodedCall.type, decodedCall.value.type)
+ const preventBalanceFormating = isPreventBalanceFormat(extrinsicName)
+
+ if (isBatchedCall(extrinsicName)) {
const lowerLevelCalls = decodedCall.value.value.calls as Array>
return lowerLevelCalls.map((call, index) => {
@@ -178,19 +199,20 @@ const preparedCall = ({
})
}
- if (isWhiteListedCall(decodedCall.type, decodedCall.value.type)) {
+ if (isWhiteListedCall(extrinsicName)) {
const lowerLevelCall = decodedCall.value.value
if (typeof lowerLevelCall === 'object') {
return (
<>
- {isBatch && (
-
- {getExtrinsicName(decodedCall.type, decodedCall.value.type)}
-
- )}
+ {isBatch && {extrinsicName}}
{Object.entries(lowerLevelCall).map(([key, value], index) =>
- eachFieldRendered({ [key]: value }, chainInfo, `${decodedCall.type}-${index}`)
+ eachFieldRendered({
+ value: { [key]: value },
+ chainInfo,
+ id: `${decodedCall.type}-${index}`,
+ preventBalanceFormating
+ })
)}
>
diff --git a/packages/ui/src/components/EasySetup/FromCallData.tsx b/packages/ui/src/components/EasySetup/FromCallData.tsx
index 13ea3d8b..fba3b3a6 100644
--- a/packages/ui/src/components/EasySetup/FromCallData.tsx
+++ b/packages/ui/src/components/EasySetup/FromCallData.tsx
@@ -8,6 +8,7 @@ import { useCallInfoFromCallData } from '../../hooks/useCallInfoFromCallData'
import { getExtrinsicName } from '../../utils/getExtrinsicName'
import { usePjsLinks } from '../../hooks/usePjsLinks'
import { Binary, HexString, Transaction } from 'polkadot-api'
+import { getPapiHowLink } from '../../utils/getPapiHowLink'
interface Props {
className?: string
@@ -40,7 +41,8 @@ const FromCallData = ({ className, onSetExtrinsic }: Props) => {
}
return call
- } catch {
+ } catch (e: unknown) {
+ !!e && setCallDataError(String(e))
return
}
},
@@ -82,13 +84,21 @@ const FromCallData = ({ className, onSetExtrinsic }: Props) => {
return (
- Paste below the "encoded call data" from a{' '}
+ Paste below the "encoded call data" from{' '}
+
+ papi console
+ {' '}
+ or{' '}
- manual extrinsic
+ pjs manual extrinsic
.
Multix will take care of wrapping it in a multisig/proxy call
diff --git a/packages/ui/src/components/library/TextFieldStyled.tsx b/packages/ui/src/components/library/TextFieldStyled.tsx
index ac3cc220..188ad21a 100644
--- a/packages/ui/src/components/library/TextFieldStyled.tsx
+++ b/packages/ui/src/components/library/TextFieldStyled.tsx
@@ -50,13 +50,14 @@ const TextFieldStyled = styled(TextField)`
&.Mui-error {
outline: 3px solid ${({ theme }) => theme.custom.error};
+ margin-bottom: 1rem;
}
}
.MuiFormHelperText-root {
&.Mui-error {
position: absolute;
- bottom: -24px;
+ bottom: -1rem;
}
}
`
diff --git a/packages/ui/src/hooks/useGetMultisigTx.tsx b/packages/ui/src/hooks/useGetMultisigTx.tsx
index da9c3619..58aa7d20 100644
--- a/packages/ui/src/hooks/useGetMultisigTx.tsx
+++ b/packages/ui/src/hooks/useGetMultisigTx.tsx
@@ -61,10 +61,10 @@ export const useGetMultisigTx = ({
}
if (forceAsMulti && !extrinsicToCall) {
- console.warn(
- 'The extrinsic call is required when multisig.asMulti is called',
- extrinsicToCall
- )
+ // console.warn(
+ // 'The extrinsic call is required when multisig.asMulti is called',
+ // extrinsicToCall
+ // )
return
}
diff --git a/packages/ui/src/pages/Creation/WithProxySelection.tsx b/packages/ui/src/pages/Creation/WithProxySelection.tsx
index b9f047c5..4fc74b74 100644
--- a/packages/ui/src/pages/Creation/WithProxySelection.tsx
+++ b/packages/ui/src/pages/Creation/WithProxySelection.tsx
@@ -13,7 +13,19 @@ const WithProxySelection = ({ setWithProxy, withProxy, className }: Props) => {
Pure proxy
+ Use a pure proxy (not cross-chain{' '}
+
+ see here
+
+ )
+ >
+ }
control={
(
className={className}
title={
- Using a proxy makes the multisig more flexible. You can then change the signatories without
- changing the proxy address. More info{' '}
+ Using a pure proxy has advantages and drawbacks see when it makes sense to use it{' '}
- in this video
+ in the docs
.
diff --git a/packages/ui/src/pages/Creation/index.tsx b/packages/ui/src/pages/Creation/index.tsx
index e1207cc2..1aa048eb 100644
--- a/packages/ui/src/pages/Creation/index.tsx
+++ b/packages/ui/src/pages/Creation/index.tsx
@@ -276,12 +276,6 @@ const MultisigCreation = ({ className }: Props) => {
threshold
])
- useEffect(() => {
- // default to using a proxy
- if (supportsProxy) {
- setWithProxy(true)
- }
- }, [supportsProxy])
useEffect(() => {
setErrorMessage('')
diff --git a/packages/ui/src/utils/getPapiHowLink.ts b/packages/ui/src/utils/getPapiHowLink.ts
new file mode 100644
index 00000000..14cd9bd8
--- /dev/null
+++ b/packages/ui/src/utils/getPapiHowLink.ts
@@ -0,0 +1 @@
+export const getPapiHowLink = () => 'https://dev.papi.how'
diff --git a/yarn.lock b/yarn.lock
index c1e7ffa4..c1ac0b08 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3689,15 +3689,15 @@ __metadata:
languageName: node
linkType: hard
-"@reactive-dot/react@npm:^0.27.1":
- version: 0.27.1
- resolution: "@reactive-dot/react@npm:0.27.1"
+"@reactive-dot/react@npm:^0.28.0":
+ version: 0.28.0
+ resolution: "@reactive-dot/react@npm:0.28.0"
dependencies:
"@reactive-dot/core": ^0.27.1
jotai: ^2.10.3
peerDependencies:
- react: 18.x
- checksum: 5e932f259c0c9ec99e0481f05cbba2f1920805e8df11f13722cd6ba999d588b1552ecf479b6cfe1c2aded27fa7876217b066731355dad4233bf8bb325d6b96e8
+ react: 18.x || 19.x
+ checksum: c23a4f2a400864773c1408770d1bb38454fb02ffb9cb94372e8c2d6cc8d1a0aeb8490816454a6b51bc55a2e9f303bfbfaade76fa16d9e86459f22fe9aee1298f
languageName: node
linkType: hard
@@ -10166,7 +10166,7 @@ __metadata:
"@polkadot/react-identicon": ^3.11.3
"@polkadot/util-crypto": ^13.2.3
"@reactive-dot/core": ^0.27.1
- "@reactive-dot/react": ^0.27.1
+ "@reactive-dot/react": ^0.28.0
"@tanstack/react-query": ^5.62.2
"@types/node": ^22.10.1
"@types/react-dom": ^18.3.1
From 23f67415d83ce0bb161a13900e681e4ad158ab66 Mon Sep 17 00:00:00 2001
From: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com>
Date: Tue, 17 Dec 2024 23:13:40 +0100
Subject: [PATCH 3/4] Support hydra specific calls (#611)
---
.../components/EasySetup/BalancesTransfer.tsx | 26 +++++++----
.../src/components/modals/ChangeMultisig.tsx | 45 +++++++++++++------
packages/ui/src/hooks/useGetMultisigTx.tsx | 32 ++++++++-----
packages/ui/src/pages/Creation/index.tsx | 21 ++++++---
4 files changed, 84 insertions(+), 40 deletions(-)
diff --git a/packages/ui/src/components/EasySetup/BalancesTransfer.tsx b/packages/ui/src/components/EasySetup/BalancesTransfer.tsx
index 73d4bde4..2d77660f 100644
--- a/packages/ui/src/components/EasySetup/BalancesTransfer.tsx
+++ b/packages/ui/src/components/EasySetup/BalancesTransfer.tsx
@@ -8,8 +8,9 @@ import { inputToBigInt, getGlobalMaxValue } from '../../utils/bnUtils'
import { TextField } from '../library'
import { getOptionLabel } from '../../utils/getOptionLabel'
import { useAccountBaseFromAccountList } from '../../hooks/useAccountBaseFromAccountList'
-import { dot, MultiAddress } from '@polkadot-api/descriptors'
+import { dot, hydration, MultiAddress } from '@polkadot-api/descriptors'
import { Transaction, TypedApi } from 'polkadot-api'
+import { useNetwork } from '../../contexts/NetworkContext'
interface Props {
className?: string
@@ -31,6 +32,7 @@ const BalancesTransfer = ({ className, onSetExtrinsic, onSetErrorMessage, from }
})
const maxValue = useMemo(() => getGlobalMaxValue(128), [])
const toAddress = useMemo(() => selected?.address || '', [selected?.address])
+ const { selectedNetwork } = useNetwork()
useEffect(() => {
if (!!amount && !hasEnoughFreeBalance) {
@@ -41,7 +43,7 @@ const BalancesTransfer = ({ className, onSetExtrinsic, onSetErrorMessage, from }
}, [amount, amountError, hasEnoughFreeBalance, onSetErrorMessage])
useEffect(() => {
- if (!api) {
+ if (!api || !selectedNetwork) {
onSetExtrinsic(undefined)
return
}
@@ -51,13 +53,19 @@ const BalancesTransfer = ({ className, onSetExtrinsic, onSetErrorMessage, from }
return
}
- onSetExtrinsic(
- (api as TypedApi).tx.Balances.transfer_keep_alive({
- dest: MultiAddress.Id(toAddress),
- value: amount
- })
- )
- }, [amount, api, chainInfo, onSetExtrinsic, toAddress])
+ const extrinsic =
+ selectedNetwork === 'hydration'
+ ? (api as TypedApi).tx.Balances.transfer_keep_alive({
+ dest: toAddress,
+ value: amount
+ })
+ : (api as TypedApi).tx.Balances.transfer_keep_alive({
+ dest: MultiAddress.Id(toAddress),
+ value: amount
+ })
+
+ onSetExtrinsic(extrinsic)
+ }, [amount, api, chainInfo, onSetExtrinsic, selectedNetwork, toAddress])
const onAddressDestChange = useCallback((account: AccountBaseInfo) => {
setSelected(account)
diff --git a/packages/ui/src/components/modals/ChangeMultisig.tsx b/packages/ui/src/components/modals/ChangeMultisig.tsx
index 6d539935..2be161cf 100644
--- a/packages/ui/src/components/modals/ChangeMultisig.tsx
+++ b/packages/ui/src/components/modals/ChangeMultisig.tsx
@@ -31,7 +31,8 @@ import { useGetSortAddress } from '../../hooks/useGetSortAddress'
import { useGetMultisigAddress } from '../../contexts/useGetMultisigAddress'
import { getAsMultiTx } from '../../utils/getAsMultiTx'
import { Enum, TypedApi } from 'polkadot-api'
-import { dot, MultiAddress } from '@polkadot-api/descriptors'
+import { dot, hydration, MultiAddress } from '@polkadot-api/descriptors'
+import { useNetwork } from '../../contexts/NetworkContext'
interface Props {
onClose: () => void
@@ -41,6 +42,7 @@ interface Props {
type Step = 'selection' | 'summary' | 'call1' | 'call2'
const ChangeMultisig = ({ onClose, className }: Props) => {
+ const { selectedNetwork } = useNetwork()
const modalRef = useRef(null)
const { api, chainInfo, compatibilityToken } = useApi()
const { selectedMultiProxy, getMultisigAsAccountBaseInfo, getMultisigByAddress } = useMultiProxy()
@@ -87,7 +89,7 @@ const ChangeMultisig = ({ onClose, className }: Props) => {
const [callError, setCallError] = useState('')
const secondCall = useMemo(() => {
- if (!api || !compatibilityToken) {
+ if (!api || !compatibilityToken || !selectedNetwork) {
// console.error('api is not ready')
return
}
@@ -120,17 +122,31 @@ const ChangeMultisig = ({ onClose, className }: Props) => {
selectedMultisig.signatories.filter((sig) => sig !== selectedAccount.address)
)
- const addProxyTx = (api as TypedApi).tx.Proxy.add_proxy({
- delegate: MultiAddress.Id(newMultisigAddress),
- proxy_type: Enum('Any'),
- delay: 0
- })
-
- const proxyTx = (api as TypedApi).tx.Proxy.proxy({
- real: MultiAddress.Id(selectedMultiProxy?.proxy),
- force_proxy_type: undefined,
- call: addProxyTx.decodedCall
- })
+ const addProxyTx =
+ selectedNetwork === 'hydration'
+ ? (api as TypedApi).tx.Proxy.add_proxy({
+ delegate: newMultisigAddress,
+ proxy_type: Enum('Any'),
+ delay: 0
+ })
+ : (api as TypedApi).tx.Proxy.add_proxy({
+ delegate: MultiAddress.Id(newMultisigAddress),
+ proxy_type: Enum('Any'),
+ delay: 0
+ })
+
+ const proxyTx =
+ selectedNetwork === 'hydration'
+ ? (api as TypedApi).tx.Proxy.proxy({
+ real: selectedMultiProxy?.proxy,
+ force_proxy_type: undefined,
+ call: addProxyTx.decodedCall
+ })
+ : (api as TypedApi).tx.Proxy.proxy({
+ real: MultiAddress.Id(selectedMultiProxy?.proxy),
+ force_proxy_type: undefined,
+ call: addProxyTx.decodedCall
+ })
// call with the old multisig to delete the new one
return getAsMultiTx({
api,
@@ -149,7 +165,8 @@ const ChangeMultisig = ({ onClose, className }: Props) => {
oldThreshold,
selectedAccount,
selectedMultiProxy?.proxy,
- selectedMultisig
+ selectedMultisig,
+ selectedNetwork
])
const firstCall = useMemo(() => {
diff --git a/packages/ui/src/hooks/useGetMultisigTx.tsx b/packages/ui/src/hooks/useGetMultisigTx.tsx
index 58aa7d20..891caaa3 100644
--- a/packages/ui/src/hooks/useGetMultisigTx.tsx
+++ b/packages/ui/src/hooks/useGetMultisigTx.tsx
@@ -6,7 +6,8 @@ import { getAsMultiTx } from '../utils/getAsMultiTx'
import { MultisigStorageInfo, Weight } from '../types'
import { getApproveAsMultiTx } from '../utils/getApproveAsMultiTx'
import { HexString, Transaction, TypedApi } from 'polkadot-api'
-import { dot, MultiAddress } from '@polkadot-api/descriptors'
+import { dot, hydration, MultiAddress } from '@polkadot-api/descriptors'
+import { useNetwork } from '../contexts/NetworkContext'
interface Params {
selectedMultisig?: MultiProxy['multisigs'][0]
@@ -37,6 +38,7 @@ export const useGetMultisigTx = ({
}: Params) => {
const { api, compatibilityToken } = useApi()
const { getSortAddress } = useGetSortAddress()
+ const { selectedNetwork } = useNetwork()
const multisigTx = useMemo(() => {
if (!selectedMultisig?.signatories) {
@@ -52,7 +54,7 @@ export const useGetMultisigTx = ({
return
}
- if (!api) {
+ if (!api || !selectedNetwork) {
return
}
@@ -85,11 +87,18 @@ export const useGetMultisigTx = ({
try {
// the proxy is selected
if (isProxy && !!extrinsicToCall) {
- tx = (api as TypedApi).tx.Proxy.proxy({
- real: MultiAddress.Id(fromAddress),
- force_proxy_type: undefined,
- call: extrinsicToCall.decodedCall
- })
+ tx =
+ selectedNetwork === 'hydration'
+ ? (api as TypedApi).tx.Proxy.proxy({
+ real: fromAddress,
+ force_proxy_type: undefined,
+ call: extrinsicToCall.decodedCall
+ })
+ : (api as TypedApi).tx.Proxy.proxy({
+ real: MultiAddress.Id(fromAddress),
+ force_proxy_type: undefined,
+ call: extrinsicToCall.decodedCall
+ })
// a multisig is selected
} else {
tx = extrinsicToCall
@@ -109,19 +118,20 @@ export const useGetMultisigTx = ({
console.error(e)
}
}, [
- selectedMultisig?.signatories,
+ selectedMultisig,
getSortAddress,
threshold,
api,
+ selectedNetwork,
senderAddress,
fromAddress,
- extrinsicToCall,
- isProxy,
forceAsMulti,
+ extrinsicToCall,
approvalLength,
+ approveAsMultiHash,
+ isProxy,
weight,
when,
- approveAsMultiHash,
compatibilityToken
])
diff --git a/packages/ui/src/pages/Creation/index.tsx b/packages/ui/src/pages/Creation/index.tsx
index 1aa048eb..e8477452 100644
--- a/packages/ui/src/pages/Creation/index.tsx
+++ b/packages/ui/src/pages/Creation/index.tsx
@@ -29,7 +29,8 @@ import { isEthereumAddress } from '@polkadot/util-crypto'
import { getAsMultiTx } from '../../utils/getAsMultiTx'
import { useMultiProxy } from '../../contexts/MultiProxyContext'
import { Binary, Enum, Transaction, TypedApi } from 'polkadot-api'
-import { dot, MultiAddress } from '@polkadot-api/descriptors'
+import { dot, hydration, MultiAddress } from '@polkadot-api/descriptors'
+import { useNetwork } from '../../contexts/NetworkContext'
interface Props {
className?: string
@@ -37,6 +38,7 @@ interface Props {
const steps = ['Signatories', 'Threshold & Name', 'Review']
const MultisigCreation = ({ className }: Props) => {
+ const { selectedNetwork } = useNetwork()
const [signatories, setSignatories] = useState([])
const [currentStep, setCurrentStep] = useState(0)
const isLastStep = useMemo(() => currentStep === steps.length - 1, [currentStep])
@@ -146,7 +148,7 @@ const MultisigCreation = ({ className }: Props) => {
// this batchCall is only useful if the user wants a proxy.
return
}
- if (!api || !compatibilityToken) {
+ if (!api || !compatibilityToken || !selectedNetwork) {
// console.error('api is not ready')
return
}
@@ -187,10 +189,16 @@ const MultisigCreation = ({ className }: Props) => {
})
// Some funds are needed on the multisig for the pure proxy creation
- const transferTx = (api as TypedApi).tx.Balances.transfer_keep_alive({
- dest: MultiAddress.Id(multiAddress),
- value: pureProxyCreationNeededFunds
- })
+ const transferTx =
+ selectedNetwork === 'hydration'
+ ? (api as TypedApi).tx.Balances.transfer_keep_alive({
+ dest: multiAddress,
+ value: pureProxyCreationNeededFunds
+ })
+ : (api as TypedApi).tx.Balances.transfer_keep_alive({
+ dest: MultiAddress.Id(multiAddress),
+ value: pureProxyCreationNeededFunds
+ })
if (!multiSigProxyCall) {
console.error('multiSigProxyCall is undefined in Creation index.tsx')
@@ -209,6 +217,7 @@ const MultisigCreation = ({ className }: Props) => {
multiAddress,
pureProxyCreationNeededFunds,
selectedAccount,
+ selectedNetwork,
signatories,
threshold,
withProxy
From ec1613164464e3c96c64a651507e8498e5738e3a Mon Sep 17 00:00:00 2001
From: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com>
Date: Fri, 20 Dec 2024 11:41:00 +0100
Subject: [PATCH 4/4] Support chopsticks with archive unstable body (#602)
---
package.json | 2 +-
.../support/page-objects/landingPage.ts | 1 +
packages/ui/cypress/tests/address-bar.cy.ts | 8 +++-
.../ui/cypress/tests/default-multisigs.cy.ts | 35 ++++++--------
.../ui/cypress/tests/multisig-creation.cy.ts | 34 ++++++--------
.../ui/cypress/tests/watched-accounts.cy.ts | 38 +++++++++------
packages/ui/cypress/utils/getShortAddress.ts | 1 +
.../Transactions/TransactionList.tsx | 2 +-
.../ui/src/hooks/useCallInfoFromCallData.tsx | 2 +-
packages/ui/src/hooks/usePendingTx.tsx | 9 ++--
squid/.env.example | 46 +++----------------
11 files changed, 75 insertions(+), 103 deletions(-)
create mode 100644 packages/ui/cypress/utils/getShortAddress.ts
diff --git a/package.json b/package.json
index 7d3924ad..4ea0c871 100644
--- a/package.json
+++ b/package.json
@@ -27,7 +27,7 @@
"lint:fix": "yarn workspaces foreach run lint:fix",
"formatAll": "prettier --write .",
"start:chopsticks-test-build-and-launch-all": "concurrently --kill-others 'npm run start:chopsticks' 'npm run ui:start-with-chopsticks' 'npm run docker:down && npm run docker:db && npm run build:indexer && npm run indexer:start:chopsticks-local' 'npm run start:graphql-server'",
- "start:chopsticks": "npx --yes @acala-network/chopsticks@1.0.1 --config chopsticks-config.yml",
+ "start:chopsticks": "npx --yes @acala-network/chopsticks@1.0.2-1 --config chopsticks-config.yml",
"start:graphql-server": "cd squid && npm run start:graphql-server",
"indexer:start:chopsticks-ci": "cd squid && npm run start:chopsticks-ci",
"indexer:start:chopsticks-local": "cd squid && npm run start:chopsticks-local",
diff --git a/packages/ui/cypress/support/page-objects/landingPage.ts b/packages/ui/cypress/support/page-objects/landingPage.ts
index 3d482fed..452c01a4 100644
--- a/packages/ui/cypress/support/page-objects/landingPage.ts
+++ b/packages/ui/cypress/support/page-objects/landingPage.ts
@@ -20,6 +20,7 @@ export const landingPage = {
multisigCreationInfoBanner: (timeout = 4000) =>
cy.get('[data-cy=banner-multisig-creation-info]', { timeout }),
creationInfoBannerCloseButton: () => cy.get('[data-cy=button-close-multisig-creation-info]'),
+ transactionListLoader: () => cy.get('[data-cy=loader-transaction-list]'),
// page specific assertion
shouldHaveNoAccountErrorAndWikiLink() {
diff --git a/packages/ui/cypress/tests/address-bar.cy.ts b/packages/ui/cypress/tests/address-bar.cy.ts
index 986075d3..cfc69ad3 100644
--- a/packages/ui/cypress/tests/address-bar.cy.ts
+++ b/packages/ui/cypress/tests/address-bar.cy.ts
@@ -277,11 +277,15 @@ describe('Account address in the address bar', () => {
watchedAccounts: [publicKey]
})
+ multisigPage.accountHeader().within(() => {
+ accountDisplay.addressLabel().should('contain.text', address.slice(0, 6))
+ })
+
// check that there is an address in the address bar
cy.url({ timeout: 3000 }).should('include', address)
- // react-router takes some time to get the search params inside the links
- cy.wait(1000)
+ // wait for the loader to be done otherwise the test fails
+ landingPage.transactionListLoader().should('not.exist')
topMenuItems.homeButton().click()
cy.url().should('include', address)
diff --git a/packages/ui/cypress/tests/default-multisigs.cy.ts b/packages/ui/cypress/tests/default-multisigs.cy.ts
index f36f8d0b..25530d36 100644
--- a/packages/ui/cypress/tests/default-multisigs.cy.ts
+++ b/packages/ui/cypress/tests/default-multisigs.cy.ts
@@ -15,24 +15,17 @@ describe('default Multisigs', () => {
watchedAccounts: [lolmcshizPubKey]
})
- multisigPage.accountHeader().within(() => {
- accountDisplay
- .addressLabel()
- .invoke('text')
- .as('defaultPolkadotAddress')
- .should('not.contain', polkadotSelectedMultiproxy.slice(0, 6))
- })
-
- cy.log('@defaultPolkadotAddress', cy.get('@defaultPolkadotAddress'))
-
// select another one
- topMenuItems.desktopMenu().within(() =>
+ topMenuItems.desktopMenu().within(() => {
topMenuItems
- .multiproxySelectorDesktop()
- .wait(1000)
+ .multiproxySelectorInputDesktop()
+ .should('not.have.value', '')
.click()
- .type(`${polkadotSelectedMultiproxy.slice(0, 6)}{downArrow}{enter}`)
- )
+ .type(`${polkadotSelectedMultiproxy.slice(0, 6)}{downArrow}{enter}`, {
+ delay: 100,
+ timeout: 6000
+ })
+ })
// verify that it's displayed
multisigPage.accountHeader().within(() => {
@@ -47,19 +40,19 @@ describe('default Multisigs', () => {
accountDisplay
.addressLabel()
.invoke('text')
- .as('defaultKusamaAddress')
.should('not.contain', kusamaSelectedMultiproxy.slice(0, 6))
})
- cy.log('@defaultKusamaAddress', cy.get('@defaultKusamaAddress'))
-
// select another one
topMenuItems.desktopMenu().within(() =>
topMenuItems
- .multiproxySelectorDesktop()
- .wait(1000)
+ .multiproxySelectorInputDesktop()
+ .should('not.have.value', '')
.click()
- .type(`${kusamaSelectedMultiproxy.slice(0, 6)}{downArrow}{enter}`)
+ .type(`${kusamaSelectedMultiproxy.slice(0, 6)}{downArrow}{enter}`, {
+ delay: 100,
+ timeout: 6000
+ })
)
// verify that it's displayed
diff --git a/packages/ui/cypress/tests/multisig-creation.cy.ts b/packages/ui/cypress/tests/multisig-creation.cy.ts
index 720a5822..40e65710 100644
--- a/packages/ui/cypress/tests/multisig-creation.cy.ts
+++ b/packages/ui/cypress/tests/multisig-creation.cy.ts
@@ -117,17 +117,14 @@ describe('Multisig creation', () => {
verifySignatories()
- // this is commented because chopsticks doesnot support archive_unstable_hashByHeight
- // see https://github.com/AcalaNetwork/chopsticks/issues/852
-
// there should be a pending pure proxy creation
- // multisigPage
- // .transactionList()
- // .should('be.visible')
- // .within(() => {
- // multisigPage.pendingTransactionItem().should('have.length', 1)
- // multisigPage.pendingTransactionCallName().should('contain.text', 'proxy.createPure')
- // })
+ multisigPage
+ .transactionList()
+ .should('be.visible')
+ .within(() => {
+ multisigPage.pendingTransactionItem().should('have.length', 1)
+ multisigPage.pendingTransactionCallName().should('contain.text', 'Proxy.create_pure')
+ })
})
it('Create a multisig without a pure proxy', () => {
@@ -174,15 +171,14 @@ describe('Multisig creation', () => {
verifySignatories()
- // this is commented because chopsticks doesnot support archive_unstable_hashByHeight
- // see https://github.com/AcalaNetwork/chopsticks/issues/852
- // multisigPage
- // .transactionList()
- // .should('be.visible')
- // .within(() => {
- // multisigPage.pendingTransactionItem().should('have.length', 1)
- // multisigPage.pendingTransactionCallName().should('contain.text', 'remark:')
- // })
+ // there should be a pending remark
+ multisigPage
+ .transactionList()
+ .should('be.visible')
+ .within(() => {
+ multisigPage.pendingTransactionItem().should('have.length', 1)
+ multisigPage.pendingTransactionCallName().should('contain.text', 'System.remark')
+ })
})
})
diff --git a/packages/ui/cypress/tests/watched-accounts.cy.ts b/packages/ui/cypress/tests/watched-accounts.cy.ts
index a100cf7f..1d12e52b 100644
--- a/packages/ui/cypress/tests/watched-accounts.cy.ts
+++ b/packages/ui/cypress/tests/watched-accounts.cy.ts
@@ -11,9 +11,10 @@ import { multisigPage } from '../support/page-objects/multisigPage'
import { editNamesModal } from '../support/page-objects/modals/editNamesModal'
import { testAccounts } from '../fixtures/testAccounts'
import { knownMultisigs } from '../fixtures/knownMultisigs'
+import { getShortAddress } from '../utils/getShortAddress'
const addWatchAccount = (address: string, name?: string) => {
- settingsPage.accountAddressInput().type(`${address}{enter}`, { delay: 20 })
+ settingsPage.accountAddressInput().type(`${address}{enter}`, { delay: 20, timeout: 6000 })
if (name) {
settingsPage.accountNameInput().type(name)
@@ -304,7 +305,7 @@ describe('Watched Accounts', () => {
it('can see all multisigs that a watched signatory is a member of', () => {
const { publicKey: signatoryPublicKey } = testAccounts['Multisig Member Account 1']
- const expectedAddresses = [
+ const expectedMultiproxies = [
{
address: knownMultisigs['test-simple-multisig-1'].address,
expectedBadge: 'multi'
@@ -323,24 +324,33 @@ describe('Watched Accounts', () => {
// ensure all multisigs are displayed in the multiproxy selector
topMenuItems
.multiproxySelectorOptionDesktop()
- .should('have.length', expectedAddresses.length)
- .each(($el, index) => {
+ .should('have.length', expectedMultiproxies.length)
+ .each(($el) => {
cy.wrap($el).within(() => {
accountDisplay
.addressLabel()
- .should('contain.text', expectedAddresses[index].address.slice(0, 6))
- accountDisplay.watchedIcon().should('be.visible')
- if (expectedAddresses[index].expectedBadge === 'pure') {
- accountDisplay.pureBadge().should('be.visible')
- } else {
- accountDisplay.multisigBadge().should('be.visible')
- }
+ .invoke('text')
+ .then((address) => {
+ const account = expectedMultiproxies.find((a) => {
+ return getShortAddress(a.address) === (address as unknown as string)
+ })
+ cy.wrap(account).should('not.be.undefined')
+ accountDisplay.watchedIcon().should('be.visible')
+ if (account?.expectedBadge === 'pure') {
+ accountDisplay.pureBadge().should('be.visible')
+ } else {
+ accountDisplay.multisigBadge().should('be.visible')
+ }
+ })
})
})
// ensure each multisig that the signatory is a member of can be viewed
- expectedAddresses.forEach(({ address }, index) => {
- topMenuItems.multiproxySelectorDesktop().click()
- topMenuItems.multiproxySelectorOptionDesktop().eq(index).click()
+ expectedMultiproxies.forEach(({ address }) => {
+ topMenuItems
+ .multiproxySelectorDesktop()
+ .click()
+ .type(`${address.slice(0, 6)}{downArrow}{enter}`)
+
multisigPage
.accountHeader()
.should('be.visible')
diff --git a/packages/ui/cypress/utils/getShortAddress.ts b/packages/ui/cypress/utils/getShortAddress.ts
new file mode 100644
index 00000000..a9dce5db
--- /dev/null
+++ b/packages/ui/cypress/utils/getShortAddress.ts
@@ -0,0 +1 @@
+export const getShortAddress = (address: string) => `${address.slice(0, 6)}..${address.slice(-6)}`
diff --git a/packages/ui/src/components/Transactions/TransactionList.tsx b/packages/ui/src/components/Transactions/TransactionList.tsx
index 3f94e839..89944406 100644
--- a/packages/ui/src/components/Transactions/TransactionList.tsx
+++ b/packages/ui/src/components/Transactions/TransactionList.tsx
@@ -29,7 +29,7 @@ const TransactionList = ({ className }: Props) => {
Transactions
{isLoadingPendingTxs && (
-
+
)}
diff --git a/packages/ui/src/hooks/useCallInfoFromCallData.tsx b/packages/ui/src/hooks/useCallInfoFromCallData.tsx
index 441278b1..b406ec6a 100644
--- a/packages/ui/src/hooks/useCallInfoFromCallData.tsx
+++ b/packages/ui/src/hooks/useCallInfoFromCallData.tsx
@@ -34,7 +34,7 @@ export const useCallInfoFromCallData = (callData?: HexString) => {
decodedCall: tx?.decodedCall,
call: tx,
hash: hashFromTx(callData),
- weight: { proof_size: weight.proof_size, ref_time: weight.ref_time },
+ weight,
section: tx?.decodedCall.type,
method: tx?.decodedCall.value.type
})
diff --git a/packages/ui/src/hooks/usePendingTx.tsx b/packages/ui/src/hooks/usePendingTx.tsx
index 72d48fb8..d366829a 100644
--- a/packages/ui/src/hooks/usePendingTx.tsx
+++ b/packages/ui/src/hooks/usePendingTx.tsx
@@ -38,7 +38,7 @@ type AggGroupedByDate = { [index: string]: CallDataInfoFromChain[] }
const opaqueMetadata = Tuple(compact, Bin(Infinity)).dec
const getExtDecoderAt = async (api: ApiType, client: PolkadotClient, blockHash?: string) => {
- const rawMetadata = await (blockHash
+ const rawMetadata = await (blockHash && !import.meta.env.DEV
? client
._request<{
result: HexString
@@ -108,9 +108,10 @@ const getCallDataFromChainPromise = (
) =>
pendingTxData.map(async (pendingTx) => {
const blockNumber = pendingTx.info.when.height
- const blockHash = (
- await client._request('archive_unstable_hashByHeight', [blockNumber])
- )?.[0] as HexString | undefined
+ const blockHashes = await client._request('archive_unstable_hashByHeight', [blockNumber])
+ const blockHash = (Array.isArray(blockHashes) ? blockHashes?.[0] : blockHashes) as
+ | HexString
+ | undefined
if (!blockHash) {
console.log('no hash found for height', blockNumber)
diff --git a/squid/.env.example b/squid/.env.example
index 73b3e62c..580d2fc3 100644
--- a/squid/.env.example
+++ b/squid/.env.example
@@ -2,44 +2,10 @@ DB_PORT=5432
GQL_PORT=4350
SQD_DEBUG=sqd:processor:mapping
-#rococo
-# BLOCK_START=3510000
-# PREFIX=42
-# RPC_WS="wss://rococo-rpc.polkadot.io"
-# CHAIN_ID='rococo'
-#kusama
-# BLOCK_START=15000000
-# PREFIX=2
-# RPC_WS="wss://rpc.ibp.network/kusama"
-# CHAIN_ID='kusama'
-
-#polkadot
-# BLOCK_START=12000000
-# PREFIX=0
-# RPC_WS="wss://rpc.ibp.network/polkadot"
-# CHAIN_ID='polkadot'
-
-#rhala
-# BLOCK_START=0
-# PREFIX=30
-# RPC_WS="wss://rhala-node.phala.network/w
-# CHAIN_ID='rhala'
-
-#phala
-# BLOCK_START=2400000
-# PREFIX=30
-# RPC_WS="wss://priv-api.phala.network/phala/ws"
-# CHAIN_ID='phala'
-
-#khala
-# BLOCK_START=0
-# PREFIX=30
-# RPC_WS="wss://khala-api.phala.network/ws"
-# CHAIN_ID='khala'
-
-# hydra rococo
-RPC_WS="wss://hydradx-rococo-rpc.play.hydration.cloud"
-CHAIN_ID='hydra-rococo'
-BLOCK_START=1560491
-PREFIX=30
\ No newline at end of file
+#paseo
+BLOCK_START=0
+PREFIX=0
+RPC_WS="wss://rpc.ibp.network/paseo"
+CHAIN_ID='paseo'
+GATEWAY_URL="https://v2.archive.subsquid.io/network/paseo"
\ No newline at end of file