@@ -1658,6 +1658,126 @@ async function wrongTimepointTest<
16581658 assert ( client . api . errors . multisig . WrongTimepoint . is ( dispatchError2 . asModule ) )
16591659}
16601660
1661+ /**
1662+ * Test that multisig cancellation with threshold < 2 fails.
1663+ *
1664+ * 1. Alice attempts to cancel a multisig with threshold = 1
1665+ * 2. Verify that the transaction fails with the appropriate error
1666+ */
1667+ async function minimumThresholdCancelTest <
1668+ TCustom extends Record < string , unknown > | undefined ,
1669+ TInitStorages extends Record < string , Record < string , any > > | undefined ,
1670+ > ( chain : Chain < TCustom , TInitStorages > ) {
1671+ const [ client ] = await setupNetworks ( chain )
1672+
1673+ const alice = defaultAccountsSr25519 . alice
1674+ const bob = defaultAccountsSr25519 . bob
1675+ const dave = defaultAccountsSr25519 . dave
1676+
1677+ // Fund test accounts
1678+ await client . dev . setStorage ( {
1679+ System : {
1680+ account : [ [ [ bob . address ] , { providers : 1 , data : { free : 1000e10 } } ] ] ,
1681+ } ,
1682+ } )
1683+
1684+ // Create a simple call to transfer funds to Dave
1685+ const transferAmount = 100e10
1686+ const transferCall = client . api . tx . balances . transferKeepAlive ( dave . address , transferAmount )
1687+
1688+ // Alice attempts to cancel a multisig with threshold = 1 (invalid)
1689+ const threshold = 1 // Invalid threshold - should be >= 2
1690+ const otherSignatories = [ bob . address ]
1691+ const callHash = transferCall . method . hash
1692+
1693+ const cancelTx = client . api . tx . multisig . cancelAsMulti (
1694+ threshold ,
1695+ otherSignatories ,
1696+ {
1697+ height : 1 ,
1698+ index : 0 ,
1699+ } ,
1700+ callHash ,
1701+ )
1702+
1703+ await sendTransaction ( cancelTx . signAsync ( alice ) )
1704+
1705+ await client . dev . newBlock ( )
1706+
1707+ // Check for ExtrinsicFailed event
1708+ const events = await client . api . query . system . events ( )
1709+
1710+ const [ ev ] = events . filter ( ( record ) => {
1711+ const { event } = record
1712+ return event . section === 'system' && event . method === 'ExtrinsicFailed'
1713+ } )
1714+
1715+ assert ( client . api . events . system . ExtrinsicFailed . is ( ev . event ) )
1716+ const dispatchError = ev . event . data . dispatchError
1717+
1718+ assert ( dispatchError . isModule )
1719+ assert ( client . api . errors . multisig . MinimumThreshold . is ( dispatchError . asModule ) )
1720+ }
1721+
1722+ /**
1723+ * Test that as_multi with threshold < 2 fails.
1724+ *
1725+ * 1. Alice attempts to create a multisig with threshold = 1 using as_multi
1726+ * 2. Verify that the transaction fails with the appropriate error
1727+ */
1728+ async function minimumThresholdAsMultiTest <
1729+ TCustom extends Record < string , unknown > | undefined ,
1730+ TInitStorages extends Record < string , Record < string , any > > | undefined ,
1731+ > ( chain : Chain < TCustom , TInitStorages > ) {
1732+ const [ client ] = await setupNetworks ( chain )
1733+
1734+ const alice = defaultAccountsSr25519 . alice
1735+ const bob = defaultAccountsSr25519 . bob
1736+ const dave = defaultAccountsSr25519 . dave
1737+
1738+ // Fund test accounts
1739+ await client . dev . setStorage ( {
1740+ System : {
1741+ account : [ [ [ bob . address ] , { providers : 1 , data : { free : 1000e10 } } ] ] ,
1742+ } ,
1743+ } )
1744+
1745+ // Create a simple call to transfer funds to Dave
1746+ const transferAmount = 100e10
1747+ const transferCall = client . api . tx . balances . transferKeepAlive ( dave . address , transferAmount )
1748+
1749+ // Alice attempts to create a multisig with threshold = 1 (invalid)
1750+ const threshold = 1 // Invalid threshold - should be >= 2
1751+ const otherSignatories = [ bob . address ]
1752+ const maxWeight = { refTime : 1000000000 , proofSize : 1000000 }
1753+
1754+ const asMultiTx = client . api . tx . multisig . asMulti (
1755+ threshold ,
1756+ otherSignatories ,
1757+ null , // No timepoint for first approval
1758+ transferCall . method . toHex ( ) ,
1759+ maxWeight ,
1760+ )
1761+
1762+ await sendTransaction ( asMultiTx . signAsync ( alice ) )
1763+
1764+ await client . dev . newBlock ( )
1765+
1766+ // Check for ExtrinsicFailed event
1767+ const events = await client . api . query . system . events ( )
1768+
1769+ const [ ev ] = events . filter ( ( record ) => {
1770+ const { event } = record
1771+ return event . section === 'system' && event . method === 'ExtrinsicFailed'
1772+ } )
1773+
1774+ assert ( client . api . events . system . ExtrinsicFailed . is ( ev . event ) )
1775+ const dispatchError = ev . event . data . dispatchError
1776+
1777+ assert ( dispatchError . isModule )
1778+ assert ( client . api . errors . multisig . MinimumThreshold . is ( dispatchError . asModule ) )
1779+ }
1780+
16611781export function multisigE2ETests <
16621782 TCustom extends Record < string , unknown > | undefined ,
16631783 TInitStorages extends Record < string , Record < string , any > > | undefined ,
@@ -1726,5 +1846,13 @@ export function multisigE2ETests<
17261846 test ( 'approval with wrong timepoint fails' , async ( ) => {
17271847 await wrongTimepointTest ( chain )
17281848 } )
1849+
1850+ test ( 'multisig cancellation with threshold < 2 fails' , async ( ) => {
1851+ await minimumThresholdCancelTest ( chain )
1852+ } )
1853+
1854+ test ( 'creating a multisig with threshold < 2 fails' , async ( ) => {
1855+ await minimumThresholdAsMultiTest ( chain )
1856+ } )
17291857 } )
17301858}
0 commit comments