or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

accounts.mdblock-config.mdindex.mdmining.mdsnapshots.mdstorage.mdtime.mdtransactions.md

transactions.mddocs/

0

# Transaction Management

1

2

Control transaction mempool state for testing transaction lifecycle scenarios, including transaction dropping and mempool manipulation.

3

4

## Capabilities

5

6

### Drop Transaction

7

8

Removes a transaction from the mempool, useful for testing transaction failure scenarios and mempool behavior.

9

10

```typescript { .api }

11

/**

12

* Removes the given transaction from the mempool, if it exists

13

* @param txHash Transaction hash of the transaction to be removed from the mempool

14

* @returns Promise resolving to true if transaction was successfully removed, false if not found

15

*/

16

dropTransaction(txHash: string): Promise<boolean>;

17

```

18

19

**Usage Examples:**

20

21

```typescript

22

import { network } from "hardhat";

23

24

const { networkHelpers } = await network.connect();

25

const [signer] = await ethers.getSigners();

26

27

// Send a transaction but don't wait for it to be mined

28

const tx = await signer.sendTransaction({

29

to: "0x123...",

30

value: ethers.parseEther("1"),

31

gasLimit: 21000

32

});

33

34

console.log(`Transaction hash: ${tx.hash}`);

35

36

// Remove transaction from mempool before it gets mined

37

const success = await networkHelpers.dropTransaction(tx.hash);

38

console.log(`Transaction dropped: ${success}`);

39

40

// Try to drop non-existent transaction

41

const notFound = await networkHelpers.dropTransaction("0xnonexistent123...");

42

console.log(`Non-existent transaction dropped: ${notFound}`); // false

43

```

44

45

## Transaction Testing Patterns

46

47

### Testing Transaction Failures

48

49

```typescript

50

it("should handle dropped transactions", async () => {

51

const { networkHelpers } = await network.connect();

52

const [signer] = await ethers.getSigners();

53

54

// Send transaction with low gas price (might not get mined quickly)

55

const tx = await signer.sendTransaction({

56

to: "0x123...",

57

value: ethers.parseEther("1"),

58

gasPrice: 1 // Very low gas price

59

});

60

61

// Drop the transaction from mempool

62

const dropped = await networkHelpers.dropTransaction(tx.hash);

63

expect(dropped).to.be.true;

64

65

// Transaction should not be found in mempool anymore

66

const txFromMempool = await ethers.provider.getTransaction(tx.hash);

67

expect(txFromMempool).to.be.null;

68

69

// Mining blocks shouldn't include the dropped transaction

70

await networkHelpers.mine(5);

71

72

const receipt = await ethers.provider.getTransactionReceipt(tx.hash);

73

expect(receipt).to.be.null;

74

});

75

```

76

77

### Testing Mempool Management

78

79

```typescript

80

it("should manage mempool effectively", async () => {

81

const { networkHelpers } = await network.connect();

82

const [signer] = await ethers.getSigners();

83

84

// Send multiple transactions

85

const txHashes: string[] = [];

86

87

for (let i = 0; i < 5; i++) {

88

const tx = await signer.sendTransaction({

89

to: "0x123...",

90

value: ethers.parseEther("0.1"),

91

nonce: await signer.getNonce() + i

92

});

93

txHashes.push(tx.hash);

94

}

95

96

// Drop some transactions selectively

97

await networkHelpers.dropTransaction(txHashes[1]);

98

await networkHelpers.dropTransaction(txHashes[3]);

99

100

// Mine blocks

101

await networkHelpers.mine(2);

102

103

// Check which transactions were mined

104

for (let i = 0; i < txHashes.length; i++) {

105

const receipt = await ethers.provider.getTransactionReceipt(txHashes[i]);

106

107

if (i === 1 || i === 3) {

108

// These were dropped

109

expect(receipt).to.be.null;

110

} else {

111

// These should be mined

112

expect(receipt).to.not.be.null;

113

expect(receipt.status).to.equal(1);

114

}

115

}

116

});

117

```

118

119

### Testing Transaction Replacement

120

121

```typescript

122

it("should handle transaction replacement scenarios", async () => {

123

const { networkHelpers } = await network.connect();

124

const [signer] = await ethers.getSigners();

125

126

const nonce = await signer.getNonce();

127

128

// Send original transaction with low gas price

129

const originalTx = await signer.sendTransaction({

130

to: "0x123...",

131

value: ethers.parseEther("1"),

132

gasPrice: ethers.parseUnits("1", "gwei"),

133

nonce: nonce

134

});

135

136

// Drop the original transaction

137

await networkHelpers.dropTransaction(originalTx.hash);

138

139

// Send replacement transaction with higher gas price (same nonce)

140

const replacementTx = await signer.sendTransaction({

141

to: "0x456...",

142

value: ethers.parseEther("2"),

143

gasPrice: ethers.parseUnits("10", "gwei"),

144

nonce: nonce // Same nonce as original

145

});

146

147

// Mine the replacement

148

await networkHelpers.mine();

149

150

// Original should not be mined

151

const originalReceipt = await ethers.provider.getTransactionReceipt(originalTx.hash);

152

expect(originalReceipt).to.be.null;

153

154

// Replacement should be mined

155

const replacementReceipt = await ethers.provider.getTransactionReceipt(replacementTx.hash);

156

expect(replacementReceipt).to.not.be.null;

157

expect(replacementReceipt.to).to.equal("0x456...");

158

});

159

```

160

161

### Testing Contract Interaction Failures

162

163

```typescript

164

it("should test contract interaction with dropped transactions", async () => {

165

const { networkHelpers } = await network.connect();

166

167

const counter = await ethers.deployContract("Counter");

168

const [signer] = await ethers.getSigners();

169

170

// Send contract interaction transaction

171

const tx = await counter.increment();

172

173

// Drop the transaction before it's mined

174

await networkHelpers.dropTransaction(tx.hash);

175

176

// Mine some blocks

177

await networkHelpers.mine(3);

178

179

// Counter should not have been incremented

180

expect(await counter.count()).to.equal(0);

181

182

// Send another increment transaction (this one won't be dropped)

183

await counter.increment();

184

185

// Now counter should be incremented

186

expect(await counter.count()).to.equal(1);

187

});

188

```

189

190

### Testing Transaction Pool Limits

191

192

```typescript

193

it("should test mempool behavior under stress", async () => {

194

const { networkHelpers } = await network.connect();

195

const [signer] = await ethers.getSigners();

196

197

const txHashes: string[] = [];

198

const nonce = await signer.getNonce();

199

200

// Fill mempool with many transactions

201

for (let i = 0; i < 100; i++) {

202

const tx = await signer.sendTransaction({

203

to: "0x123...",

204

value: ethers.parseEther("0.001"),

205

gasPrice: ethers.parseUnits("1", "gwei"),

206

nonce: nonce + i

207

});

208

txHashes.push(tx.hash);

209

}

210

211

// Drop every other transaction

212

const droppedCount = await Promise.all(

213

txHashes

214

.filter((_, i) => i % 2 === 0)

215

.map(hash => networkHelpers.dropTransaction(hash))

216

);

217

218

const successfulDrops = droppedCount.filter(success => success).length;

219

expect(successfulDrops).to.be.greaterThan(0);

220

221

// Mine all remaining transactions

222

await networkHelpers.mine(10);

223

224

// Verify approximately half the transactions were mined

225

const minedCount = await Promise.all(

226

txHashes.map(async hash => {

227

const receipt = await ethers.provider.getTransactionReceipt(hash);

228

return receipt !== null;

229

})

230

);

231

232

const actualMinedCount = minedCount.filter(mined => mined).length;

233

expect(actualMinedCount).to.be.approximately(50, 10); // Allow some variance

234

});

235

```

236

237

### Testing Time-Sensitive Transactions

238

239

```typescript

240

it("should test time-sensitive transaction dropping", async () => {

241

const { networkHelpers } = await network.connect();

242

243

// Deploy time-sensitive contract (e.g., auction with deadline)

244

const deadline = Math.floor(Date.now() / 1000) + 3600; // 1 hour from now

245

const auction = await ethers.deployContract("TimedAuction", [deadline]);

246

247

const [bidder1, bidder2] = await ethers.getSigners();

248

249

// Place bid near deadline

250

await networkHelpers.time.increaseTo(deadline - 60); // 1 minute before deadline

251

252

const bidTx = await auction.connect(bidder1).bid({ value: ethers.parseEther("1") });

253

254

// Drop the bid transaction

255

await networkHelpers.dropTransaction(bidTx.hash);

256

257

// Advance past deadline

258

await networkHelpers.time.increaseTo(deadline + 1);

259

260

// Mine blocks

261

await networkHelpers.mine();

262

263

// Verify auction ended without the dropped bid

264

expect(await auction.ended()).to.be.true;

265

expect(await auction.highestBidder()).to.equal(ethers.ZeroAddress);

266

267

// Late bid should fail (auction ended)

268

await expect(

269

auction.connect(bidder2).bid({ value: ethers.parseEther("2") })

270

).to.be.revertedWith("Auction ended");

271

});

272

```

273

274

## Transaction Lifecycle Testing

275

276

### Complete Transaction Lifecycle

277

278

```typescript

279

it("should test complete transaction lifecycle", async () => {

280

const { networkHelpers } = await network.connect();

281

const [signer] = await ethers.getSigners();

282

283

// Create transaction

284

const tx = await signer.sendTransaction({

285

to: "0x123...",

286

value: ethers.parseEther("1")

287

});

288

289

// Transaction should be in mempool

290

const pendingTx = await ethers.provider.getTransaction(tx.hash);

291

expect(pendingTx).to.not.be.null;

292

expect(pendingTx.blockNumber).to.be.null; // Not mined yet

293

294

// Drop transaction

295

const dropped = await networkHelpers.dropTransaction(tx.hash);

296

expect(dropped).to.be.true;

297

298

// Transaction should no longer be in mempool

299

const droppedTx = await ethers.provider.getTransaction(tx.hash);

300

expect(droppedTx).to.be.null;

301

302

// Mine blocks

303

await networkHelpers.mine(5);

304

305

// Transaction should never appear in any block

306

const receipt = await ethers.provider.getTransactionReceipt(tx.hash);

307

expect(receipt).to.be.null;

308

});

309

```