@@ -1785,6 +1785,138 @@ func (s *IbcEurekaSolanaIFTTestSuite) Test_IFT_TwoStepAdminTransfer() {
17851785 s .Require ().Equal (thirdAdminWallet .PublicKey (), state .Admin , "Admin should be the third admin" )
17861786 s .Require ().Nil (state .PendingAdmin , "Pending admin should be cleared" )
17871787 }))
1788+
1789+ // Edge case: unauthorized propose (non-admin tries to propose)
1790+ s .Require ().True (s .Run ("Unauthorized propose is rejected" , func () {
1791+ unauthorizedWallet , err := s .Solana .Chain .CreateAndFundWallet ()
1792+ s .Require ().NoError (err )
1793+
1794+ proposeIx , err := ift .NewProposeAdminInstruction (
1795+ unauthorizedWallet .PublicKey (),
1796+ s .IFTAppState ,
1797+ unauthorizedWallet .PublicKey (), // not the admin
1798+ solanago .SysVarInstructionsPubkey ,
1799+ )
1800+ s .Require ().NoError (err )
1801+
1802+ tx , err := s .Solana .Chain .NewTransactionFromInstructions (unauthorizedWallet .PublicKey (), proposeIx )
1803+ s .Require ().NoError (err )
1804+
1805+ _ , err = s .Solana .Chain .SignAndBroadcastTxWithRetry (ctx , tx , rpc .CommitmentConfirmed , unauthorizedWallet )
1806+ s .Require ().Error (err , "Non-admin should not be able to propose" )
1807+ }))
1808+
1809+ // Edge case: cancel with no pending proposal
1810+ s .Require ().True (s .Run ("Cancel with no pending proposal is rejected" , func () {
1811+ cancelIx , err := ift .NewCancelAdminProposalInstruction (
1812+ s .IFTAppState ,
1813+ thirdAdminWallet .PublicKey (), // current admin
1814+ solanago .SysVarInstructionsPubkey ,
1815+ )
1816+ s .Require ().NoError (err )
1817+
1818+ tx , err := s .Solana .Chain .NewTransactionFromInstructions (thirdAdminWallet .PublicKey (), cancelIx )
1819+ s .Require ().NoError (err )
1820+
1821+ _ , err = s .Solana .Chain .SignAndBroadcastTxWithRetry (ctx , tx , rpc .CommitmentConfirmed , thirdAdminWallet )
1822+ s .Require ().Error (err , "Cancel should fail when no pending proposal exists" )
1823+ }))
1824+
1825+ // Set up a proposal so we can test more edge cases
1826+ var fourthAdminWallet * solanago.Wallet
1827+ s .Require ().True (s .Run ("Propose fourth admin for edge case tests" , func () {
1828+ var err error
1829+ fourthAdminWallet , err = s .Solana .Chain .CreateAndFundWallet ()
1830+ s .Require ().NoError (err )
1831+
1832+ proposeIx , err := ift .NewProposeAdminInstruction (
1833+ fourthAdminWallet .PublicKey (),
1834+ s .IFTAppState ,
1835+ thirdAdminWallet .PublicKey (),
1836+ solanago .SysVarInstructionsPubkey ,
1837+ )
1838+ s .Require ().NoError (err )
1839+
1840+ tx , err := s .Solana .Chain .NewTransactionFromInstructions (thirdAdminWallet .PublicKey (), proposeIx )
1841+ s .Require ().NoError (err )
1842+
1843+ _ , err = s .Solana .Chain .SignAndBroadcastTxWithRetry (ctx , tx , rpc .CommitmentConfirmed , thirdAdminWallet )
1844+ s .Require ().NoError (err )
1845+ }))
1846+
1847+ // Edge case: unauthorized accept (non-pending wallet tries to accept)
1848+ s .Require ().True (s .Run ("Unauthorized accept is rejected" , func () {
1849+ acceptIx , err := ift .NewAcceptAdminInstruction (
1850+ s .IFTAppState ,
1851+ thirdAdminWallet .PublicKey (), // current admin, not the pending admin
1852+ solanago .SysVarInstructionsPubkey ,
1853+ )
1854+ s .Require ().NoError (err )
1855+
1856+ tx , err := s .Solana .Chain .NewTransactionFromInstructions (thirdAdminWallet .PublicKey (), acceptIx )
1857+ s .Require ().NoError (err )
1858+
1859+ _ , err = s .Solana .Chain .SignAndBroadcastTxWithRetry (ctx , tx , rpc .CommitmentConfirmed , thirdAdminWallet )
1860+ s .Require ().Error (err , "Non-pending admin should not be able to accept" )
1861+ }))
1862+
1863+ // Edge case: propose while pending already exists
1864+ s .Require ().True (s .Run ("Propose while pending exists is rejected" , func () {
1865+ anotherWallet , err := s .Solana .Chain .CreateAndFundWallet ()
1866+ s .Require ().NoError (err )
1867+
1868+ proposeIx , err := ift .NewProposeAdminInstruction (
1869+ anotherWallet .PublicKey (),
1870+ s .IFTAppState ,
1871+ thirdAdminWallet .PublicKey (), // current admin
1872+ solanago .SysVarInstructionsPubkey ,
1873+ )
1874+ s .Require ().NoError (err )
1875+
1876+ tx , err := s .Solana .Chain .NewTransactionFromInstructions (thirdAdminWallet .PublicKey (), proposeIx )
1877+ s .Require ().NoError (err )
1878+
1879+ _ , err = s .Solana .Chain .SignAndBroadcastTxWithRetry (ctx , tx , rpc .CommitmentConfirmed , thirdAdminWallet )
1880+ s .Require ().Error (err , "Propose should fail when a pending proposal already exists" )
1881+ }))
1882+
1883+ // Edge case: cancel then stale accept
1884+ s .Require ().True (s .Run ("Cancel the proposal" , func () {
1885+ cancelIx , err := ift .NewCancelAdminProposalInstruction (
1886+ s .IFTAppState ,
1887+ thirdAdminWallet .PublicKey (),
1888+ solanago .SysVarInstructionsPubkey ,
1889+ )
1890+ s .Require ().NoError (err )
1891+
1892+ tx , err := s .Solana .Chain .NewTransactionFromInstructions (thirdAdminWallet .PublicKey (), cancelIx )
1893+ s .Require ().NoError (err )
1894+
1895+ _ , err = s .Solana .Chain .SignAndBroadcastTxWithRetry (ctx , tx , rpc .CommitmentConfirmed , thirdAdminWallet )
1896+ s .Require ().NoError (err )
1897+ }))
1898+
1899+ s .Require ().True (s .Run ("Stale accept after cancel is rejected" , func () {
1900+ acceptIx , err := ift .NewAcceptAdminInstruction (
1901+ s .IFTAppState ,
1902+ fourthAdminWallet .PublicKey (), // was the pending admin before cancel
1903+ solanago .SysVarInstructionsPubkey ,
1904+ )
1905+ s .Require ().NoError (err )
1906+
1907+ tx , err := s .Solana .Chain .NewTransactionFromInstructions (fourthAdminWallet .PublicKey (), acceptIx )
1908+ s .Require ().NoError (err )
1909+
1910+ _ , err = s .Solana .Chain .SignAndBroadcastTxWithRetry (ctx , tx , rpc .CommitmentConfirmed , fourthAdminWallet )
1911+ s .Require ().Error (err , "Previously-pending admin should not be able to accept after cancel" )
1912+ }))
1913+
1914+ // Verify state unchanged after all negative tests
1915+ s .Require ().True (s .Run ("Verify admin unchanged after edge case tests" , func () {
1916+ state := readAppState ()
1917+ s .Require ().Equal (thirdAdminWallet .PublicKey (), state .Admin , "Admin should still be the third admin" )
1918+ s .Require ().Nil (state .PendingAdmin , "Pending admin should be nil" )
1919+ }))
17881920}
17891921
17901922// Test_IFT_ExistingToken_InvalidPendingTransfer tests that ift_transfer rejects a wrong pending_transfer PDA
0 commit comments