diff --git a/packages/ui/cypress/fixtures/knownMultisigs.ts b/packages/ui/cypress/fixtures/knownMultisigs.ts index c07b2c22..a6da17dd 100644 --- a/packages/ui/cypress/fixtures/knownMultisigs.ts +++ b/packages/ui/cypress/fixtures/knownMultisigs.ts @@ -8,5 +8,17 @@ export const knownMultisigs = { testAccounts['Multisig Member Account 1'].address, testAccounts['Multisig Member Account 2'].address ] + }, + 'multisig-with-unknown-transaction': { + address: '5CNUH7K6RgXqEo4zK3i9cEDX1CDozJkmvuK6cRZ3z8ovY5CN', + pureAddress: '5GeQBX3xT9oV5PVjnuakx6tRFEwxKGqnohfduWpp4MVt1uC6', + purePublicKey: '0xcaa3c7393cdc0d101797a216222c1d44a92bdc3653f6d0fadfad040adad4e091', + threshold: 2, + hashOfUknownCall: '0x49478dcdda5a328ba4918e8faca68f347462f41903c461122a36b7c51483768f', + callData: '0x000060556e6b6e6f776e205472616e73616374696f6e2054657374', + signatories: [ + testAccounts['Signatory 1 Of Multisig With Unknown Tx'].address, + testAccounts['Signatory 2 Of Multisig With Unknown Tx'].address + ] } } diff --git a/packages/ui/cypress/fixtures/testAccounts.ts b/packages/ui/cypress/fixtures/testAccounts.ts index 57947466..42cda194 100644 --- a/packages/ui/cypress/fixtures/testAccounts.ts +++ b/packages/ui/cypress/fixtures/testAccounts.ts @@ -61,5 +61,19 @@ export const testAccounts = { name: 'Funded Account 3 Chopsticks', type: 'sr25519', mnemonic: 'bottom drive obey lake curtain smoke basket hold race lonely fit walk//chopsticks/3' + } as InjectedAccountWitMnemonic, + 'Signatory 1 Of Multisig With Unknown Tx': { + address: '5C5RWYL7zoV6V2vdwXENSpSzWHXUxyhHBrEzhfySdQmkiF9d', + publicKey: '0x008c37659f858da7ec1416ce01b975af4c6eb5931805047d173d63123174a74e', + name: 'Signatory 1 Of Multisig With Unknown Tx', + type: 'sr25519', + mnemonic: 'various sun sell patch follow stove warfare worry cupboard kick wise wild' + } as InjectedAccountWitMnemonic, + 'Signatory 2 Of Multisig With Unknown Tx': { + address: '5DAA5LQP8C4Cus1caXr3rwDa5LuPCJmXeoj3HrCUWEpGUV7g', + publicKey: '0x3064b82d59077c4d2d7b924b72e63fb9b829b5cd0706b9236c35b79ffa89995b', + name: 'Signatory 2 Of Multisig With Unknown Tx', + type: 'sr25519', + mnemonic: 'canyon narrow primary zoo purpose double rice faculty critic embark trophy economy' } as InjectedAccountWitMnemonic } diff --git a/packages/ui/cypress/support/page-objects/modals/txSigningModal.ts b/packages/ui/cypress/support/page-objects/modals/txSigningModal.ts new file mode 100644 index 00000000..0ea4986f --- /dev/null +++ b/packages/ui/cypress/support/page-objects/modals/txSigningModal.ts @@ -0,0 +1,8 @@ +export const txSigningModal = { + body: () => cy.get('[data-cy=modal-tx-signing]'), + callHashLabel: () => cy.get('[data-cy=label-call-hash]'), + approveButton: () => cy.get('[data-cy=button-approve-tx]'), + rejectButton: () => cy.get('[data-cy=button-reject-tx]'), + callDataInput: () => cy.get('[data-cy=input-call-data]'), + callInfoContainer: () => cy.get('[data-cy=container-call-info]') +} diff --git a/packages/ui/cypress/support/page-objects/multisigPage.ts b/packages/ui/cypress/support/page-objects/multisigPage.ts index a4c6f42b..13e2d9ff 100644 --- a/packages/ui/cypress/support/page-objects/multisigPage.ts +++ b/packages/ui/cypress/support/page-objects/multisigPage.ts @@ -6,8 +6,6 @@ export const multisigPage = { optionsMenuButton: () => cy.get('[data-cy=button-options-menu]'), editNamesMenuOption: () => cy.get('[data-cy=menu-option-edit-names]'), subscanMenuOption: () => cy.get('[data-cy=menu-option-subscan]'), - transactionList: () => cy.get('[data-cy=container-transaction-list]', { timeout: 20000 }), - pendingTransactionItem: () => cy.get('[data-cy=container-pending-tx-item]'), reviewButton: () => cy.get('[data-cy=button-review-tx]'), setIdentityMenuOption: () => cy.get('[data-cy=menu-option-set-identity]'), @@ -17,5 +15,12 @@ export const multisigPage = { thresholdListItem: () => cy.get('[data-cy=list-item-threshold]'), balanceListItem: () => cy.get('[data-cy=list-item-balance]'), signatoriesAccordion: () => cy.get('[data-cy=accordion-signatories]'), - expandSignatoriesIcon: () => cy.get('[data-cy=icon-expand-signatories-summary]') + expandSignatoriesIcon: () => cy.get('[data-cy=icon-expand-signatories-summary]'), + + // transaction list elements + transactionList: () => cy.get('[data-cy=container-transaction-list]', { timeout: 20000 }), + pendingTransactionItem: () => cy.get('[data-cy=container-pending-tx-item]'), + pendingTransactionCallName: () => cy.get('[data-cy=label-call-name]'), + unknownCallIcon: () => cy.get('[data-cy=icon-unknown-call]'), + unknownCallAlert: () => cy.get('[data-cy=alert-no-call-data]') } diff --git a/packages/ui/cypress/tests/unknown-transaction.cy.ts b/packages/ui/cypress/tests/unknown-transaction.cy.ts new file mode 100644 index 00000000..948bac8f --- /dev/null +++ b/packages/ui/cypress/tests/unknown-transaction.cy.ts @@ -0,0 +1,59 @@ +import { testAccounts } from '../fixtures/testAccounts' +import { landingPageUrl } from '../fixtures/landingData' +import { multisigPage } from '../support/page-objects/multisigPage' +import { txSigningModal } from '../support/page-objects/modals/txSigningModal' +import { knownMultisigs } from '../fixtures/knownMultisigs' + +describe('Unknown Transaction', () => { + beforeEach(() => { + cy.setupAndVisit({ + url: landingPageUrl, + extensionConnectionAllowed: true, + injectExtensionWithAccounts: [testAccounts['Signatory 2 Of Multisig With Unknown Tx']] + }) + }) + + it('can see an unknown transaction displayed in the transaction list', () => { + multisigPage + .transactionList() + .should('be.visible') + .within(() => { + multisigPage.pendingTransactionItem().should('have.length', 1) + multisigPage.pendingTransactionItem().within(() => { + multisigPage.pendingTransactionCallName().should('contain.text', 'Unknown call') + multisigPage.unknownCallIcon().should('be.visible') + multisigPage.unknownCallAlert().should('be.visible') + }) + }) + }) + + it('can see the expected state of an unknown tx without call data', () => { + const { hashOfUknownCall: expectedCallHash, callData } = + knownMultisigs['multisig-with-unknown-transaction'] + + multisigPage + .transactionList() + .should('be.visible') + .within(() => { + multisigPage.pendingTransactionItem().within(() => { + multisigPage.reviewButton().click() + }) + }) + txSigningModal + .body() + .should('be.visible') + .within(() => { + txSigningModal.callHashLabel().should('contain.text', expectedCallHash) + txSigningModal.approveButton().should('not.be.enabled') + txSigningModal.rejectButton().should('not.exist') + // now provide call data and ensure we see the call info and approve button enabled + txSigningModal.callDataInput().type(callData) + txSigningModal + .callInfoContainer() + .should('be.visible') + .should('contain.text', 'system.remark') + .should('contain.text', 'remark: Unknown Transaction Test') + txSigningModal.approveButton().should('be.enabled') + }) + }) +}) diff --git a/packages/ui/src/components/CallInfo.tsx b/packages/ui/src/components/CallInfo.tsx index eb3c8aa0..2cfa7047 100644 --- a/packages/ui/src/components/CallInfo.tsx +++ b/packages/ui/src/components/CallInfo.tsx @@ -200,8 +200,14 @@ const CallInfo = ({ const hasArgs = useMemo(() => args && Object.keys(args).length > 0, [args]) return ( -
- +
+ {name} {!!aggregatedData.callData && withLink && ( No Call data found on-chain. Use Multix to initiate multisig transactions and avoid this annoyance. diff --git a/packages/ui/src/components/Transactions/Transaction.tsx b/packages/ui/src/components/Transactions/Transaction.tsx index e6f16f8a..d4424560 100644 --- a/packages/ui/src/components/Transactions/Transaction.tsx +++ b/packages/ui/src/components/Transactions/Transaction.tsx @@ -58,6 +58,7 @@ const Transaction = ({ ) : ( Transaction signing @@ -298,7 +299,12 @@ const ProposalSigning = ({ > Call hash
- {proposalData.hash} + + {proposalData.hash} + {!isProposerSelected && !proposalData.callData && ( @@ -319,6 +325,7 @@ const ProposalSigning = ({ onChange={onAddedCallDataChange} value={addedCallData || ''} fullWidth + data-cy="input-call-data" /> onSign(false)} disabled={isSubmitting} + data-cy="button-reject-tx" > Reject @@ -392,6 +400,7 @@ const ProposalSigning = ({ variant="primary" onClick={() => onSign(true)} disabled={!!errorMessage || isSubmitting || (mustSubmitCallData && !callInfo?.call)} + data-cy="button-approve-tx" > Approve