@rootzero/contracts 0.9.3 → 0.9.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,7 +4,6 @@ pragma solidity ^0.8.33;
4
4
  import {AssetAmount, AccountAsset, AccountAmount, HostAmount, HostAccountAsset, Tx} from "../core/Types.sol";
5
5
  import {Sizes} from "./Schema.sol";
6
6
  import {Keys} from "./Keys.sol";
7
- import {Writer, Writers} from "./Writers.sol";
8
7
 
9
8
  /// @notice Zero-copy view into a calldata block stream.
10
9
  /// All positions (`i`, `bound`) are byte offsets relative to the start of the source region.
@@ -47,6 +46,7 @@ library Cursors {
47
46
  error UnexpectedValue();
48
47
  /// @dev Prime block counts are not divisible by, or do not match, their declared group sizes.
49
48
  error BadRatio();
49
+
50
50
  // -------------------------------------------------------------------------
51
51
  // Cursor construction and navigation
52
52
  // -------------------------------------------------------------------------
@@ -65,6 +65,17 @@ library Cursors {
65
65
  cur.len = source.length;
66
66
  }
67
67
 
68
+ /// @notice Create a cursor and prime it for a grouped iteration pass.
69
+ /// Equivalent to `open(source)` followed by `primeRun(group)`.
70
+ /// @param source Calldata slice that forms the block stream.
71
+ /// @param group Expected block group size (e.g. 1 for single, 2 for paired).
72
+ /// @return cur Cursor with `bound` set to the end of the first run.
73
+ /// @return groups Number of block groups in the run (`prime block count / group`).
74
+ function init(bytes calldata source, uint group) internal pure returns (Cur memory cur, uint groups) {
75
+ cur = open(source);
76
+ (, groups) = cur.primeRun(group);
77
+ }
78
+
68
79
  /// @notice Move the cursor to an absolute position within the source region.
69
80
  /// @param cur Cursor to update.
70
81
  /// @param i New read position (byte offset relative to source start).
@@ -75,6 +86,26 @@ library Cursors {
75
86
  return cur;
76
87
  }
77
88
 
89
+ /// @notice Advance the cursor by `n` bytes within the source region.
90
+ /// Reverts with `IncompleteCursor` if `n` exceeds the remaining cursor region length.
91
+ /// @param cur Cursor to advance.
92
+ /// @param n Number of bytes to skip from the current read position.
93
+ function skip(Cur memory cur, uint n) internal pure returns (Cur memory) {
94
+ if (n > cur.len - cur.i) revert IncompleteCursor();
95
+ cur.i += n;
96
+ return cur;
97
+ }
98
+
99
+ /// @notice Advance the cursor to a later absolute position within the source region.
100
+ /// Reverts with `IncompleteCursor` if `i` exceeds the cursor region length or is before `cur.i`.
101
+ /// @param cur Cursor to advance.
102
+ /// @param i New read position (byte offset relative to source start).
103
+ function skipTo(Cur memory cur, uint i) internal pure returns (Cur memory) {
104
+ if (i > cur.len || cur.i > i) revert IncompleteCursor();
105
+ cur.i = i;
106
+ return cur;
107
+ }
108
+
78
109
  /// @notice Create a subcursor over the half-open range `[from, to)` within the source region.
79
110
  /// The returned cursor starts at position zero within that sliced region.
80
111
  /// @param cur Source cursor.
@@ -103,7 +134,19 @@ library Cursors {
103
134
  /// @param to End byte offset within the source region (exclusive).
104
135
  /// @return data Calldata view over the requested sub-range.
105
136
  function raw(Cur memory cur, uint from, uint to) internal pure returns (bytes calldata data) {
106
- data = cur.slice(from, to).raw();
137
+ if (from > to || to > cur.len) revert MalformedBlocks();
138
+ if (cur.len > msg.data.length || cur.offset > msg.data.length - cur.len) revert MalformedBlocks();
139
+ data = msg.data[cur.offset + from:cur.offset + to];
140
+ }
141
+
142
+ /// @notice Hash a sub-range of the cursor region.
143
+ /// Does not advance the cursor; `from` and `to` are relative to the source region.
144
+ /// @param cur Source cursor.
145
+ /// @param from Start byte offset within the source region (inclusive).
146
+ /// @param to End byte offset within the source region (exclusive).
147
+ /// @return digest Keccak256 hash of the requested sub-range.
148
+ function hash(Cur memory cur, uint from, uint to) internal pure returns (bytes32 digest) {
149
+ digest = keccak256(cur.raw(from, to));
107
150
  }
108
151
 
109
152
  /// @notice Read a block header at position `i` without advancing the cursor.
@@ -112,11 +155,11 @@ library Cursors {
112
155
  /// @return key Four-byte block type identifier.
113
156
  /// @return len Payload byte length declared in the header.
114
157
  function peek(Cur memory cur, uint i) internal pure returns (bytes4 key, uint len) {
115
- if (i + 8 > cur.len) revert MalformedBlocks();
158
+ if (i + Sizes.Header > cur.len) revert MalformedBlocks();
116
159
  uint abs = cur.offset + i;
117
160
  key = bytes4(msg.data[abs:abs + 4]);
118
161
  len = uint32(bytes4(msg.data[abs + 4:abs + 8]));
119
- if (i + 8 + len > cur.len) revert MalformedBlocks();
162
+ if (i + Sizes.Header + len > cur.len) revert MalformedBlocks();
120
163
  }
121
164
 
122
165
  /// @notice Return the byte offset immediately past the block at the current cursor position.
@@ -128,34 +171,49 @@ library Cursors {
128
171
  return cur.i + Sizes.Header + len;
129
172
  }
130
173
 
174
+ /// @notice Return true if position `i` is at a block header with the given key.
175
+ /// Returns false when `i` is out of bounds or the key differs.
176
+ /// @param cur Source cursor.
177
+ /// @param i Byte offset of the block header within the source region.
178
+ /// @param key Expected block type identifier.
179
+ /// @return Whether the block header at `i` uses `key`.
180
+ function hasAt(Cur memory cur, uint i, bytes4 key) internal pure returns (bool) {
181
+ if (i > cur.len || Sizes.Header > cur.len - i) return false;
182
+ uint abs = cur.offset + i;
183
+ return bytes4(msg.data[abs:abs + 4]) == key;
184
+ }
185
+
131
186
  /// @notice Return true if the current cursor position is at a block header with the given key.
132
187
  /// Returns false when `cur.i` is out of bounds or the key differs.
133
188
  /// @param cur Source cursor.
134
189
  /// @param key Expected block type identifier.
135
190
  /// @return Whether the block header at `cur.i` uses `key`.
136
191
  function isAt(Cur memory cur, bytes4 key) internal pure returns (bool) {
137
- if (cur.i + 8 > cur.len) return false;
138
- uint abs = cur.offset + cur.i;
139
- return bytes4(msg.data[abs:abs + 4]) == key;
192
+ return cur.hasAt(cur.i, key);
140
193
  }
141
194
 
142
- /// @notice Return whether the remaining cursor region is empty or exactly one block with `key`.
143
- /// Returns false for an empty remaining region. Reverts if the next block has another key or
144
- /// if a matching block is followed by trailing bytes.
145
- /// @param cur Source cursor.
146
- /// @param key Expected optional block key.
147
- /// @return Whether the remaining region contains exactly one block with `key`.
148
- function maybeOnly(Cur memory cur, bytes4 key) internal pure returns (bool) {
149
- if (cur.i == cur.len) return false;
150
- if (!cur.isAt(key)) revert InvalidBlock();
151
- if (cur.past() != cur.len) revert IncompleteCursor();
152
- return true;
195
+ /// @notice Enter a block at the current position and return its next offset.
196
+ /// Advances `cur.i` past the block header so the payload can be parsed
197
+ /// directly from the same cursor. The returned `next` is the byte offset
198
+ /// immediately after the block payload, relative to the current cursor region.
199
+ /// @param cur Cursor positioned at the expected block; advanced past the 8-byte header.
200
+ /// @param key Expected block key.
201
+ /// @param min Minimum acceptable payload length.
202
+ /// @param max Maximum acceptable payload length; 0 means unbounded.
203
+ /// @return next Byte offset immediately after the block payload.
204
+ function enter(Cur memory cur, bytes4 key, uint min, uint max) internal pure returns (uint next) {
205
+ (bytes4 current, uint len) = peek(cur, cur.i);
206
+ if (current != key) revert InvalidBlock();
207
+ if (len < min || (max != 0 && len > max)) revert InvalidBlock();
208
+ next = cur.i + Sizes.Header + len;
209
+ cur.i += Sizes.Header;
153
210
  }
154
211
 
155
212
  /// @notice Validate a block at position `i` and return its payload location.
156
213
  /// Does not advance the cursor.
157
214
  /// @param cur Source cursor.
158
215
  /// @param i Byte offset of the block within the source region.
216
+ /// @param end Required next offset after the block; 0 means no exact-end check.
159
217
  /// @param key Expected block type key; reverts if actual key differs.
160
218
  /// @param min Minimum acceptable payload length (inclusive).
161
219
  /// @param max Maximum acceptable payload length (inclusive); 0 means unbounded.
@@ -164,15 +222,30 @@ library Cursors {
164
222
  function expect(
165
223
  Cur memory cur,
166
224
  uint i,
225
+ uint end,
167
226
  bytes4 key,
168
227
  uint min,
169
228
  uint max
170
229
  ) internal pure returns (uint abs, uint next) {
171
230
  (bytes4 current, uint len) = peek(cur, i);
172
231
  if (current != key) revert InvalidBlock();
173
- abs = cur.offset + i + 8;
174
- next = i + 8 + len;
175
232
  if (len < min || (max != 0 && len > max)) revert InvalidBlock();
233
+ abs = cur.offset + i + Sizes.Header;
234
+ next = i + Sizes.Header + len;
235
+ if (end != 0 && next != end) revert IncompleteCursor();
236
+ }
237
+
238
+ /// @notice Validate and consume the current block, advancing `cur.i` past it.
239
+ /// @param cur Cursor to advance.
240
+ /// @param end Required next offset after the block; 0 means no exact-end check.
241
+ /// @param key Expected block type key.
242
+ /// @param min Minimum payload length.
243
+ /// @param max Maximum payload length (0 = unbounded).
244
+ /// @return abs Absolute calldata offset of the payload start.
245
+ function consume(Cur memory cur, uint end, bytes4 key, uint min, uint max) internal pure returns (uint abs) {
246
+ uint next;
247
+ (abs, next) = expect(cur, cur.i, end, key, min, max);
248
+ cur.i = next;
176
249
  }
177
250
 
178
251
  /// @notice Count consecutive blocks of the same key starting at `i`.
@@ -186,7 +259,7 @@ library Cursors {
186
259
  while (next < cur.len) {
187
260
  (bytes4 current, uint len) = peek(cur, next);
188
261
  if (current != key) break;
189
- next += 8 + len;
262
+ next += Sizes.Header + len;
190
263
 
191
264
  unchecked {
192
265
  ++total;
@@ -197,20 +270,19 @@ library Cursors {
197
270
  /// @notice Initialise the cursor for a grouped iteration pass.
198
271
  /// Reads the key of the first block, counts the consecutive run of that key,
199
272
  /// stores the run end in `cur.bound`, validates that the count is a
200
- /// multiple of `group`, and returns both the raw block count and the
201
- /// normalized quotient (`count / group`).
273
+ /// multiple of `group`, and returns the run key and normalized group count.
202
274
  /// @param cur Cursor to prime; `cur.bound` is updated in place.
203
275
  /// @param group Expected group size (e.g. 1 for single-asset, 2 for paired input/output).
204
276
  /// @return key Block type identifier of the run.
205
- /// @return count Total number of blocks in the run (always a multiple of `group`).
206
- /// @return quotient Number of groups represented by the run (`count / group`).
207
- function primeRun(Cur memory cur, uint group) internal pure returns (bytes4 key, uint count, uint quotient) {
277
+ /// @return groups Number of groups represented by the run (`block count / group`).
278
+ function primeRun(Cur memory cur, uint group) internal pure returns (bytes4 key, uint groups) {
208
279
  if (group == 0) revert ZeroGroup();
209
280
  key = cur.i + 4 > cur.len ? bytes4(0) : bytes4(msg.data[cur.offset + cur.i:cur.offset + cur.i + 4]);
281
+ uint count;
210
282
  (count, cur.bound) = countRun(cur, cur.i, key);
211
283
  if (count == 0) revert ZeroCursor();
212
284
  if (count % group != 0) revert BadRatio();
213
- quotient = count / group;
285
+ groups = count / group;
214
286
  }
215
287
 
216
288
  /// @notice Scan forward from `i` for the first block matching `key`.
@@ -222,7 +294,7 @@ library Cursors {
222
294
  while (i < cur.len) {
223
295
  (bytes4 current, uint len) = peek(cur, i);
224
296
  if (current == key) return i;
225
- i += 8 + len;
297
+ i += Sizes.Header + len;
226
298
  }
227
299
  return cur.len;
228
300
  }
@@ -235,29 +307,6 @@ library Cursors {
235
307
  return find(cur, cur.i, key);
236
308
  }
237
309
 
238
- /// @notice Validate and consume the current block, advancing `cur.i` past it.
239
- /// @param cur Cursor to advance.
240
- /// @param key Expected block type key.
241
- /// @param min Minimum payload length.
242
- /// @param max Maximum payload length (0 = unbounded).
243
- /// @return abs Absolute calldata offset of the payload start.
244
- function consume(Cur memory cur, bytes4 key, uint min, uint max) internal pure returns (uint abs) {
245
- uint next;
246
- (abs, next) = expect(cur, cur.i, key, min, max);
247
- cur.i = next;
248
- }
249
-
250
- /// @notice Enter a Bundle block at the current position and return the next offset.
251
- /// Advances `cur.i` past the bundle header so the bundled members can be parsed
252
- /// directly from the same cursor. The returned `next` is the byte offset
253
- /// immediately after the bundle payload, relative to the current cursor region.
254
- /// @param cur Cursor positioned at a bundle block; advanced past the 8-byte header.
255
- /// @return next Byte offset immediately after the bundle payload.
256
- function bundle(Cur memory cur) internal pure returns (uint next) {
257
- (, next) = expect(cur, cur.i, Keys.Bundle, 0, 0);
258
- cur.i += 8;
259
- }
260
-
261
310
  /// @notice Enter a List block at the current position and return the next offset.
262
311
  /// Advances `cur.i` past the list header so the list members can be parsed
263
312
  /// directly from the same cursor. The returned `next` is the byte offset
@@ -265,8 +314,7 @@ library Cursors {
265
314
  /// @param cur Cursor positioned at a list block; advanced past the 8-byte header.
266
315
  /// @return next Byte offset immediately after the list payload.
267
316
  function list(Cur memory cur) internal pure returns (uint next) {
268
- (, next) = expect(cur, cur.i, Keys.List, 0, 0);
269
- cur.i += 8;
317
+ next = enter(cur, Keys.List, 0, 0);
270
318
  }
271
319
 
272
320
  /// @notice Consume a block with the given key at the current position and return a cursor over the full block slice.
@@ -276,11 +324,24 @@ library Cursors {
276
324
  /// @param key Expected block type key.
277
325
  /// @return out Cursor scoped to the full block.
278
326
  function take(Cur memory cur, bytes4 key) internal pure returns (Cur memory out) {
279
- (, uint next) = expect(cur, cur.i, key, 0, 0);
327
+ (, uint next) = expect(cur, cur.i, 0, key, 0, 0);
280
328
  out = cur.slice(cur.i, next);
281
329
  cur.i = next;
282
330
  }
283
331
 
332
+ /// @notice Return whether the remaining cursor region is empty or exactly one block with `key`.
333
+ /// Returns false for an empty remaining region. Reverts if the next block has another key or
334
+ /// if a matching block is followed by trailing bytes.
335
+ /// @param cur Source cursor.
336
+ /// @param key Expected optional block key.
337
+ /// @return Whether the remaining region contains exactly one block with `key`.
338
+ function maybeOnly(Cur memory cur, bytes4 key) internal pure returns (bool) {
339
+ if (cur.i == cur.len) return false;
340
+ if (!cur.isAt(key)) revert InvalidBlock();
341
+ if (cur.past() != cur.len) revert IncompleteCursor();
342
+ return true;
343
+ }
344
+
284
345
  /// @notice Consume an optional block with the given key and return a cursor over the full block slice.
285
346
  /// If the current block key does not match, returns an empty cursor and leaves `cur.i` unchanged.
286
347
  /// Otherwise behaves like `take(cur, key)`.
@@ -291,13 +352,13 @@ library Cursors {
291
352
  return cur.isAt(key) ? take(cur, key) : cur.slice(cur.i, cur.i);
292
353
  }
293
354
 
294
- /// @notice Consume an optional ROUTE block at the current position and return a cursor over the full block slice.
295
- /// If the current block is not ROUTE, returns an empty cursor and leaves `cur.i` unchanged.
296
- /// Otherwise behaves like `take(cur, Keys.Route)`.
297
- /// @param cur Cursor positioned at an optional ROUTE block.
298
- /// @return out Cursor scoped to the full ROUTE block, or empty when no ROUTE block is present.
299
- function maybeRoute(Cur memory cur) internal pure returns (Cur memory out) {
300
- return maybeTake(cur, Keys.Route);
355
+ /// @notice Consume an optional DATA block at the current position and return a cursor over the full block slice.
356
+ /// If the current block is not DATA, returns an empty cursor and leaves `cur.i` unchanged.
357
+ /// Otherwise behaves like `take(cur, Keys.Data)`.
358
+ /// @param cur Cursor positioned at an optional DATA block.
359
+ /// @return out Cursor scoped to the full DATA block, or empty when no DATA block is present.
360
+ function maybeData(Cur memory cur) internal pure returns (Cur memory out) {
361
+ return maybeTake(cur, Keys.Data);
301
362
  }
302
363
 
303
364
  /// @notice Enter a List block, prime its member run, and return the group count.
@@ -307,56 +368,45 @@ library Cursors {
307
368
  /// @return next Byte offset immediately after the list payload.
308
369
  function list(Cur memory cur, uint group) internal pure returns (uint groups, uint next) {
309
370
  next = list(cur);
310
- (, , groups) = cur.primeRun(group);
371
+ (, groups) = cur.primeRun(group);
311
372
  if (cur.bound != next) revert IncompleteCursor();
312
373
  }
313
374
 
375
+ /// @notice Exit a nested region at an exact boundary.
376
+ /// Reverts with `IncompleteCursor` if `end` exceeds the cursor region length
377
+ /// or `cur.i != end`.
378
+ /// @param cur Cursor to check.
379
+ /// @param end Relative end offset of the nested region.
380
+ function exit(Cur memory cur, uint end) internal pure {
381
+ if (end > cur.len || cur.i != end) revert IncompleteCursor();
382
+ }
383
+
314
384
  /// @notice Assert that the cursor has consumed exactly up to `bound`.
315
385
  /// Reverts with `IncompleteCursor` if `bound` is zero or `cur.i != cur.bound`.
316
386
  /// @param cur Cursor to check.
317
- function complete(Cur memory cur) internal pure {
387
+ function close(Cur memory cur) internal pure {
318
388
  if (cur.bound == 0 || cur.i != cur.bound) revert IncompleteCursor();
319
389
  }
320
390
 
321
391
  /// @notice Assert that the cursor has consumed its entire source region.
322
392
  /// Reverts with `IncompleteCursor` when `cur.i != cur.len`.
323
393
  /// @param cur Cursor to check.
324
- function end(Cur memory cur) internal pure {
394
+ function complete(Cur memory cur) internal pure {
325
395
  if (cur.i != cur.len) revert IncompleteCursor();
326
396
  }
327
397
 
328
- /// @notice Resume parsing after a nested region delimited by `resumeAt`.
329
- /// Reverts with `IncompleteCursor` if `cur.i` has advanced past `resumeAt` or `resumeAt`
330
- /// exceeds the cursor region length. Otherwise moves `cur.i` to `end`.
331
- /// @param cur Cursor to advance.
332
- /// @param resumeAt Relative end offset of the nested region to resume after.
333
- function resume(Cur memory cur, uint resumeAt) internal pure {
334
- if (resumeAt > cur.len || cur.i > resumeAt) revert IncompleteCursor();
335
- cur.i = resumeAt;
336
- }
337
-
338
- /// @notice Ensure that parsing has reached an exact nested-region boundary.
339
- /// Reverts with `IncompleteCursor` if `ensureAt` exceeds the cursor region length
340
- /// or `cur.i != ensureAt`.
341
- /// @param cur Cursor to check.
342
- /// @param ensureAt Relative offset that `cur.i` must match exactly.
343
- function ensure(Cur memory cur, uint ensureAt) internal pure {
344
- if (ensureAt > cur.len || cur.i != ensureAt) revert IncompleteCursor();
345
- }
346
-
347
- /// @notice Assert completion and finalise a writer in one step.
348
- /// @param cur Cursor to check.
349
- /// @param writer Writer to finalise.
350
- /// @return Trimmed output bytes from the writer.
351
- function complete(Cur memory cur, Writer memory writer) internal pure returns (bytes memory) {
352
- if (cur.bound == 0 || cur.i != cur.bound) revert IncompleteCursor();
353
- return Writers.finish(writer);
354
- }
355
-
356
398
  // -------------------------------------------------------------------------
357
399
  // Block factory helpers
358
400
  // -------------------------------------------------------------------------
359
401
 
402
+ /// @notice Encode a block with a raw payload.
403
+ /// @param key Block type key.
404
+ /// @param data Raw payload bytes.
405
+ /// @return Encoded block bytes.
406
+ function createBlock(bytes4 key, bytes memory data) internal pure returns (bytes memory) {
407
+ return bytes.concat(key, bytes4(uint32(data.length)), data);
408
+ }
409
+
360
410
  /// @notice Encode a block with a single 32-byte payload word.
361
411
  /// @param key Block type key.
362
412
  /// @param value 32-byte payload.
@@ -420,28 +470,11 @@ library Cursors {
420
470
  return bytes.concat(key, bytes4(uint32(0xa0)), a, b, c, d, e);
421
471
  }
422
472
 
423
- /// @notice Encode a block with a 32-byte fixed head followed by a variable-length tail.
424
- /// @param key Block type key.
425
- /// @param head Fixed 32-byte head payload.
426
- /// @param tail Variable-length payload bytes appended after the head.
427
- /// @return Encoded block bytes.
428
- function createBlockHead32(bytes4 key, bytes32 head, bytes memory tail) internal pure returns (bytes memory) {
429
- return bytes.concat(key, bytes4(uint32(0x20 + tail.length)), head, tail);
430
- }
431
-
432
- /// @notice Encode a block with a 64-byte fixed head followed by a variable-length tail.
433
- /// @param key Block type key.
434
- /// @param a First fixed payload word.
435
- /// @param b Second fixed payload word.
436
- /// @param tail Variable-length payload bytes appended after the fixed head.
437
- /// @return Encoded block bytes.
438
- function createBlockHead64(
439
- bytes4 key,
440
- bytes32 a,
441
- bytes32 b,
442
- bytes memory tail
443
- ) internal pure returns (bytes memory) {
444
- return bytes.concat(key, bytes4(uint32(0x40 + tail.length)), a, b, tail);
473
+ /// @notice Encode a BYTES block with a raw payload.
474
+ /// @param data Raw payload bytes.
475
+ /// @return Encoded BYTES block bytes.
476
+ function toBytesBlock(bytes memory data) internal pure returns (bytes memory) {
477
+ return createBlock(Keys.Bytes, data);
445
478
  }
446
479
 
447
480
  /// @notice Encode a BOUNTY block.
@@ -452,24 +485,6 @@ library Cursors {
452
485
  return createBlock64(Keys.Bounty, bytes32(bounty), relayer);
453
486
  }
454
487
 
455
- /// @notice Encode a STEP block.
456
- /// @param target Command target identifier.
457
- /// @param value Native value forwarded with the step.
458
- /// @param request Variable-length nested request payload.
459
- /// @return Encoded STEP block bytes.
460
- function toStepBlock(uint target, uint value, bytes memory request) internal pure returns (bytes memory) {
461
- return createBlockHead64(Keys.Step, bytes32(target), bytes32(value), request);
462
- }
463
-
464
- /// @notice Encode a CALL block.
465
- /// @param target Target node identifier.
466
- /// @param value Native value forwarded with the call.
467
- /// @param data Raw calldata payload for the target.
468
- /// @return Encoded CALL block bytes.
469
- function toCallBlock(uint target, uint value, bytes memory data) internal pure returns (bytes memory) {
470
- return createBlockHead64(Keys.Call, bytes32(target), bytes32(value), data);
471
- }
472
-
473
488
  /// @notice Encode a BALANCE block.
474
489
  /// @param asset Asset identifier.
475
490
  /// @param meta Asset metadata slot.
@@ -485,87 +500,121 @@ library Cursors {
485
500
  /// @param meta Asset metadata slot.
486
501
  /// @param amount Token amount.
487
502
  /// @return Encoded CUSTODY block bytes.
488
- function toCustodyBlock(
489
- uint host,
490
- bytes32 asset,
491
- bytes32 meta,
492
- uint amount
493
- ) internal pure returns (bytes memory) {
503
+ function toCustodyBlock(uint host, bytes32 asset, bytes32 meta, uint amount) internal pure returns (bytes memory) {
494
504
  return createBlock128(Keys.Custody, bytes32(host), asset, meta, bytes32(amount));
495
505
  }
496
506
 
507
+ /// @notice Encode a STEP block.
508
+ /// @param target Command target identifier.
509
+ /// @param value Native value forwarded with the step.
510
+ /// @param request Raw nested request payload.
511
+ /// @return Encoded STEP block bytes.
512
+ function toStepBlock(uint target, uint value, bytes memory request) internal pure returns (bytes memory) {
513
+ return createBlock(Keys.Step, bytes.concat(bytes32(target), bytes32(value), toBytesBlock(request)));
514
+ }
515
+
516
+ /// @notice Encode a CALL block.
517
+ /// @param target Target node identifier.
518
+ /// @param value Native value forwarded with the call.
519
+ /// @param data Raw calldata payload for the target.
520
+ /// @return Encoded CALL block bytes.
521
+ function toCallBlock(uint target, uint value, bytes memory data) internal pure returns (bytes memory) {
522
+ return createBlock(Keys.Call, bytes.concat(bytes32(target), bytes32(value), toBytesBlock(data)));
523
+ }
524
+
525
+ /// @notice Encode a CONTEXT block.
526
+ /// @param account Command account identifier.
527
+ /// @param value Native value budget assigned to the context.
528
+ /// @param state Embedded state block stream.
529
+ /// @param request Embedded request block stream.
530
+ /// @return Encoded CONTEXT block bytes.
531
+ function toContextBlock(
532
+ bytes32 account,
533
+ uint value,
534
+ bytes memory state,
535
+ bytes memory request
536
+ ) internal pure returns (bytes memory) {
537
+ return createBlock(Keys.Context, bytes.concat(account, bytes32(value), toBytesBlock(state), toBytesBlock(request)));
538
+ }
539
+
497
540
  // -------------------------------------------------------------------------
498
541
  // Raw calldata loaders
499
542
  // -------------------------------------------------------------------------
500
543
 
501
- /// @notice Load one 32-byte word from calldata.
502
- /// @dev Performs no bounds, key, length, or cursor checks.
503
- /// @param abs Absolute calldata offset of the word start.
504
- /// @return a Loaded word.
505
- function load32(uint abs) internal pure returns (bytes32 a) {
544
+ /// @notice Read the next calldata word from the cursor and advance by `n` bytes.
545
+ /// @dev Performs no bounds, key, length, or cursor checks. Always loads 32 bytes;
546
+ /// callers may cast the returned word to `bytesN` when `n < 32`.
547
+ /// @param cur Cursor whose current position is advanced by `n` bytes.
548
+ /// @param n Number of bytes to advance.
549
+ /// @return value Loaded word.
550
+ function read(Cur memory cur, uint n) internal pure returns (bytes32 value) {
551
+ uint abs = cur.offset + cur.i;
506
552
  assembly ("memory-safe") {
507
- a := calldataload(abs)
553
+ value := calldataload(abs)
508
554
  }
555
+ cur.i += n;
509
556
  }
510
557
 
511
- /// @notice Load two 32-byte words from calldata.
558
+ /// @notice Read the next 16 bytes from the cursor and advance by 16 bytes.
512
559
  /// @dev Performs no bounds, key, length, or cursor checks.
513
- /// @param abs Absolute calldata offset of the first word.
514
- /// @return a First loaded word.
515
- /// @return b Second loaded word.
516
- function load64(uint abs) internal pure returns (bytes32 a, bytes32 b) {
560
+ /// @param cur Cursor whose current position is advanced by 16 bytes.
561
+ /// @return value Loaded bytes16 value.
562
+ function read16(Cur memory cur) internal pure returns (bytes16 value) {
563
+ uint abs = cur.offset + cur.i;
517
564
  assembly ("memory-safe") {
518
- a := calldataload(abs)
519
- b := calldataload(add(abs, 0x20))
565
+ value := calldataload(abs)
520
566
  }
567
+ cur.i += 16;
521
568
  }
522
569
 
523
- /// @notice Load three 32-byte words from calldata.
570
+ /// @notice Read the next 32-byte word from the cursor and advance by one word.
524
571
  /// @dev Performs no bounds, key, length, or cursor checks.
525
- /// @param abs Absolute calldata offset of the first word.
526
- /// @return a First loaded word.
527
- /// @return b Second loaded word.
528
- /// @return c Third loaded word.
529
- function load96(uint abs) internal pure returns (bytes32 a, bytes32 b, bytes32 c) {
572
+ /// @param cur Cursor whose current position is advanced by 32 bytes.
573
+ /// @return value Loaded word.
574
+ function read32(Cur memory cur) internal pure returns (bytes32 value) {
575
+ uint abs = cur.offset + cur.i;
530
576
  assembly ("memory-safe") {
531
- a := calldataload(abs)
532
- b := calldataload(add(abs, 0x20))
533
- c := calldataload(add(abs, 0x40))
577
+ value := calldataload(abs)
534
578
  }
579
+ cur.i += 32;
535
580
  }
536
581
 
537
- /// @notice Load four 32-byte words from calldata.
582
+ /// @notice Read the next two 32-byte words from the cursor and advance by 64 bytes.
538
583
  /// @dev Performs no bounds, key, length, or cursor checks.
539
- /// @param abs Absolute calldata offset of the first word.
584
+ /// @param cur Cursor whose current position is advanced by 64 bytes.
540
585
  /// @return a First loaded word.
541
586
  /// @return b Second loaded word.
542
- /// @return c Third loaded word.
543
- /// @return d Fourth loaded word.
544
- function load128(uint abs) internal pure returns (bytes32 a, bytes32 b, bytes32 c, bytes32 d) {
587
+ function read64(Cur memory cur) internal pure returns (bytes32 a, bytes32 b) {
588
+ uint abs = cur.offset + cur.i;
545
589
  assembly ("memory-safe") {
546
590
  a := calldataload(abs)
547
591
  b := calldataload(add(abs, 0x20))
548
- c := calldataload(add(abs, 0x40))
549
- d := calldataload(add(abs, 0x60))
550
592
  }
593
+ cur.i += 64;
551
594
  }
552
595
 
553
- /// @notice Load five 32-byte words from calldata.
596
+ /// @notice Read the next three 32-byte words from the cursor and advance by 96 bytes.
554
597
  /// @dev Performs no bounds, key, length, or cursor checks.
555
- /// @param abs Absolute calldata offset of the first word.
598
+ /// @param cur Cursor whose current position is advanced by 96 bytes.
556
599
  /// @return a First loaded word.
557
600
  /// @return b Second loaded word.
558
601
  /// @return c Third loaded word.
559
- /// @return d Fourth loaded word.
560
- /// @return e Fifth loaded word.
561
- function load160(uint abs) internal pure returns (bytes32 a, bytes32 b, bytes32 c, bytes32 d, bytes32 e) {
602
+ function read96(Cur memory cur) internal pure returns (bytes32 a, bytes32 b, bytes32 c) {
603
+ uint abs = cur.offset + cur.i;
562
604
  assembly ("memory-safe") {
563
605
  a := calldataload(abs)
564
606
  b := calldataload(add(abs, 0x20))
565
607
  c := calldataload(add(abs, 0x40))
566
- d := calldataload(add(abs, 0x60))
567
- e := calldataload(add(abs, 0x80))
568
608
  }
609
+ cur.i += 96;
610
+ }
611
+
612
+ /// @notice Consume the next 32-byte word and require it to match `expected`.
613
+ /// @dev Performs no bounds, key, length, or cursor checks beyond the value comparison.
614
+ /// @param cur Cursor whose current position is advanced by 32 bytes.
615
+ /// @param expected Required word value.
616
+ function require32(Cur memory cur, bytes32 expected) internal pure {
617
+ if (read32(cur) != expected) revert UnexpectedValue();
569
618
  }
570
619
 
571
620
  // -------------------------------------------------------------------------
@@ -580,17 +629,24 @@ library Cursors {
580
629
  /// @param key Expected dynamic block key.
581
630
  /// @return data Raw block payload bytes.
582
631
  function unpackRaw(Cur memory cur, bytes4 key) internal pure returns (bytes calldata data) {
583
- (uint abs, uint next) = expect(cur, cur.i, key, 0, 0);
632
+ (uint abs, uint next) = expect(cur, cur.i, 0, key, 0, 0);
584
633
  data = msg.data[abs:cur.offset + next];
585
634
  cur.i = next;
586
635
  }
587
636
 
637
+ /// @notice Consume a reserved BYTES block and return its raw payload.
638
+ /// @param cur Cursor; advanced past the BYTES block.
639
+ /// @return data Raw BYTES payload.
640
+ function unpackBytes(Cur memory cur) internal pure returns (bytes calldata data) {
641
+ return unpackRaw(cur, Keys.Bytes);
642
+ }
643
+
588
644
  /// @notice Consume a dynamic block with a single bytes32 payload.
589
645
  /// @param cur Cursor; advanced past the block.
590
646
  /// @param key Expected dynamic block key.
591
647
  /// @return value Decoded bytes32.
592
648
  function unpack32(Cur memory cur, bytes4 key) internal pure returns (bytes32 value) {
593
- uint abs = consume(cur, key, 32, 32);
649
+ uint abs = consume(cur, 0, key, 32, 32);
594
650
  value = bytes32(msg.data[abs:abs + 32]);
595
651
  }
596
652
 
@@ -600,7 +656,7 @@ library Cursors {
600
656
  /// @return a First decoded bytes32.
601
657
  /// @return b Second decoded bytes32.
602
658
  function unpack64(Cur memory cur, bytes4 key) internal pure returns (bytes32 a, bytes32 b) {
603
- uint abs = consume(cur, key, 64, 64);
659
+ uint abs = consume(cur, 0, key, 64, 64);
604
660
  a = bytes32(msg.data[abs:abs + 32]);
605
661
  b = bytes32(msg.data[abs + 32:abs + 64]);
606
662
  }
@@ -612,7 +668,7 @@ library Cursors {
612
668
  /// @return b Second decoded bytes32.
613
669
  /// @return c Third decoded bytes32.
614
670
  function unpack96(Cur memory cur, bytes4 key) internal pure returns (bytes32 a, bytes32 b, bytes32 c) {
615
- uint abs = consume(cur, key, 96, 96);
671
+ uint abs = consume(cur, 0, key, 96, 96);
616
672
  a = bytes32(msg.data[abs:abs + 32]);
617
673
  b = bytes32(msg.data[abs + 32:abs + 64]);
618
674
  c = bytes32(msg.data[abs + 64:abs + 96]);
@@ -625,11 +681,8 @@ library Cursors {
625
681
  /// @return b Second decoded bytes32.
626
682
  /// @return c Third decoded bytes32.
627
683
  /// @return d Fourth decoded bytes32.
628
- function unpack128(
629
- Cur memory cur,
630
- bytes4 key
631
- ) internal pure returns (bytes32 a, bytes32 b, bytes32 c, bytes32 d) {
632
- uint abs = consume(cur, key, 128, 128);
684
+ function unpack128(Cur memory cur, bytes4 key) internal pure returns (bytes32 a, bytes32 b, bytes32 c, bytes32 d) {
685
+ uint abs = consume(cur, 0, key, 128, 128);
633
686
  a = bytes32(msg.data[abs:abs + 32]);
634
687
  b = bytes32(msg.data[abs + 32:abs + 64]);
635
688
  c = bytes32(msg.data[abs + 64:abs + 96]);
@@ -648,7 +701,7 @@ library Cursors {
648
701
  Cur memory cur,
649
702
  bytes4 key
650
703
  ) internal pure returns (bytes32 a, bytes32 b, bytes32 c, bytes32 d, bytes32 e) {
651
- uint abs = consume(cur, key, 160, 160);
704
+ uint abs = consume(cur, 0, key, 160, 160);
652
705
  a = bytes32(msg.data[abs:abs + 32]);
653
706
  b = bytes32(msg.data[abs + 32:abs + 64]);
654
707
  c = bytes32(msg.data[abs + 64:abs + 96]);
@@ -656,102 +709,6 @@ library Cursors {
656
709
  e = bytes32(msg.data[abs + 128:abs + 160]);
657
710
  }
658
711
 
659
- /// @notice Consume a dynamic block with a single uint payload.
660
- /// @param cur Cursor; advanced past the block.
661
- /// @param key Expected dynamic block key.
662
- /// @return value Decoded uint value.
663
- function unpackUint(Cur memory cur, bytes4 key) internal pure returns (uint value) {
664
- value = uint(unpack32(cur, key));
665
- }
666
-
667
- /// @notice Consume a dynamic block with two uint payload words.
668
- /// @param cur Cursor; advanced past the block.
669
- /// @param key Expected dynamic block key.
670
- /// @return a First decoded uint.
671
- /// @return b Second decoded uint.
672
- function unpack2Uint(Cur memory cur, bytes4 key) internal pure returns (uint a, uint b) {
673
- (bytes32 x, bytes32 y) = unpack64(cur, key);
674
- return (uint(x), uint(y));
675
- }
676
-
677
- /// @notice Consume a dynamic block with three uint payload words.
678
- /// @param cur Cursor; advanced past the block.
679
- /// @param key Expected dynamic block key.
680
- /// @return a First decoded uint.
681
- /// @return b Second decoded uint.
682
- /// @return c Third decoded uint.
683
- function unpack3Uint(Cur memory cur, bytes4 key) internal pure returns (uint a, uint b, uint c) {
684
- (bytes32 x, bytes32 y, bytes32 z) = unpack96(cur, key);
685
- return (uint(x), uint(y), uint(z));
686
- }
687
-
688
- // Generic fixed-head decoders
689
-
690
- /// @notice Consume a dynamic block with a 32-byte fixed head followed by a variable-length tail.
691
- /// @param cur Cursor; advanced past the block.
692
- /// @param key Expected dynamic block key.
693
- /// @return head Fixed 32-byte head.
694
- /// @return tail Variable-length payload bytes after the fixed head.
695
- function unpackHead32(Cur memory cur, bytes4 key) internal pure returns (bytes32 head, bytes calldata tail) {
696
- uint abs = consume(cur, key, 32, 0);
697
- head = bytes32(msg.data[abs:abs + 32]);
698
- tail = msg.data[abs + 32:cur.offset + cur.i];
699
- }
700
-
701
- /// @notice Consume a dynamic block with a 64-byte fixed head followed by a variable-length tail.
702
- /// @param cur Cursor; advanced past the block.
703
- /// @param key Expected dynamic block key.
704
- /// @return a First fixed head word.
705
- /// @return b Second fixed head word.
706
- /// @return tail Variable-length payload bytes after the fixed head.
707
- function unpackHead64(
708
- Cur memory cur,
709
- bytes4 key
710
- ) internal pure returns (bytes32 a, bytes32 b, bytes calldata tail) {
711
- uint abs = consume(cur, key, 64, 0);
712
- a = bytes32(msg.data[abs:abs + 32]);
713
- b = bytes32(msg.data[abs + 32:abs + 64]);
714
- tail = msg.data[abs + 64:cur.offset + cur.i];
715
- }
716
-
717
- /// @notice Consume a dynamic block with a 96-byte fixed head followed by a variable-length tail.
718
- /// @param cur Cursor; advanced past the block.
719
- /// @param key Expected dynamic block key.
720
- /// @return a First fixed head word.
721
- /// @return b Second fixed head word.
722
- /// @return c Third fixed head word.
723
- /// @return tail Variable-length payload bytes after the fixed head.
724
- function unpackHead96(
725
- Cur memory cur,
726
- bytes4 key
727
- ) internal pure returns (bytes32 a, bytes32 b, bytes32 c, bytes calldata tail) {
728
- uint abs = consume(cur, key, 96, 0);
729
- a = bytes32(msg.data[abs:abs + 32]);
730
- b = bytes32(msg.data[abs + 32:abs + 64]);
731
- c = bytes32(msg.data[abs + 64:abs + 96]);
732
- tail = msg.data[abs + 96:cur.offset + cur.i];
733
- }
734
-
735
- /// @notice Consume a dynamic block with a 128-byte fixed head followed by a variable-length tail.
736
- /// @param cur Cursor; advanced past the block.
737
- /// @param key Expected dynamic block key.
738
- /// @return a First fixed head word.
739
- /// @return b Second fixed head word.
740
- /// @return c Third fixed head word.
741
- /// @return d Fourth fixed head word.
742
- /// @return tail Variable-length payload bytes after the fixed head.
743
- function unpackHead128(
744
- Cur memory cur,
745
- bytes4 key
746
- ) internal pure returns (bytes32 a, bytes32 b, bytes32 c, bytes32 d, bytes calldata tail) {
747
- uint abs = consume(cur, key, 128, 0);
748
- a = bytes32(msg.data[abs:abs + 32]);
749
- b = bytes32(msg.data[abs + 32:abs + 64]);
750
- c = bytes32(msg.data[abs + 64:abs + 96]);
751
- d = bytes32(msg.data[abs + 96:abs + 128]);
752
- tail = msg.data[abs + 128:cur.offset + cur.i];
753
- }
754
-
755
712
  // Generic typed-shape decoders
756
713
 
757
714
  /// @notice Consume a fixed-size asset amount block and return asset, meta, and amount.
@@ -764,7 +721,7 @@ library Cursors {
764
721
  Cur memory cur,
765
722
  bytes4 key
766
723
  ) internal pure returns (bytes32 asset, bytes32 meta, uint amount) {
767
- uint abs = consume(cur, key, 96, 96);
724
+ uint abs = consume(cur, 0, key, 96, 96);
768
725
  asset = bytes32(msg.data[abs:abs + 32]);
769
726
  meta = bytes32(msg.data[abs + 32:abs + 64]);
770
727
  amount = uint(bytes32(msg.data[abs + 64:abs + 96]));
@@ -781,7 +738,7 @@ library Cursors {
781
738
  Cur memory cur,
782
739
  bytes4 key
783
740
  ) internal pure returns (bytes32 account, bytes32 asset, bytes32 meta, uint amount) {
784
- uint abs = consume(cur, key, 128, 128);
741
+ uint abs = consume(cur, 0, key, 128, 128);
785
742
  account = bytes32(msg.data[abs:abs + 32]);
786
743
  asset = bytes32(msg.data[abs + 32:abs + 64]);
787
744
  meta = bytes32(msg.data[abs + 64:abs + 96]);
@@ -799,7 +756,7 @@ library Cursors {
799
756
  Cur memory cur,
800
757
  bytes4 key
801
758
  ) internal pure returns (uint host, bytes32 asset, bytes32 meta, uint amount) {
802
- uint abs = consume(cur, key, 128, 128);
759
+ uint abs = consume(cur, 0, key, 128, 128);
803
760
  host = uint(bytes32(msg.data[abs:abs + 32]));
804
761
  asset = bytes32(msg.data[abs + 32:abs + 64]);
805
762
  meta = bytes32(msg.data[abs + 64:abs + 96]);
@@ -817,7 +774,7 @@ library Cursors {
817
774
  Cur memory cur,
818
775
  bytes4 key
819
776
  ) internal pure returns (uint host, bytes32 account, bytes32 asset, bytes32 meta) {
820
- uint abs = consume(cur, key, 128, 128);
777
+ uint abs = consume(cur, 0, key, 128, 128);
821
778
  host = uint(bytes32(msg.data[abs:abs + 32]));
822
779
  account = bytes32(msg.data[abs + 32:abs + 64]);
823
780
  asset = bytes32(msg.data[abs + 64:abs + 96]);
@@ -836,7 +793,7 @@ library Cursors {
836
793
  Cur memory cur,
837
794
  bytes4 key
838
795
  ) internal pure returns (bytes32 from, bytes32 to, bytes32 asset, bytes32 meta, uint amount) {
839
- uint abs = consume(cur, key, 160, 160);
796
+ uint abs = consume(cur, 0, key, 160, 160);
840
797
  from = bytes32(msg.data[abs:abs + 32]);
841
798
  to = bytes32(msg.data[abs + 32:abs + 64]);
842
799
  asset = bytes32(msg.data[abs + 64:abs + 96]);
@@ -860,20 +817,6 @@ library Cursors {
860
817
  node = uint(unpack32(cur, Keys.Node));
861
818
  }
862
819
 
863
- /// @notice Consume a RATE block and return the value.
864
- /// @param cur Cursor; advanced past the block.
865
- /// @return value Encoded ratio or rate.
866
- function unpackRate(Cur memory cur) internal pure returns (uint value) {
867
- value = uint(unpack32(cur, Keys.Rate));
868
- }
869
-
870
- /// @notice Consume a QUANTITY block and return the amount.
871
- /// @param cur Cursor; advanced past the block.
872
- /// @return amount Scalar quantity value.
873
- function unpackQuantity(Cur memory cur) internal pure returns (uint amount) {
874
- amount = uint(unpack32(cur, Keys.Quantity));
875
- }
876
-
877
820
  /// @notice Consume a FEE block and return the amount.
878
821
  /// @param cur Cursor; advanced past the block.
879
822
  /// @return amount Fee amount.
@@ -895,7 +838,7 @@ library Cursors {
895
838
  /// @return asset Asset identifier.
896
839
  /// @return meta Asset metadata slot.
897
840
  function unpackAccountAsset(Cur memory cur) internal pure returns (bytes32 account, bytes32 asset, bytes32 meta) {
898
- uint abs = consume(cur, Keys.AccountAsset, 96, 96);
841
+ uint abs = consume(cur, 0, Keys.AccountAsset, 96, 96);
899
842
  account = bytes32(msg.data[abs:abs + 32]);
900
843
  asset = bytes32(msg.data[abs + 32:abs + 64]);
901
844
  meta = bytes32(msg.data[abs + 64:abs + 96]);
@@ -918,18 +861,6 @@ library Cursors {
918
861
  relayer = y;
919
862
  }
920
863
 
921
- /// @notice Consume a BOUNDS block and return the signed min and max values.
922
- /// @param cur Cursor; advanced past the block.
923
- /// @return min Lower signed bound.
924
- /// @return max Upper signed bound.
925
- function unpackBounds(Cur memory cur) internal pure returns (int min, int max) {
926
- uint abs = consume(cur, Keys.Bounds, 64, 64);
927
- assembly ("memory-safe") {
928
- min := calldataload(abs)
929
- max := calldataload(add(abs, 0x20))
930
- }
931
- }
932
-
933
864
  /// @notice Consume an AMOUNT block and return its fields as separate values.
934
865
  /// @param cur Cursor; advanced past the block.
935
866
  /// @return asset Asset identifier.
@@ -1095,9 +1026,7 @@ library Cursors {
1095
1026
  /// @return asset Asset identifier.
1096
1027
  /// @return meta Asset metadata slot.
1097
1028
  /// @return amount Token amount.
1098
- function unpackCustody(
1099
- Cur memory cur
1100
- ) internal pure returns (uint host, bytes32 asset, bytes32 meta, uint amount) {
1029
+ function unpackCustody(Cur memory cur) internal pure returns (uint host, bytes32 asset, bytes32 meta, uint amount) {
1101
1030
  return unpackHostAmount(cur, Keys.Custody);
1102
1031
  }
1103
1032
 
@@ -1131,25 +1060,49 @@ library Cursors {
1131
1060
  // Type-specific dynamic decoders
1132
1061
 
1133
1062
  /// @notice Consume a STEP block and return its sub-command invocation fields.
1134
- /// The `req` slice covers any additional payload bytes after the fixed head.
1063
+ /// The `req` slice is the raw payload of the block's required BYTES child.
1135
1064
  /// @param cur Cursor; advanced past the block.
1136
1065
  /// @return target Destination node ID for the sub-command.
1137
1066
  /// @return value Native value to forward with the call.
1138
1067
  /// @return req Embedded request bytes for the sub-command.
1139
1068
  function unpackStep(Cur memory cur) internal pure returns (uint target, uint value, bytes calldata req) {
1140
- (bytes32 a, bytes32 b, bytes calldata tail) = unpackHead64(cur, Keys.Step);
1141
- return (uint(a), uint(b), tail);
1069
+ uint end = cur.enter(Keys.Step, 64 + Sizes.Header, 0);
1070
+ target = uint(cur.read32());
1071
+ value = uint(cur.read32());
1072
+ req = cur.unpackBytes();
1073
+ cur.exit(end);
1142
1074
  }
1143
1075
 
1144
1076
  /// @notice Consume a CALL block and return its target invocation fields.
1145
- /// The `data` slice covers any additional payload bytes after the fixed head.
1077
+ /// The `data` slice is the raw payload of the block's required BYTES child.
1146
1078
  /// @param cur Cursor; advanced past the block.
1147
1079
  /// @return target Target node ID to call.
1148
1080
  /// @return value Native value to forward with the call.
1149
1081
  /// @return data Raw calldata payload for the target.
1150
1082
  function unpackCall(Cur memory cur) internal pure returns (uint target, uint value, bytes calldata data) {
1151
- (bytes32 a, bytes32 b, bytes calldata tail) = unpackHead64(cur, Keys.Call);
1152
- return (uint(a), uint(b), tail);
1083
+ uint end = cur.enter(Keys.Call, 64 + Sizes.Header, 0);
1084
+ target = uint(cur.read32());
1085
+ value = uint(cur.read32());
1086
+ data = cur.unpackBytes();
1087
+ cur.exit(end);
1088
+ }
1089
+
1090
+ /// @notice Consume a CONTEXT block and return its command context fields.
1091
+ /// The `state` and `request` slices are the raw payloads of the required BYTES children.
1092
+ /// @param cur Cursor; advanced past the block.
1093
+ /// @return account Command account identifier.
1094
+ /// @return value Native value budget assigned to the context.
1095
+ /// @return state Embedded state block stream.
1096
+ /// @return request Embedded request block stream.
1097
+ function unpackContext(
1098
+ Cur memory cur
1099
+ ) internal pure returns (bytes32 account, uint value, bytes calldata state, bytes calldata request) {
1100
+ uint end = cur.enter(Keys.Context, 64 + 2 * Sizes.Header, 0);
1101
+ account = cur.read32();
1102
+ value = uint(cur.read32());
1103
+ state = cur.unpackBytes();
1104
+ request = cur.unpackBytes();
1105
+ cur.exit(end);
1153
1106
  }
1154
1107
 
1155
1108
  // Type-specific validators
@@ -1162,10 +1115,12 @@ library Cursors {
1162
1115
  /// @return deadline Expiry timestamp.
1163
1116
  /// @return proof Raw proof bytes (layout: `[bytes20 signer][bytes65 sig]`).
1164
1117
  function expectAuth(Cur memory cur, uint i, uint cid) internal pure returns (uint deadline, bytes calldata proof) {
1165
- (uint abs, uint next) = expect(cur, i, Keys.Auth, 149, 0);
1118
+ (uint abs, uint next) = expect(cur, i, 0, Keys.Auth, 64 + Sizes.Header + Sizes.Proof, 0);
1166
1119
  if (uint(bytes32(msg.data[abs:abs + 32])) != cid) revert UnexpectedValue();
1167
1120
  deadline = uint(bytes32(msg.data[abs + 32:abs + 64]));
1168
- proof = msg.data[abs + 64:cur.offset + next];
1121
+
1122
+ (abs, ) = expect(cur, i + Sizes.Header + 64, next, Keys.Bytes, Sizes.Proof, Sizes.Proof);
1123
+ proof = msg.data[abs:abs + Sizes.Proof];
1169
1124
  }
1170
1125
 
1171
1126
  // -------------------------------------------------------------------------
@@ -1183,7 +1138,7 @@ library Cursors {
1183
1138
  bytes4 key,
1184
1139
  bytes32 asset
1185
1140
  ) internal pure returns (bytes32 meta, uint amount) {
1186
- uint abs = consume(cur, key, 96, 96);
1141
+ uint abs = consume(cur, 0, key, 96, 96);
1187
1142
  if (bytes32(msg.data[abs:abs + 32]) != asset) revert UnexpectedValue();
1188
1143
  meta = bytes32(msg.data[abs + 32:abs + 64]);
1189
1144
  amount = uint(bytes32(msg.data[abs + 64:abs + 96]));
@@ -1201,7 +1156,7 @@ library Cursors {
1201
1156
  bytes32 asset,
1202
1157
  bytes32 meta
1203
1158
  ) internal pure returns (uint amount) {
1204
- uint abs = consume(cur, key, 96, 96);
1159
+ uint abs = consume(cur, 0, key, 96, 96);
1205
1160
  if (bytes32(msg.data[abs:abs + 32]) != asset) revert UnexpectedValue();
1206
1161
  if (bytes32(msg.data[abs + 32:abs + 64]) != meta) revert UnexpectedValue();
1207
1162
  amount = uint(bytes32(msg.data[abs + 64:abs + 96]));
@@ -1213,7 +1168,7 @@ library Cursors {
1213
1168
  /// @param asset Expected asset identifier.
1214
1169
  /// @return meta Metadata slot from the block.
1215
1170
  function requireUnitAssetAmount(Cur memory cur, bytes4 key, bytes32 asset) internal pure returns (bytes32 meta) {
1216
- uint abs = consume(cur, key, 96, 96);
1171
+ uint abs = consume(cur, 0, key, 96, 96);
1217
1172
  if (bytes32(msg.data[abs:abs + 32]) != asset) revert UnexpectedValue();
1218
1173
  meta = bytes32(msg.data[abs + 32:abs + 64]);
1219
1174
  if (uint(bytes32(msg.data[abs + 64:abs + 96])) != 1) revert UnexpectedValue();
@@ -1240,7 +1195,7 @@ library Cursors {
1240
1195
  bytes4 key,
1241
1196
  uint host
1242
1197
  ) internal pure returns (bytes32 asset, bytes32 meta, uint amount) {
1243
- uint abs = consume(cur, key, 128, 128);
1198
+ uint abs = consume(cur, 0, key, 128, 128);
1244
1199
  if (uint(bytes32(msg.data[abs:abs + 32])) != host) revert UnexpectedValue();
1245
1200
  asset = bytes32(msg.data[abs + 32:abs + 64]);
1246
1201
  meta = bytes32(msg.data[abs + 64:abs + 96]);
@@ -1260,7 +1215,7 @@ library Cursors {
1260
1215
  uint host,
1261
1216
  bytes32 asset
1262
1217
  ) internal pure returns (bytes32 meta, uint amount) {
1263
- uint abs = consume(cur, key, 128, 128);
1218
+ uint abs = consume(cur, 0, key, 128, 128);
1264
1219
  if (uint(bytes32(msg.data[abs:abs + 32])) != host) revert UnexpectedValue();
1265
1220
  if (bytes32(msg.data[abs + 32:abs + 64]) != asset) revert UnexpectedValue();
1266
1221
  meta = bytes32(msg.data[abs + 64:abs + 96]);
@@ -1279,7 +1234,7 @@ library Cursors {
1279
1234
  uint host,
1280
1235
  bytes32 asset
1281
1236
  ) internal pure returns (bytes32 meta) {
1282
- uint abs = consume(cur, key, 128, 128);
1237
+ uint abs = consume(cur, 0, key, 128, 128);
1283
1238
  if (uint(bytes32(msg.data[abs:abs + 32])) != host) revert UnexpectedValue();
1284
1239
  if (bytes32(msg.data[abs + 32:abs + 64]) != asset) revert UnexpectedValue();
1285
1240
  meta = bytes32(msg.data[abs + 64:abs + 96]);
@@ -1299,7 +1254,7 @@ library Cursors {
1299
1254
  uint host,
1300
1255
  bytes32 account
1301
1256
  ) internal pure returns (bytes32 asset, bytes32 meta) {
1302
- uint abs = consume(cur, key, 128, 128);
1257
+ uint abs = consume(cur, 0, key, 128, 128);
1303
1258
  if (uint(bytes32(msg.data[abs:abs + 32])) != host) revert UnexpectedValue();
1304
1259
  if (bytes32(msg.data[abs + 32:abs + 64]) != account) revert UnexpectedValue();
1305
1260
  asset = bytes32(msg.data[abs + 64:abs + 96]);
@@ -1318,7 +1273,7 @@ library Cursors {
1318
1273
  bytes4 key,
1319
1274
  uint host
1320
1275
  ) internal pure returns (bytes32 account, bytes32 asset, bytes32 meta) {
1321
- uint abs = consume(cur, key, 128, 128);
1276
+ uint abs = consume(cur, 0, key, 128, 128);
1322
1277
  if (uint(bytes32(msg.data[abs:abs + 32])) != host) revert UnexpectedValue();
1323
1278
  account = bytes32(msg.data[abs + 32:abs + 64]);
1324
1279
  asset = bytes32(msg.data[abs + 64:abs + 96]);
@@ -1345,7 +1300,7 @@ library Cursors {
1345
1300
  /// @return proof Raw proof bytes.
1346
1301
  function requireAuth(Cur memory cur, uint cid) internal pure returns (uint deadline, bytes calldata proof) {
1347
1302
  (deadline, proof) = expectAuth(cur, cur.i, cid);
1348
- cur.i += Sizes.Header + 64 + proof.length;
1303
+ cur.i += Sizes.Auth;
1349
1304
  }
1350
1305
 
1351
1306
  // -------------------------------------------------------------------------
@@ -1362,7 +1317,7 @@ library Cursors {
1362
1317
  uint i = find(cur, 0, Keys.Node);
1363
1318
  if (i == cur.len) return backup;
1364
1319
 
1365
- (uint abs, ) = expect(cur, i, Keys.Node, 32, 32);
1320
+ (uint abs, ) = expect(cur, i, 0, Keys.Node, 32, 32);
1366
1321
  return uint(bytes32(msg.data[abs:abs + 32]));
1367
1322
  }
1368
1323
 
@@ -1386,7 +1341,7 @@ library Cursors {
1386
1341
  uint i = find(cur, 0, Keys.Account);
1387
1342
  if (i == cur.len) return backup;
1388
1343
 
1389
- (uint abs, ) = expect(cur, i, Keys.Account, 32, 32);
1344
+ (uint abs, ) = expect(cur, i, 0, Keys.Account, 32, 32);
1390
1345
  return bytes32(msg.data[abs:abs + 32]);
1391
1346
  }
1392
1347
 
@@ -1399,7 +1354,7 @@ library Cursors {
1399
1354
  uint i = find(cur, cur.bound, Keys.Node);
1400
1355
  if (i == cur.len) return backup;
1401
1356
 
1402
- (uint abs, ) = expect(cur, i, Keys.Node, 32, 32);
1357
+ (uint abs, ) = expect(cur, i, 0, Keys.Node, 32, 32);
1403
1358
  return uint(bytes32(msg.data[abs:abs + 32]));
1404
1359
  }
1405
1360
 
@@ -1412,7 +1367,7 @@ library Cursors {
1412
1367
  uint i = find(cur, cur.bound, Keys.Account);
1413
1368
  if (i == cur.len) return backup;
1414
1369
 
1415
- (uint abs, ) = expect(cur, i, Keys.Account, 32, 32);
1370
+ (uint abs, ) = expect(cur, i, 0, Keys.Account, 32, 32);
1416
1371
  return bytes32(msg.data[abs:abs + 32]);
1417
1372
  }
1418
1373
 
@@ -1422,19 +1377,19 @@ library Cursors {
1422
1377
  /// The signed slice covers from `cur.i` up to (but not including) the AUTH proof bytes.
1423
1378
  /// @param cur Source cursor; `bound` marks the end of the primary data region.
1424
1379
  /// @param cid Command ID that the signature must be bound to.
1425
- /// @return hash keccak256 of the signed message slice.
1380
+ /// @return digest keccak256 of the signed message slice.
1426
1381
  /// @return deadline Expiry timestamp from the AUTH block.
1427
1382
  /// @return proof Raw proof bytes (layout: `[bytes20 signer][bytes65 sig]`).
1428
1383
  function authLast(
1429
1384
  Cur memory cur,
1430
1385
  uint cid
1431
- ) internal pure returns (bytes32 hash, uint deadline, bytes calldata proof) {
1386
+ ) internal pure returns (bytes32 digest, uint deadline, bytes calldata proof) {
1432
1387
  if (cur.len - cur.i < Sizes.Auth) revert MalformedBlocks();
1433
1388
 
1434
1389
  uint i = cur.len - Sizes.Auth;
1435
1390
  if (i < cur.bound) revert MalformedBlocks();
1436
1391
 
1437
1392
  (deadline, proof) = expectAuth(cur, i, cid);
1438
- hash = keccak256(msg.data[cur.offset + cur.i:cur.offset + cur.len - Sizes.Proof]);
1393
+ digest = cur.hash(cur.i, cur.len - Sizes.Proof);
1439
1394
  }
1440
1395
  }