Skip to content

Commit

Permalink
Merge branch 'tbaut-values-callinfo-generic' of github.com:ChainSafe/…
Browse files Browse the repository at this point in the history
…Multix into tbaut-values-callinfo-generic
  • Loading branch information
Tbaut committed Oct 6, 2023
2 parents 563bbda + da3c510 commit 7ed6e70
Show file tree
Hide file tree
Showing 8 changed files with 210 additions and 213 deletions.
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
"prettier": "^3.0.3"
},
"resolutions": {
"graphql": "^16.0.0"
"graphql": "^16.0.0",
"@polkadot/util-crypto": "12.5.1",
"@polkadot/util": "12.5.1"
}
}
9 changes: 9 additions & 0 deletions packages/ui/cypress/fixtures/knownMultisigs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { injectedAccounts } from './injectedAccounts'

export const knownMultisigs = {
'test-multisig-1': {
address: '5CmwqwwLEkEtsmB9gFaTJdCfurz33xyggHnvwHaGKtvmQNxq',
threshold: 2,
signatories: [injectedAccounts[0].address, injectedAccounts[1].address]
}
}
21 changes: 21 additions & 0 deletions packages/ui/cypress/support/commands.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/// <reference types="cypress" />

import { AuthRequests, Extension, TxRequests } from './Extension'
import { MultisigInfo, rejectCurrentMultisigTxs } from '../utils/rejectCurrentMultisigTxs'
import { InjectedAccountWitMnemonic } from '../fixtures/injectedAccounts'

// ***********************************************
Expand Down Expand Up @@ -77,6 +78,8 @@ Cypress.Commands.add('rejectTx', (id: number, reason: string) => {
return extension.rejectTx(id, reason)
})

Cypress.Commands.add('rejectCurrentMultisigTx', rejectCurrentMultisigTxs)

declare global {
namespace Cypress {
interface Chainable {
Expand All @@ -86,43 +89,61 @@ declare global {
* @example cy.initExtension([{ address: '7NPoMQbiA6trJKkjB35uk96MeJD4PGWkLQLH7k7hXEkZpiba', name: 'Alice', type: 'sr25519'}])
*/
initExtension: (accounts: InjectedAccountWitMnemonic[]) => Chainable<AUTWindow>

/**
* Read the authentication request queue.
* @example cy.getAuthRequests().then((authQueue) => { cy.wrap(Object.values(authQueue).length).should("eq", 1) })
*/
getAuthRequests: () => Chainable<AuthRequests>

/**
* Authorize a specific request
* @param {number} id - the id of the request to authorize. This id is part of the getAuthRequests object response.
* @param {string[]} accountAddresses - the account addresses to share with the applications. These addresses must be part of the ones shared in the `initExtension`
* @example cy.enableAuth(1694443839903, ["7NPoMQbiA6trJKkjB35uk96MeJD4PGWkLQLH7k7hXEkZpiba"])
*/
enableAuth: (id: number, accountAddresses: string[]) => void

/**
* Reject a specific request
* @param {number} id - the id of the request to reject. This id is part of the getAuthRequests object response.
* @param {reason} reason - the reason for the rejection
* @example cy.rejectAuth(1694443839903, "Cancelled")
*/
rejectAuth: (id: number, reason: string) => void

/**
* Read the tx request queue.
* @example cy.getTxRequests().then((txQueue) => { cy.wrap(Object.values(txQueue).length).should("eq", 1) })
*/
getTxRequests: () => Chainable<TxRequests>

/**
* Authorize a specific request
* @param {number} id - the id of the request to approve. This id is part of the getTxRequests object response.
* @example cy.approveTx(1694443839903)
*/
approveTx: (id: number) => void

/**
* Reject a specific request
* @param {number} id - the id of the tx request to reject. This id is part of the getTxRequests object response.
* @param {reason} reason - the reason for the rejection
* @example cy.rejectAuth(1694443839903, "Cancelled")
*/
rejectTx: (id: number, reason: string) => void

/**
* Reject all pending multisig requests with a specific account
* @param {InjectedAccountWitMnemonic} opt.account - The account to reject pending transactions with. It should be the proposer
* @param {multisigInfo} opt.multisigInfo - The information about the multisig to remove pending transactions from
* @param {WSendpoint} opt.WSendpoint - The RPC endpoint to connect to to submit the rejection batch transaction
*/
rejectCurrentMultisigTx: (opt: {
account: InjectedAccountWitMnemonic
multisigInfo: MultisigInfo
WSendpoint: string
}) => void
}
}
}
14 changes: 13 additions & 1 deletion packages/ui/cypress/tests/transactions.cy.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { injectedAccounts } from '../fixtures/injectedAccounts'
import { knownMultisigs } from '../fixtures/knownMultisigs'
import { landingPageUrl } from '../fixtures/landingData'
import { landingPage } from '../support/page-objects/landingPage'
import { multisigPage } from '../support/page-objects/multisigPage'
Expand Down Expand Up @@ -51,7 +52,18 @@ describe('Perform transactions', () => {
})
})

it.skip('Makes a balance transfer with Alice', () => {
it('Makes a balance transfer with Alice', () => {
cy.rejectCurrentMultisigTx({
account: injectedAccounts[0],
multisigInfo: {
address: knownMultisigs['test-multisig-1'].address,
threshold: knownMultisigs['test-multisig-1'].threshold,
otherSignatories: knownMultisigs['test-multisig-1'].signatories.filter(
(address) => address !== injectedAccounts[0].address
)
},
WSendpoint: 'wss://rococo-rpc.polkadot.io'
})
multisigPage.newTransactionButton().click()
sendTxModal.sendTxTitle().should('be.visible')
fillAndSubmitTransactionForm()
Expand Down
135 changes: 135 additions & 0 deletions packages/ui/cypress/utils/rejectCurrentMultisigTxs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import { cryptoWaitReady } from '@polkadot/util-crypto'
import { InjectedAccountWitMnemonic } from '../fixtures/injectedAccounts'
import { Keyring, WsProvider, ApiPromise } from '@polkadot/api'
import { MultisigStorageInfo } from '../../src/types'
import { PendingTx } from '../../src/hooks/usePendingTx'
import { SubmittableExtrinsic } from '@polkadot/api/types'
import { ISubmittableResult, AnyTuple, Codec } from '@polkadot/types/types'
import { StorageKey } from '@polkadot/types'

export interface MultisigInfo {
address: string
otherSignatories: string[]
threshold: number
}
const callBack =
(resolve: (thenableOrResult?: unknown) => void) =>
({ status, txHash }: ISubmittableResult) => {
console.log('Transaction status:', status.type)
if (status.isBroadcast) {
console.log('Broadcasted', txHash.toHex())
}

if (status.isInBlock) {
console.log('In block')
}

if (status.isFinalized) {
console.log('Finalized block hash', status.asFinalized.toHex())
resolve()
}
}

const getPendingMultisixTx = (
multisigTxs: [StorageKey<AnyTuple>, Codec][],
multisigInfo: MultisigInfo
) => {
const curratedMultisigTxs: PendingTx[] = []

multisigTxs.forEach((storage) => {
// this is supposed to be the multisig address that we asked the storage for
const multisigFromChain = (storage[0].toHuman() as Array<string>)[0]
const hash = (storage[0].toHuman() as Array<string>)[1]
const info = storage[1].toJSON() as unknown as MultisigStorageInfo

// Fix for ghost proposals for https://github.com/polkadot-js/apps/issues/9103
// These 2 should be the same
if (multisigFromChain.toLowerCase() !== multisigInfo.address.toLowerCase()) {
console.error(
'The onchain call and the one found in the block donot correspond',
multisigFromChain,
multisigInfo.address
)
return
}

curratedMultisigTxs.push({
hash,
info,
from: multisigInfo.address
})
})

return curratedMultisigTxs
}

const getRejectionsTxs = (
pendingMultisigTxs: PendingTx[],
account: InjectedAccountWitMnemonic,
multisigInfo: MultisigInfo,
api: ApiPromise
) => {
const allTxs: SubmittableExtrinsic<'promise', ISubmittableResult>[] = []
pendingMultisigTxs.forEach((pendingMultisigTx) => {
const depositor = pendingMultisigTx.info.depositor
if (depositor !== account.address) {
console.log('multisig tx not proposed by the same account', depositor, account.address)
return
}

const rejectCurrent = api.tx.multisig.cancelAsMulti(
multisigInfo.threshold,
multisigInfo.otherSignatories,
pendingMultisigTx.info.when,
pendingMultisigTx.hash
)
allTxs.push(rejectCurrent)
})

return allTxs
}

export const rejectCurrentMultisigTxs = ({
account,
multisigInfo,
WSendpoint
}: {
account: InjectedAccountWitMnemonic
multisigInfo: MultisigInfo
WSendpoint: string
}) => {
// this function takes some time waiting for a finalized block
// with the removal of all pending tx. We set a max timout of 30s
return cy.then(
{ timeout: 30000 },
() =>
new Cypress.Promise(async (resolve) => {
await cryptoWaitReady()

const keyring = new Keyring({ type: 'sr25519' })
keyring.addFromMnemonic(account.mnemonic)

const wsProvider = new WsProvider(WSendpoint)
const api = await ApiPromise.create({ provider: wsProvider })

const multisigTxs = await api.query.multisig.multisigs.entries(multisigInfo.address)
const pendingMultisigTxs = getPendingMultisixTx(multisigTxs, multisigInfo)

if (!pendingMultisigTxs.length) {
console.log('no pending multisig tx for', multisigInfo.address)
resolve()
return
}

console.log('pendingMultisigTxs', pendingMultisigTxs)

const allTxs = getRejectionsTxs(pendingMultisigTxs, account, multisigInfo, api)

console.log(`The multisig has ${allTxs.length} pending txs. Rejecting them now`)

api.tx.utility
.batchAll(allTxs)
.signAndSend(keyring.getPair(account.address), callBack(resolve))
})
)
}
2 changes: 1 addition & 1 deletion packages/ui/src/hooks/useGetEncodedAddress.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export const useGetEncodedAddress = () => {

const getEncodedAddress = useCallback(
(address: string | Uint8Array | undefined) => {
if (!chainInfo || !address) {
if (!chainInfo || !address || address === 'undefined') {
return
}

Expand Down
2 changes: 1 addition & 1 deletion packages/ui/src/hooks/usePendingTx.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export const usePendingTx = (multiProxy?: MultiProxy) => {
// These 2 should be the same
if (multisigFromChain.toLowerCase() !== multisigs[index].toLowerCase()) {
console.error(
'The onchain call and the one found in the block donot correstpond',
'The onchain call and the one found in the block donot correspond',
multisigFromChain,
multisigs[index]
)
Expand Down
Loading

0 comments on commit 7ed6e70

Please sign in to comment.