11import { describe , it , expect , assert } from "vitest" ;
22import { RunNodeState , runNode } from "../../lib/node" ;
33import * as eth from "../../lib/ethViem" ;
4- import contractsFactory from "../../lib/abis/deposit" ;
4+ import deposit from "../../lib/abis/deposit" ;
55import "../../lib/expect" ;
66import { beforeEachWithCleanup } from "../../lib/lifecycle" ;
77
@@ -18,12 +18,13 @@ describe("contract account's nonce", () => {
1818 devClients = eth . devClientsFromNodeWebSocket ( node , cleanup . push ) ;
1919 } , 60 * 1000 ) ;
2020
21+ // See also: https://github.com/humanode-network/humanode/issues/1402
2122 it ( "is being preserved after zeroing the balance" , async ( ) => {
2223 const [ alice , _ ] = devClients ;
2324
2425 const deployContractTxHash = await alice . deployContract ( {
25- abi : contractsFactory . abi ,
26- bytecode : contractsFactory . bytecode ,
26+ abi : deposit . abi ,
27+ bytecode : deposit . bytecode ,
2728 value : 1n , // Even the smallest deposit is enough
2829 gas : 150_274n ,
2930 } ) ;
@@ -33,20 +34,38 @@ describe("contract account's nonce", () => {
3334 timeout : 18_000 ,
3435 } ) ;
3536 expect ( deployContractTxReceipt . status ) . toBe ( "success" ) ;
36- const factoryAddress = deployContractTxReceipt . contractAddress ;
37- assert ( factoryAddress ) ;
37+ const contract = deployContractTxReceipt . contractAddress ;
38+ assert ( contract ) ;
3839
39- // If there's a bug in the EVM, it will clear the contract state after `withdrawAll`.
40+ // The nonce of the contract account immediately after creation.
41+ const INITIAL_NONCE = 1 ;
42+
43+ // EIP-161 https://eips.ethereum.org/EIPS/eip-161:
44+ //
45+ // > At the end of the transaction, any account touched by ... that transaction which is now *empty*
46+ // > SHALL instead become non-existent (i.e. deleted).
47+ // > Where: ...
48+ // > An account is considered *empty* when it has no code and zero nonce and zero balance.
49+ //
50+ // So once the balance is zeroed below, the account state shouldn't be deleted because
51+ // the account contains the contract code.
4052 const withdrawalTx = await alice . writeContract ( {
41- address : factoryAddress ,
42- abi : contractsFactory . abi ,
53+ address : contract ,
54+ abi : deposit . abi ,
4355 functionName : "withdrawAll" ,
4456 gas : 30_585n ,
4557 } ) ;
4658 const withdrawalReceipt = await publicClient . waitForTransactionReceipt ( {
4759 hash : withdrawalTx ,
4860 timeout : 18_000 ,
4961 } ) ;
50- expect ( withdrawalReceipt . status ) . toBe ( "success" ) ;
62+ expect ( withdrawalReceipt . status , "status of withdrawal" ) . toBe ( "success" ) ;
63+
64+ // Ethereum RPC returns the account nonce in the `eth_getTransactionCount` RPC call.
65+ // https://github.com/humanode-network/frontier/blob/1afab28e8d5aebe7d44f9043b3ba19e9555123dc/client/rpc/src/eth/state.rs#L116-L169
66+ const nonce = await publicClient . getTransactionCount ( {
67+ address : contract ,
68+ } ) ;
69+ expect ( nonce ) . toBe ( INITIAL_NONCE ) ;
5170 } ) ;
5271} ) ;
0 commit comments