1+ // SPDX-License-Identifier: MIT
2+ pragma solidity 0.8.24 ;
3+
4+ import "./TestSetup.sol " ;
5+ import "forge-std/console.sol " ;
6+
7+ contract PaginationGasTest is TestSetup {
8+ uint256 constant SMALL_CONTENT_SIZE = 10 ; // 10 bytes
9+ uint256 constant MEDIUM_CONTENT_SIZE = 100 ; // 100 bytes
10+ uint256 constant LARGE_CONTENT_SIZE = 1000 ; // 1KB
11+ uint256 constant HUGE_CONTENT_SIZE = 10000 ; // 10KB
12+
13+ // Create ethscriptions with different content sizes for testing
14+ function setUp () public override {
15+ super .setUp ();
16+
17+ // Create ethscriptions with various content sizes
18+ // We'll create 100 ethscriptions to test pagination properly
19+ for (uint256 i = 0 ; i < 100 ; i++ ) {
20+ bytes32 txHash = bytes32 (uint256 (0x1000000 + i));
21+ address creator = address (uint160 (0x100 + (i % 10 ))); // 10 different creators
22+ address owner = address (uint160 (0x200 + (i % 5 ))); // 5 different owners
23+
24+ // Vary content size based on index
25+ bytes memory content;
26+ if (i % 4 == 0 ) {
27+ content = new bytes (SMALL_CONTENT_SIZE);
28+ } else if (i % 4 == 1 ) {
29+ content = new bytes (MEDIUM_CONTENT_SIZE);
30+ } else if (i % 4 == 2 ) {
31+ content = new bytes (LARGE_CONTENT_SIZE);
32+ } else {
33+ content = new bytes (HUGE_CONTENT_SIZE);
34+ }
35+
36+ // Fill content with some data
37+ for (uint256 j = 0 ; j < content.length ; j++ ) {
38+ content[j] = bytes1 (uint8 (j % 256 ));
39+ }
40+
41+ vm.prank (creator);
42+ Ethscriptions.CreateEthscriptionParams memory params = Ethscriptions.CreateEthscriptionParams ({
43+ ethscriptionId: txHash,
44+ contentUriHash: keccak256 (abi.encodePacked ("uri " , i)),
45+ initialOwner: owner,
46+ content: content,
47+ mimetype: "application/octet-stream " ,
48+ esip6: false ,
49+ protocolParams: Ethscriptions.ProtocolParams ({
50+ protocolName: "" ,
51+ operation: "" ,
52+ data: ""
53+ })
54+ });
55+
56+ ethscriptions.createEthscription (params);
57+ }
58+
59+ console.log ("Setup complete: Created 100 ethscriptions " );
60+ console.log ("- 25 with small content (10 bytes) " );
61+ console.log ("- 25 with medium content (100 bytes) " );
62+ console.log ("- 25 with large content (1KB) " );
63+ console.log ("- 25 with huge content (10KB) " );
64+ console.log ("" );
65+ }
66+
67+ function testGas_GetEthscriptions_WithContent () public view {
68+ console.log ("=== Testing getEthscriptions WITH content === " );
69+ console.log ("" );
70+
71+ // Test various page sizes with content
72+ uint256 [] memory pageSizes = new uint256 [](7 );
73+ pageSizes[0 ] = 1 ;
74+ pageSizes[1 ] = 10 ;
75+ pageSizes[2 ] = 20 ;
76+ pageSizes[3 ] = 30 ;
77+ pageSizes[4 ] = 40 ;
78+ pageSizes[5 ] = 50 ;
79+ pageSizes[6 ] = 60 ; // Should be clamped to 50
80+
81+ for (uint256 i = 0 ; i < pageSizes.length ; i++ ) {
82+ uint256 gasStart = gasleft ();
83+ Ethscriptions.PaginatedEthscriptionsResponse memory result = ethscriptions.getEthscriptions (0 , pageSizes[i], true );
84+ uint256 gasUsed = gasStart - gasleft ();
85+
86+ console.log ("Requested: " , pageSizes[i], "items " );
87+ console.log (" Returned: " , result.items.length , "items " );
88+ console.log (" Gas used: " , gasUsed);
89+ console.log (" Gas per item: " , result.items.length > 0 ? gasUsed / result.items.length : 0 );
90+ console.log ("" );
91+ }
92+ }
93+
94+ function testGas_GetEthscriptions_WithoutContent () public view {
95+ console.log ("=== Testing getEthscriptions WITHOUT content === " );
96+ console.log ("" );
97+
98+ // Test various page sizes without content
99+ uint256 [] memory pageSizes = new uint256 [](10 );
100+ pageSizes[0 ] = 1 ;
101+ pageSizes[1 ] = 10 ;
102+ pageSizes[2 ] = 50 ;
103+ pageSizes[3 ] = 100 ;
104+ pageSizes[4 ] = 200 ;
105+ pageSizes[5 ] = 300 ;
106+ pageSizes[6 ] = 500 ;
107+ pageSizes[7 ] = 750 ;
108+ pageSizes[8 ] = 1000 ;
109+ pageSizes[9 ] = 1500 ; // Should be clamped to 1000
110+
111+ for (uint256 i = 0 ; i < pageSizes.length ; i++ ) {
112+ uint256 gasStart = gasleft ();
113+ Ethscriptions.PaginatedEthscriptionsResponse memory result = ethscriptions.getEthscriptions (0 , pageSizes[i], false );
114+ uint256 gasUsed = gasStart - gasleft ();
115+
116+ console.log ("Requested: " , pageSizes[i], "items " );
117+ console.log (" Returned: " , result.items.length , "items " );
118+ console.log (" Gas used: " , gasUsed);
119+ console.log (" Gas per item: " , result.items.length > 0 ? gasUsed / result.items.length : 0 );
120+ console.log ("" );
121+ }
122+ }
123+
124+ function testGas_GetOwnerEthscriptions_WithContent () public view {
125+ console.log ("=== Testing getOwnerEthscriptions WITH content === " );
126+ console.log ("" );
127+
128+ // Test with owner that has 20 ethscriptions (address(0x200))
129+ address targetOwner = address (0x200 );
130+ uint256 ownerBalance = ethscriptions.balanceOf (targetOwner);
131+ console.log ("Owner balance: " , ownerBalance);
132+ console.log ("" );
133+
134+ // Test various page sizes
135+ uint256 [] memory pageSizes = new uint256 [](5 );
136+ pageSizes[0 ] = 5 ;
137+ pageSizes[1 ] = 10 ;
138+ pageSizes[2 ] = 15 ;
139+ pageSizes[3 ] = 20 ;
140+ pageSizes[4 ] = 30 ; // More than owner has
141+
142+ for (uint256 i = 0 ; i < pageSizes.length ; i++ ) {
143+ uint256 gasStart = gasleft ();
144+ Ethscriptions.PaginatedEthscriptionsResponse memory result = ethscriptions.getOwnerEthscriptions (targetOwner, 0 , pageSizes[i], true );
145+ uint256 gasUsed = gasStart - gasleft ();
146+
147+ console.log ("Requested: " , pageSizes[i], "items " );
148+ console.log (" Returned: " , result.items.length , "items " );
149+ console.log (" Gas used: " , gasUsed);
150+ console.log (" Gas per item: " , result.items.length > 0 ? gasUsed / result.items.length : 0 );
151+ console.log ("" );
152+ }
153+ }
154+
155+ function testGas_GetOwnerEthscriptions_WithoutContent () public view {
156+ console.log ("=== Testing getOwnerEthscriptions WITHOUT content === " );
157+ console.log ("" );
158+
159+ // Test with owner that has 20 ethscriptions
160+ address targetOwner = address (0x200 );
161+ uint256 ownerBalance = ethscriptions.balanceOf (targetOwner);
162+ console.log ("Owner balance: " , ownerBalance);
163+ console.log ("" );
164+
165+ // Test various page sizes
166+ uint256 [] memory pageSizes = new uint256 [](5 );
167+ pageSizes[0 ] = 5 ;
168+ pageSizes[1 ] = 10 ;
169+ pageSizes[2 ] = 15 ;
170+ pageSizes[3 ] = 20 ;
171+ pageSizes[4 ] = 30 ; // More than owner has
172+
173+ for (uint256 i = 0 ; i < pageSizes.length ; i++ ) {
174+ uint256 gasStart = gasleft ();
175+ Ethscriptions.PaginatedEthscriptionsResponse memory result = ethscriptions.getOwnerEthscriptions (targetOwner, 0 , pageSizes[i], false );
176+ uint256 gasUsed = gasStart - gasleft ();
177+
178+ console.log ("Requested: " , pageSizes[i], "items " );
179+ console.log (" Returned: " , result.items.length , "items " );
180+ console.log (" Gas used: " , gasUsed);
181+ console.log (" Gas per item: " , result.items.length > 0 ? gasUsed / result.items.length : 0 );
182+ console.log ("" );
183+ }
184+ }
185+
186+ function testGas_EdgeCases () public view {
187+ console.log ("=== Testing Edge Cases === " );
188+ console.log ("" );
189+
190+ // Test with start beyond total supply
191+ uint256 gasStart = gasleft ();
192+ Ethscriptions.PaginatedEthscriptionsResponse memory result1 = ethscriptions.getEthscriptions (200 , 10 , true );
193+ uint256 gasUsed = gasStart - gasleft ();
194+ console.log ("Start beyond total (200, 10): " );
195+ console.log (" Returned: " , result1.items.length , "items " );
196+ console.log (" Gas used: " , gasUsed);
197+ console.log ("" );
198+
199+ // Test with limit = 0 (should revert)
200+ console.log ("Limit = 0: " );
201+ try ethscriptions.getEthscriptions (0 , 0 , true ) returns (Ethscriptions.PaginatedEthscriptionsResponse memory ) {
202+ console.log (" ERROR: Should have reverted! " );
203+ } catch {
204+ console.log (" Correctly reverted with InvalidPaginationLimit " );
205+ }
206+ console.log ("" );
207+
208+ // Test pagination continuation
209+ gasStart = gasleft ();
210+ Ethscriptions.PaginatedEthscriptionsResponse memory page1 = ethscriptions.getEthscriptions (0 , 30 , true );
211+ gasUsed = gasStart - gasleft ();
212+ console.log ("First page (0, 30): " );
213+ console.log (" Returned: " , page1.items.length , "items " );
214+ console.log (" Has more: " , page1.hasMore);
215+ console.log (" Next start: " , page1.nextStart);
216+ console.log (" Gas used: " , gasUsed);
217+ console.log ("" );
218+
219+ if (page1.hasMore) {
220+ gasStart = gasleft ();
221+ Ethscriptions.PaginatedEthscriptionsResponse memory page2 = ethscriptions.getEthscriptions (page1.nextStart, 30 , true );
222+ gasUsed = gasStart - gasleft ();
223+ console.log ("Second page starting at: " , page1.nextStart);
224+ console.log (" Returned: " , page2.items.length , "items " );
225+ console.log (" Has more: " , page2.hasMore);
226+ console.log (" Gas used: " , gasUsed);
227+ console.log ("" );
228+ }
229+ }
230+
231+ function testGas_MaximumLimits () public {
232+ console.log ("=== Testing Maximum Safe Limits === " );
233+ console.log ("" );
234+
235+ // Test approaching gas limits with content
236+ console.log ("Testing max with content (trying different sizes): " );
237+ uint256 [] memory testSizes = new uint256 [](5 );
238+ testSizes[0 ] = 40 ;
239+ testSizes[1 ] = 45 ;
240+ testSizes[2 ] = 50 ;
241+ testSizes[3 ] = 55 ;
242+ testSizes[4 ] = 60 ;
243+
244+ for (uint256 i = 0 ; i < testSizes.length ; i++ ) {
245+ try ethscriptions.getEthscriptions (0 , testSizes[i], true ) returns (Ethscriptions.PaginatedEthscriptionsResponse memory result ) {
246+ uint256 gasStart = gasleft ();
247+ ethscriptions.getEthscriptions (0 , testSizes[i], true );
248+ uint256 gasUsed = gasStart - gasleft ();
249+ console.log (" Size: " , testSizes[i]);
250+ console.log (" Returned items: " , result.items.length );
251+ console.log (" Gas used: " , gasUsed);
252+ } catch {
253+ console.log (" Size FAILED: " , testSizes[i]);
254+ }
255+ }
256+ console.log ("" );
257+
258+ // Test approaching gas limits without content
259+ console.log ("Testing max without content (trying different sizes): " );
260+ uint256 [] memory testSizesNoContent = new uint256 [](5 );
261+ testSizesNoContent[0 ] = 800 ;
262+ testSizesNoContent[1 ] = 900 ;
263+ testSizesNoContent[2 ] = 1000 ;
264+ testSizesNoContent[3 ] = 1100 ;
265+ testSizesNoContent[4 ] = 1200 ;
266+
267+ // Need to create more ethscriptions for this test
268+ if (ethscriptions.totalSupply () < 1200 ) {
269+ console.log (" (Skipping - need more ethscriptions for full test) " );
270+ } else {
271+ for (uint256 i = 0 ; i < testSizesNoContent.length ; i++ ) {
272+ try ethscriptions.getEthscriptions (0 , testSizesNoContent[i], false ) returns (Ethscriptions.PaginatedEthscriptionsResponse memory result ) {
273+ uint256 gasStart = gasleft ();
274+ ethscriptions.getEthscriptions (0 , testSizesNoContent[i], false );
275+ uint256 gasUsed = gasStart - gasleft ();
276+ console.log (" Size: " , testSizesNoContent[i]);
277+ console.log (" Returned items: " , result.items.length );
278+ console.log (" Gas used: " , gasUsed);
279+ } catch {
280+ console.log (" Size FAILED: " , testSizesNoContent[i]);
281+ }
282+ }
283+ }
284+ }
285+ }
0 commit comments