Skip to content

Commit 7384835

Browse files
committed
test: add simple transient storage unit fuzz test
1 parent 2b9733e commit 7384835

File tree

4 files changed

+116
-1
lines changed

4 files changed

+116
-1
lines changed

src/SubscriptionModule.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ contract SubscriptionModule {
2828

2929
using CirclesLib for TypeDefinitions.Stream[];
3030

31-
using LibTransient for *;
31+
using LibTransient for LibTransient.TUint256;
3232

3333
/*//////////////////////////////////////////////////////////////
3434
STATE VARIABLES

test/Base.t.sol

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ pragma solidity >=0.8.28 <0.9.0;
33

44
import { ModuleHarness } from "./harness/ModuleHarness.sol";
55

6+
import { TransientPattern } from "./mocks/TransientPattern.sol";
67
import { Assertions } from "./utils/Assertions.sol";
78
import { Defaults } from "./utils/Defaults.sol";
89
import { Fuzzers } from "./utils/Fuzzers.sol";
@@ -23,6 +24,7 @@ abstract contract Base_Test is Assertions, Fuzzers, Modifiers {
2324
Defaults internal defaults;
2425

2526
ModuleHarness internal module;
27+
TransientPattern internal transientPattern;
2628

2729
/*//////////////////////////////////////////////////////////////
2830
SET-UP FUNCTION
@@ -40,6 +42,9 @@ abstract contract Base_Test is Assertions, Fuzzers, Modifiers {
4042
module = new ModuleHarness();
4143
vm.label(address(module), "ModuleHarness");
4244

45+
transientPattern = new TransientPattern();
46+
vm.label(address(transientPattern), "TransientPattern");
47+
4348
defaults.setUsers(users);
4449
setVariables(defaults, users);
4550

test/mocks/TransientPattern.sol

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.28;
3+
4+
import { LibTransient } from "@solady/src/utils/LibTransient.sol";
5+
6+
contract TransientPattern {
7+
using LibTransient for LibTransient.TUint256;
8+
9+
bytes32 public constant T_REDEEMABLE_AMOUNT = 0x70bfbb43a5ce660914e09d1b48fcc488982d5981137b973eac35b0592a414e90;
10+
11+
struct Data {
12+
uint256 a;
13+
uint256 l;
14+
uint256 f;
15+
}
16+
17+
Data internal data;
18+
19+
bytes32 public v;
20+
21+
function create(uint256 a, uint256 f) external {
22+
require(f > 0);
23+
data = Data({ a: a, l: block.timestamp - f, f: f });
24+
}
25+
26+
function redeem(bool isDoSomething) external {
27+
Data memory d = data;
28+
29+
// Calculate periods
30+
uint256 p = (block.timestamp - d.l) / d.f;
31+
require(p >= 1);
32+
33+
// Update data
34+
d.l += p * d.f;
35+
data = d;
36+
37+
// Set transient storage
38+
LibTransient.tUint256(T_REDEEMABLE_AMOUNT).set(p * d.a);
39+
40+
if (isDoSomething) _doSomething();
41+
else _doSomethingElse();
42+
43+
// Best practice
44+
LibTransient.tUint256(T_REDEEMABLE_AMOUNT).clear();
45+
}
46+
47+
function _doSomething() internal {
48+
v = keccak256(abi.encode("SOMETHING", LibTransient.tUint256(T_REDEEMABLE_AMOUNT).get()));
49+
}
50+
51+
function _doSomethingElse() internal {
52+
v = keccak256(abi.encode("SOMETHING_ELSE", LibTransient.tUint256(T_REDEEMABLE_AMOUNT).get()));
53+
}
54+
55+
function getData() external view returns (Data memory) {
56+
return data;
57+
}
58+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// SPDX-License-Identifier: GPL-3.0-or-later
2+
pragma solidity >=0.8.28 <0.9.0;
3+
4+
import { Base_Test } from "test/Base.t.sol";
5+
6+
import { TransientPattern } from "test/mocks/TransientPattern.sol";
7+
8+
contract TransientPattern_Unit_Fuzz_Test is Base_Test {
9+
function testFuzz_TransientFlows(uint256 a, uint256 f, uint256 t, bool flag) external {
10+
a = bound(a, 1e18, 100_000_000e18);
11+
f = bound(f, 3600, 86_400);
12+
t = bound(t, f + 1, 10 * f);
13+
14+
// Create
15+
transientPattern.create(a, f);
16+
17+
// Cache last redeemed
18+
uint256 l = vm.getBlockTimestamp() - f;
19+
20+
// Time warp
21+
vm.warp(vm.getBlockTimestamp() + t);
22+
23+
// Assert
24+
TransientPattern.Data memory ld = TransientPattern.Data(a, l, f);
25+
assertEq(keccak256(abi.encode(transientPattern.getData())), keccak256(abi.encode(ld)));
26+
27+
// Cache expected transient amount
28+
uint256 periods = (vm.getBlockTimestamp() - l) / f;
29+
uint256 ta = periods * a;
30+
31+
emit log_named_uint("periods", periods);
32+
emit log_named_uint("transient amount", ta);
33+
34+
// Redeem
35+
transientPattern.redeem(flag);
36+
37+
// Assert
38+
assertEq(transientPattern.v(), _expectedValue(flag, ta));
39+
40+
// Can we access the transient slot and get a non-zero value?
41+
uint256 shouldBeZero = uint256(vm.load(address(transientPattern), transientPattern.T_REDEEMABLE_AMOUNT()));
42+
assertEq(shouldBeZero, 0);
43+
}
44+
45+
function _expectedValue(bool isDoSomething, uint256 ta) internal returns (bytes32) {
46+
return (isDoSomething) ? _getHash("SOMETHING", ta) : _getHash("SOMETHING_ELSE", ta);
47+
}
48+
49+
function _getHash(string memory s, uint256 ta) internal returns (bytes32) {
50+
return keccak256(abi.encode(s, ta));
51+
}
52+
}

0 commit comments

Comments
 (0)