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

storage.mddocs/

0

# Storage Operations

1

2

Direct access to contract storage slots enables advanced testing scenarios including state manipulation, storage layout verification, and testing edge cases that are difficult to reach through normal contract interfaces.

3

4

## Capabilities

5

6

### Get Storage At

7

8

Reads data from a specific storage slot of a contract.

9

10

```typescript { .api }

11

/**

12

* Retrieves the data located at the given address, storage index, and block number

13

* @param address The contract address to retrieve storage from

14

* @param index The storage slot position (0-based)

15

* @param block The block number, or "latest", "earliest", or "pending" (defaults to "latest")

16

* @returns Promise resolving to a string containing the hexadecimal value stored at that slot

17

*/

18

getStorageAt(

19

address: string,

20

index: NumberLike,

21

block?: NumberLike | BlockTag

22

): Promise<string>;

23

24

type BlockTag = "latest" | "earliest" | "pending";

25

```

26

27

**Usage Examples:**

28

29

```typescript

30

import { network } from "hardhat";

31

32

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

33

34

// Read storage slot 0 (often the first state variable)

35

const slot0 = await networkHelpers.getStorageAt("0x123...", 0);

36

console.log(`Storage slot 0: ${slot0}`);

37

38

// Read specific slot at a specific block

39

const historicalValue = await networkHelpers.getStorageAt(

40

"0x123...",

41

5,

42

1000 // block number

43

);

44

45

// Read using different block tags

46

const latestValue = await networkHelpers.getStorageAt("0x123...", 0, "latest");

47

const pendingValue = await networkHelpers.getStorageAt("0x123...", 0, "pending");

48

```

49

50

### Set Storage At

51

52

Writes data directly to a specific storage slot of a contract.

53

54

```typescript { .api }

55

/**

56

* Writes a single value to an account's storage at the specified slot

57

* @param address The contract address where the value should be stored

58

* @param index The storage slot position (0-based)

59

* @param value The value to store (will be converted to 32-byte hex)

60

* @returns Promise that resolves once the storage value is set

61

*/

62

setStorageAt(

63

address: string,

64

index: NumberLike,

65

value: NumberLike

66

): Promise<void>;

67

```

68

69

**Usage Examples:**

70

71

```typescript

72

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

73

74

// Set storage slot 0 to value 42

75

await networkHelpers.setStorageAt("0x123...", 0, 42);

76

77

// Set storage slot with hex value

78

await networkHelpers.setStorageAt("0x123...", 1, "0x1234567890abcdef");

79

80

// Set storage slot with bigint

81

await networkHelpers.setStorageAt("0x123...", 2, 1000000000000000000n);

82

```

83

84

### Set Code

85

86

Replaces the bytecode of a contract, effectively changing its implementation.

87

88

```typescript { .api }

89

/**

90

* Modifies the bytecode stored at an account's address

91

* @param address The address where the given code should be stored

92

* @param code The bytecode to store (as a hex string with 0x prefix)

93

* @returns Promise that resolves once the code is set

94

*/

95

setCode(address: string, code: string): Promise<void>;

96

```

97

98

**Usage Examples:**

99

100

```typescript

101

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

102

103

// Deploy new contract and get its bytecode

104

const newContract = await ethers.deployContract("NewImplementation");

105

const newBytecode = await ethers.provider.getCode(newContract.target);

106

107

// Replace existing contract's bytecode

108

await networkHelpers.setCode("0x123...", newBytecode);

109

110

// Set empty bytecode (effectively deleting the contract)

111

await networkHelpers.setCode("0x456...", "0x");

112

```

113

114

## Storage Testing Patterns

115

116

### Direct State Manipulation

117

118

```typescript

119

it("should manipulate contract state directly", async () => {

120

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

121

122

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

123

const contractAddress = await contract.getAddress();

124

125

// Read initial value (assuming counter is in slot 0)

126

const initialValue = await networkHelpers.getStorageAt(contractAddress, 0);

127

expect(parseInt(initialValue, 16)).to.equal(0);

128

129

// Set counter to 100 directly via storage

130

await networkHelpers.setStorageAt(contractAddress, 0, 100);

131

132

// Verify through contract interface

133

expect(await contract.count()).to.equal(100);

134

});

135

```

136

137

### Testing Storage Layout

138

139

```typescript

140

it("should verify storage layout", async () => {

141

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

142

143

// Contract with multiple state variables:

144

// uint256 public value1; // slot 0

145

// uint256 public value2; // slot 1

146

// address public owner; // slot 2

147

const contract = await ethers.deployContract("MultiStorage");

148

const contractAddress = await contract.getAddress();

149

150

// Set values through contract

151

await contract.setValue1(123);

152

await contract.setValue2(456);

153

154

// Verify storage layout

155

const slot0 = await networkHelpers.getStorageAt(contractAddress, 0);

156

const slot1 = await networkHelpers.getStorageAt(contractAddress, 1);

157

158

expect(parseInt(slot0, 16)).to.equal(123);

159

expect(parseInt(slot1, 16)).to.equal(456);

160

161

// Owner should be in slot 2

162

const slot2 = await networkHelpers.getStorageAt(contractAddress, 2);

163

const owner = await contract.owner();

164

expect(slot2.toLowerCase()).to.include(owner.slice(2).toLowerCase());

165

});

166

```

167

168

### Proxy Contract Testing

169

170

```typescript

171

it("should test proxy implementation switching", async () => {

172

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

173

174

// Deploy proxy and implementations

175

const proxy = await ethers.deployContract("Proxy");

176

const impl1 = await ethers.deployContract("Implementation1");

177

const impl2 = await ethers.deployContract("Implementation2");

178

179

const proxyAddress = await proxy.getAddress();

180

181

// Set initial implementation

182

await proxy.setImplementation(impl1.target);

183

184

// Get implementation bytecode

185

const impl2Code = await ethers.provider.getCode(impl2.target);

186

187

// Replace proxy's implementation directly

188

await networkHelpers.setCode(proxyAddress, impl2Code);

189

190

// Verify the proxy now behaves like Implementation2

191

const proxyAsImpl2 = await ethers.getContractAt("Implementation2", proxyAddress);

192

await expect(proxyAsImpl2.newFunction()).to.not.be.reverted;

193

});

194

```

195

196

### Simulating Storage Corruption

197

198

```typescript

199

it("should handle corrupted storage", async () => {

200

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

201

202

const contract = await ethers.deployContract("TokenContract");

203

const contractAddress = await contract.getAddress();

204

205

// Corrupt the total supply (assuming it's in slot 0)

206

await networkHelpers.setStorageAt(contractAddress, 0, "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");

207

208

// Contract should handle overflow/corruption gracefully

209

await expect(contract.transfer("0x123...", 1)).to.be.reverted;

210

});

211

```

212

213

### Bypassing Access Controls

214

215

```typescript

216

it("should bypass access controls via storage", async () => {

217

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

218

219

const contract = await ethers.deployContract("AccessControlled");

220

const contractAddress = await contract.getAddress();

221

222

// Find the storage slot for the admin address (assume slot 0)

223

const myAddress = await ethers.getSigners().then(s => s[0].address);

224

225

// Set ourselves as admin directly via storage

226

await networkHelpers.setStorageAt(

227

contractAddress,

228

0,

229

ethers.zeroPadValue(myAddress, 32)

230

);

231

232

// Now admin functions should work

233

await expect(contract.adminOnlyFunction()).to.not.be.reverted;

234

});

235

```

236

237

### Testing Packed Storage

238

239

```typescript

240

it("should handle packed storage variables", async () => {

241

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

242

243

// Contract with packed variables:

244

// struct PackedData {

245

// uint128 value1; // slot 0, first 16 bytes

246

// uint128 value2; // slot 0, last 16 bytes

247

// }

248

const contract = await ethers.deployContract("PackedStorage");

249

const contractAddress = await contract.getAddress();

250

251

// Set packed values - both values in same slot

252

const value1 = 123;

253

const value2 = 456;

254

255

// Pack both values into single 32-byte slot

256

const packedValue = ethers.toBigInt(value1) | (ethers.toBigInt(value2) << 128n);

257

258

await networkHelpers.setStorageAt(contractAddress, 0, packedValue);

259

260

// Verify both values are correctly stored

261

expect(await contract.getValue1()).to.equal(value1);

262

expect(await contract.getValue2()).to.equal(value2);

263

});

264

```

265

266

### Historical State Analysis

267

268

```typescript

269

it("should analyze historical state", async () => {

270

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

271

272

const contract = await ethers.deployContract("HistoryContract");

273

const contractAddress = await contract.getAddress();

274

275

// Record initial state

276

const initialBlock = await networkHelpers.time.latestBlock();

277

await contract.setValue(100);

278

279

// Change state

280

await networkHelpers.mine(5);

281

await contract.setValue(200);

282

283

// Change state again

284

await networkHelpers.mine(5);

285

await contract.setValue(300);

286

287

const finalBlock = await networkHelpers.time.latestBlock();

288

289

// Read historical values

290

const valueAtInitial = await networkHelpers.getStorageAt(

291

contractAddress,

292

0,

293

initialBlock + 1

294

);

295

const valueAtMiddle = await networkHelpers.getStorageAt(

296

contractAddress,

297

0,

298

initialBlock + 6

299

);

300

const valueAtEnd = await networkHelpers.getStorageAt(

301

contractAddress,

302

0,

303

finalBlock

304

);

305

306

expect(parseInt(valueAtInitial, 16)).to.equal(100);

307

expect(parseInt(valueAtMiddle, 16)).to.equal(200);

308

expect(parseInt(valueAtEnd, 16)).to.equal(300);

309

});

310

```