diff --git a/packages/ui/cypress/tests/hydration-extrinsic-display.cy.ts b/packages/ui/cypress/tests/hydration-extrinsic-display.cy.ts index 8e7f1f6b..77298bbd 100644 --- a/packages/ui/cypress/tests/hydration-extrinsic-display.cy.ts +++ b/packages/ui/cypress/tests/hydration-extrinsic-display.cy.ts @@ -17,7 +17,7 @@ describe('Verify extrinsics display', () => { }) }) - it('The omnipool.sell extrinsic is displayed in plank', () => { + it('The Router.sell extrinsic is displayed in plank', () => { multisigPage.accountHeader().within(() => { accountDisplay.addressLabel().should('contain.text', expectedMultisigAddress.slice(0, 6)) }) @@ -28,17 +28,17 @@ describe('Verify extrinsics display', () => { .within(() => { multisigPage.pendingTransactionItem().should('have.length', 1) multisigPage.pendingTransactionItem().within(() => { - multisigPage.pendingTransactionCallName().should('contain.text', 'omnipool.sell') + multisigPage.pendingTransactionCallName().should('contain.text', 'Router.sell') multisigPage.unknownCallIcon().should('not.exist') multisigPage.unknownCallAlert().should('not.exist') expander.paramExpander().click() - expander.contentExpander().should('contain', 'amount: 10,000,000,000,000') - expander.contentExpander().should('contain', 'min_buy_amount: 59,509') + expander.contentExpander().should('contain', '"amount_in": "10000000000000"') + expander.contentExpander().should('contain', '"min_amount_out": "72179"') }) }) }) - it('A manual omnipool.sell extrinsic creation has input in plank', () => { + it.skip('A manual omnipool.sell extrinsic creation has input in plank', () => { multisigPage.accountHeader().within(() => { accountDisplay.addressLabel().should('contain.text', expectedMultisigAddress.slice(0, 6)) }) @@ -56,7 +56,7 @@ describe('Verify extrinsics display', () => { sendTxModal.paramField('amount').should('not.contain', 'HDX') }) - it('A manual balances.transferKeepAlive extrinsic has input in HDX', () => { + it.skip('A manual balances.transferKeepAlive extrinsic has input in HDX', () => { multisigPage.accountHeader().within(() => { accountDisplay.addressLabel().should('contain.text', expectedMultisigAddress.slice(0, 6)) }) @@ -74,7 +74,7 @@ describe('Verify extrinsics display', () => { sendTxModal.paramField('value').should('contain', 'HDX') }) - it('A from call data balances.transferKeepAlive extrinsic has balance displayed in HDX and identicon for destination', () => { + it.skip('A from call data balances.transferKeepAlive extrinsic has balance displayed in HDX and identicon for destination', () => { const balanceTransferCallData = '0x0703d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d0b00b04e2bde6f' const sendingAmount = '123 HDX' @@ -98,4 +98,25 @@ describe('Verify extrinsics display', () => { }) }) }) + + it('A from call data balances.transferKeepAlive extrinsic is correctly displayed', () => { + const balanceTransferCallData = + '0x0703d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d0b00b04e2bde6f' + const sendingAmount = '"value": "123000000000000"' + const expectedRecipient = '"dest": "7NPoMQbiA6trJKkjB35uk96MeJD4PGWkLQLH7k7hXEkZpiba"' + + multisigPage.accountHeader().within(() => { + accountDisplay.addressLabel().should('contain.text', expectedMultisigAddress.slice(0, 6)) + }) + + multisigPage.newTransactionButton().click() + sendTxModal.sendTxTitle().should('be.visible') + sendTxModal.selectEasySetup().click() + sendTxModal.selectionEasySetupSetupFromCallData().click() + sendTxModal.callDataInput().click().type(balanceTransferCallData) + sendTxModal.sendTxContent().within(() => { + expander.contentExpander().should('contain', sendingAmount) + expander.contentExpander().should('contain', expectedRecipient) + }) + }) }) diff --git a/packages/ui/package.json b/packages/ui/package.json index 7af1bf40..47a87c2d 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -10,6 +10,7 @@ "@mui/material": "^5.15.15", "@mui/styled-engine": "^5.15.14", "@polkadot-api/descriptors": "portal:.papi/descriptors", + "@polkadot-api/tx-utils": "^0.0.4", "@polkadot-labs/hdkd": "^0.0.8", "@polkadot-labs/hdkd-helpers": "^0.0.8", "@polkadot/api": "^13.1.1", diff --git a/packages/ui/src/components/AccountDisplay/AccountDisplay.tsx b/packages/ui/src/components/AccountDisplay/AccountDisplay.tsx index 468bdf4b..67281ac6 100644 --- a/packages/ui/src/components/AccountDisplay/AccountDisplay.tsx +++ b/packages/ui/src/components/AccountDisplay/AccountDisplay.tsx @@ -100,15 +100,15 @@ const AccountDisplay = ({ className="multisigName" data-cy="label-account-name" > + {displayName} {!isLocalNameDisplayed && !!subIdentity && ( - {subIdentity}/ + /{subIdentity} )} - {displayName} {canEdit && !displayName && ( No Name diff --git a/packages/ui/src/components/EasySetup/FromCallData.tsx b/packages/ui/src/components/EasySetup/FromCallData.tsx index aee24825..fc35c438 100644 --- a/packages/ui/src/components/EasySetup/FromCallData.tsx +++ b/packages/ui/src/components/EasySetup/FromCallData.tsx @@ -26,19 +26,23 @@ const FromCallData = ({ className, onSetExtrinsic }: Props) => { setIsProxyProxyRemoved(false) if (!api) return call - const decodedCall = (await api.txFromCallData(Binary.fromHex(call))).decodedCall - - // check if this call is a proxy.proxy - if (decodedCall.type === 'Proxy' && decodedCall.value.type === 'proxy') { - // a proxy.proxy call is encoded with e.g - // callIndex 1e00 - // real 00 eb53ed54b7f921a438923e6eb52c4d89afc5c0fed5d0d15fb78648c53da227a0 - // forceProxyType 00 - setIsProxyProxyRemoved(true) - return `0x${call.substring(74)}` as HexString + try { + const decodedCall = (await api.txFromCallData(Binary.fromHex(call))).decodedCall + + // check if this call is a proxy.proxy + if (decodedCall.type === 'Proxy' && decodedCall.value.type === 'proxy') { + // a proxy.proxy call is encoded with e.g + // callIndex 1e00 + // real 00 eb53ed54b7f921a438923e6eb52c4d89afc5c0fed5d0d15fb78648c53da227a0 + // forceProxyType 00 + setIsProxyProxyRemoved(true) + return `0x${call.substring(74)}` as HexString + } + + return call + } catch (error) { + return } - - return call }, [api] ) diff --git a/packages/ui/src/hooks/useCallInfoFromCallData.tsx b/packages/ui/src/hooks/useCallInfoFromCallData.tsx index cfb93ae4..e357e342 100644 --- a/packages/ui/src/hooks/useCallInfoFromCallData.tsx +++ b/packages/ui/src/hooks/useCallInfoFromCallData.tsx @@ -25,25 +25,31 @@ export const useCallInfoFromCallData = (callData?: HexString) => { setIsGettingCallInfo(true) - const tx = api.txFromCallData(Binary.fromHex(callData), compatibilityToken) + try { + const tx = api.txFromCallData(Binary.fromHex(callData), compatibilityToken) - tx.getPaymentInfo(PAYMENT_INFO_ACCOUNT, { at: 'best' }) - .then(({ weight }) => { - setCallInfo({ - decodedCall: tx?.decodedCall, - call: tx, - hash: hashFromTx(callData), - weight: { proof_size: weight.proof_size, ref_time: weight.ref_time }, - method: tx?.decodedCall.type, - section: tx?.decodedCall.value.type + tx.getPaymentInfo(PAYMENT_INFO_ACCOUNT, { at: 'best' }) + .then(({ weight }) => { + setCallInfo({ + decodedCall: tx?.decodedCall, + call: tx, + hash: hashFromTx(callData), + weight: { proof_size: weight.proof_size, ref_time: weight.ref_time }, + method: tx?.decodedCall.type, + section: tx?.decodedCall.value.type + }) + setIsGettingCallInfo(false) }) - setIsGettingCallInfo(false) - }) - .catch((e) => { - console.error(e) - setIsGettingCallInfo(false) - setCallInfo(undefined) - }) + .catch((e) => { + console.error(e) + setIsGettingCallInfo(false) + setCallInfo(undefined) + }) + } catch (e) { + console.error(e) + setIsGettingCallInfo(false) + setCallInfo(undefined) + } }, [api, callData, compatibilityToken]) return { callInfo, isGettingCallInfo } diff --git a/packages/ui/src/hooks/useGetIdentity.tsx b/packages/ui/src/hooks/useGetIdentity.tsx index c381cdb5..0a336a4e 100644 --- a/packages/ui/src/hooks/useGetIdentity.tsx +++ b/packages/ui/src/hooks/useGetIdentity.tsx @@ -17,7 +17,7 @@ export const useGetIdentity = () => { return } - const { display, parentAddress } = await ( + const { sub, parentAddress } = await ( api as TypedApi ).query.Identity.SuperOf.getValue(address, { at: 'best' }).then((res) => { const [parentAddress, parentIdentity] = res || [] @@ -26,13 +26,12 @@ export const useGetIdentity = () => { return { parentAddress: '', display: '' } } - const display = - (parentIdentity.type !== 'None' && - parentIdentity.value && - (parentIdentity.value as FixedSizeBinary<3>).asText()) || - '' + const sub = + parentIdentity.type !== 'None' && parentIdentity.value + ? (parentIdentity.value as FixedSizeBinary<3>).asText() + : '' - return { display, parentAddress } + return { sub, parentAddress } }) const addressToUse = parentAddress || address @@ -43,7 +42,7 @@ export const useGetIdentity = () => { at: 'best' } ).then((val) => { - const id: IdentityInfo = { judgements: [], display } + const id: IdentityInfo = { judgements: [], sub } val?.[0].judgements.forEach(([, judgement]) => { id.judgements.push(judgement.type) }) @@ -53,13 +52,7 @@ export const useGetIdentity = () => { // console.log('value', JSONprint(value)); const text = (value as IdentityData)?.value as FixedSizeBinary<2> | undefined if (text) { - // if it has a parent this is the sub name - // as the display name is already set - if (key === 'display' && !!parentAddress) { - id['sub'] = text.asText() - } else { - id[key] = text.asText() - } + id[key] = text.asText() } } }) diff --git a/packages/ui/src/hooks/usePendingTx.tsx b/packages/ui/src/hooks/usePendingTx.tsx index 9d13347a..4b6b45e3 100644 --- a/packages/ui/src/hooks/usePendingTx.tsx +++ b/packages/ui/src/hooks/usePendingTx.tsx @@ -9,27 +9,11 @@ import { ApiType, useApi } from '../contexts/ApiContext' import dayjs from 'dayjs' import localizedFormat from 'dayjs/plugin/localizedFormat' import { PolkadotClient, Transaction } from 'polkadot-api' -import { getDynamicBuilder, getLookupFn } from '@polkadot-api/metadata-builders' -import { - AccountId, - Bin, - Binary, - compact, - createDecoder, - enhanceDecoder, - HexString, - metadata as metadataCodec, - Struct, - Tuple, - u8, - Variant, - type Decoder, - type StringRecord, - type V14 -} from '@polkadot-api/substrate-bindings' -import { Hex } from '@polkadot-api/substrate-bindings' +import { Bin, Binary, compact, HexString, Tuple } from '@polkadot-api/substrate-bindings' import { hashFromTx } from '../utils/txHash' import { getEncodedCallFromDecodedTx } from '../utils/getEncodedCallFromDecodedTx' +import { getExtrinsicDecoder } from '@polkadot-api/tx-utils' + dayjs.extend(localizedFormat) export interface PendingTx { @@ -62,120 +46,11 @@ const getExtDecoderAt = async (api: ApiType, client: PolkadotClient, blockHash?: .then((x) => opaqueMetadata(x.result)[1]) : api.apis.Metadata.metadata()) - const metadata = metadataCodec.dec(rawMetadata.asBytes()).metadata.value as V14 - const dynBuilder = getDynamicBuilder(getLookupFn(metadata)) - - const versionDec = enhanceDecoder(u8[1], (value) => ({ - version: value & ~(1 << 7), - signed: !!(value & (1 << 7)) - })) - - const address = Variant({ - Id: AccountId(), - Raw: Hex(), - Address32: Hex(32), - Address20: Hex(20) - }).dec - const signature = Variant({ - Ed25519: Hex(64), - Sr25519: Hex(64), - Ecdsa: Hex(65) - }).dec - - const extra = Struct.dec( - Object.fromEntries( - metadata.extrinsic.signedExtensions.map( - (x) => [x.identifier, dynBuilder.buildDefinition(x.type)[1]] as [string, Decoder] - ) - ) as StringRecord> - ) - - const allBytesDec = Hex(Infinity).dec - const signedBody = Struct.dec({ - address, - signature, - extra, - callData: allBytesDec - }) - - return createDecoder((data) => { - const len = compact.dec(data) - const { signed, version } = versionDec(data) - const body = signed ? signedBody : allBytesDec + const decoder = await getExtrinsicDecoder(rawMetadata.asOpaqueBytes()) - return { len, signed, version, body: body(data) } - }) + return decoder } -// export const getMultisigInfo = (call: ISanitizedCall): Partial[] => { -// const result: Partial[] = [] - -// const getCallResult = ({ args, method }: ISanitizedCall) => { -// if (typeof method !== 'string' && method.pallet === 'multisig') { -// if (method.method === 'asMulti' && typeof args.call?.method !== 'string') { -// result.push({ -// name: `${args.call?.method?.pallet}.${args.call?.method.method}`, -// hash: args.call?.hash, -// callData: args.callData as CallDataInfoFromChain['callData'] -// }) -// } else { -// result.push({ -// name: 'Unknown call', -// hash: (args?.call_hash as Uint8Array).toString() || undefined, -// callData: undefined -// }) -// } -// // this is not a multisig call -// // try to dig deeper -// } else { -// if (args.calls) { -// for (const call of args.calls) { -// getCallResult(call) -// } -// } else if (args.call) { -// getCallResult(args.call) -// } -// } -// } - -// getCallResult(call) -// return result -// } - -// export const getMultisigInfo = ( -// call: Transaction['decodedCall'] -// ): Partial[] => { -// const result: Partial[] = [] - -// // result.push({ -// // name: 'Unknown call', -// // hash: (args?.call_hash as Uint8Array).toString() || undefined, -// // callData: undefined -// // }) - -// const getCallResults = (call: Transaction['decodedCall']) => { -// if (call.type === 'Multisig') { -// if (call.value.type === 'as_Multi') { - -// result.push({ -// name: `${call.value.value.call.type}.${call.value.value.call.value.type}`, -// hash: args.call?.hash, -// callData: args.callData as CallDataInfoFromChain['callData'] -// }) -// } else { -// result.push({ -// name: 'Unknown call', -// hash: undefined, -// callData: undefined -// }) -// } -// } else { -// } -// } -// getCallResults(call) -// return result -// } - const getMultisigInfo = async ( call: Transaction['decodedCall'], api: ApiType @@ -252,12 +127,10 @@ const getCallDataFromChainPromise = ( const txPromises = body.map((extrinsics) => { const decodedExtrinsic = decoder(extrinsics) - const toDecode = decodedExtrinsic.signed - ? (decodedExtrinsic.body as any).callData - : decodedExtrinsic.body + const toDecode = decodedExtrinsic.callData // console.log('-----------------------------') // console.log(decodedExtrinsic) - return api.txFromCallData(Binary.fromHex(toDecode)) + return api.txFromCallData(toDecode) }) const allDecodedTxs = await Promise.all(txPromises) @@ -305,13 +178,6 @@ const getCallDataFromChainPromise = ( const { name, hash, callData } = multisigTxInfo - // let call: false | GenericCall = false - // try { - // call = !!callData && !!hash && ext.registry.createType('Call', callData) - // } catch (error) { - // console.error('Error in getCallDataFromChainPromise', error) - // } - return { callData, hash: hash || pendingTx.hash, diff --git a/yarn.lock b/yarn.lock index 9bd2c80e..53961c72 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3444,6 +3444,17 @@ __metadata: languageName: node linkType: hard +"@polkadot-api/tx-utils@npm:^0.0.4": + version: 0.0.4 + resolution: "@polkadot-api/tx-utils@npm:0.0.4" + dependencies: + "@polkadot-api/metadata-builders": 0.9.1 + "@polkadot-api/substrate-bindings": 0.9.3 + "@polkadot-api/utils": 0.1.2 + checksum: 002439dde254a4ac28db69019086314b521baee942ed4e2289907328e1385153a947277f49c4adb8954e0fc0fb8bc668a0549a8fab0fbb13c7cbe5e0e929f9de + languageName: node + linkType: hard + "@polkadot-api/utils@npm:0.1.0": version: 0.1.0 resolution: "@polkadot-api/utils@npm:0.1.0" @@ -10951,6 +10962,7 @@ __metadata: "@mui/material": ^5.15.15 "@mui/styled-engine": ^5.15.14 "@polkadot-api/descriptors": "portal:.papi/descriptors" + "@polkadot-api/tx-utils": ^0.0.4 "@polkadot-labs/hdkd": ^0.0.8 "@polkadot-labs/hdkd-helpers": ^0.0.8 "@polkadot/api": ^13.1.1