@rootzero/contracts 0.8.0 → 0.9.0

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.
Files changed (75) hide show
  1. package/Commands.sol +5 -10
  2. package/Core.sol +3 -4
  3. package/Cursors.sol +4 -6
  4. package/Events.sol +1 -1
  5. package/Queries.sol +1 -1
  6. package/README.md +18 -19
  7. package/Utils.sol +3 -3
  8. package/blocks/Cursors.sol +1437 -0
  9. package/blocks/Keys.sol +59 -34
  10. package/blocks/Schema.sol +109 -126
  11. package/blocks/Writers.sol +476 -301
  12. package/commands/Base.sol +32 -22
  13. package/commands/Burn.sol +3 -3
  14. package/commands/Credit.sol +6 -7
  15. package/commands/Debit.sol +3 -3
  16. package/commands/Deposit.sol +7 -7
  17. package/commands/Pipe.sol +3 -3
  18. package/commands/Provision.sol +19 -49
  19. package/commands/Transfer.sol +9 -19
  20. package/commands/Withdraw.sol +5 -6
  21. package/commands/admin/AllowAssets.sol +3 -3
  22. package/commands/admin/Allowance.sol +43 -0
  23. package/commands/admin/Authorize.sol +4 -4
  24. package/commands/admin/DenyAssets.sol +3 -3
  25. package/commands/admin/Destroy.sol +3 -3
  26. package/commands/admin/Execute.sol +38 -0
  27. package/commands/admin/Init.sol +3 -3
  28. package/commands/admin/Relocate.sol +5 -5
  29. package/commands/admin/Unauthorize.sol +4 -4
  30. package/core/Access.sol +38 -34
  31. package/core/Balances.sol +17 -18
  32. package/core/{Operation.sol → Calls.sol} +5 -8
  33. package/core/{CursorBase.sol → Context.sol} +11 -5
  34. package/core/Host.sol +10 -9
  35. package/core/Types.sol +86 -0
  36. package/docs/GETTING_STARTED.md +37 -29
  37. package/events/Asset.sol +1 -1
  38. package/events/Command.sol +10 -10
  39. package/events/Deposit.sol +3 -4
  40. package/events/Listing.sol +1 -1
  41. package/events/Peer.sol +3 -3
  42. package/events/Position.sol +21 -0
  43. package/events/Query.sol +3 -3
  44. package/events/Withdraw.sol +2 -3
  45. package/package.json +1 -1
  46. package/peer/AllowAssets.sol +1 -1
  47. package/peer/Allowance.sol +36 -0
  48. package/peer/AssetPull.sol +1 -1
  49. package/peer/Base.sol +8 -4
  50. package/peer/DenyAssets.sol +1 -1
  51. package/peer/Settle.sol +3 -4
  52. package/queries/Assets.sol +8 -8
  53. package/queries/Balances.sol +11 -11
  54. package/queries/Base.sol +2 -3
  55. package/queries/Positions.sol +25 -19
  56. package/utils/Accounts.sol +14 -13
  57. package/utils/Assets.sol +77 -57
  58. package/utils/Ids.sol +4 -4
  59. package/utils/Layout.sol +1 -1
  60. package/utils/Utils.sol +10 -0
  61. package/blocks/cursors/Core.sol +0 -1121
  62. package/blocks/cursors/Erc1155.sol +0 -149
  63. package/blocks/cursors/Erc20.sol +0 -130
  64. package/blocks/cursors/Erc721.sol +0 -66
  65. package/commands/Create.sol +0 -44
  66. package/commands/Remove.sol +0 -44
  67. package/commands/Settle.sol +0 -38
  68. package/commands/Stake.sol +0 -49
  69. package/commands/Supply.sol +0 -43
  70. package/commands/admin/Allocate.sol +0 -43
  71. package/core/HostBound.sol +0 -14
  72. package/events/Erc721.sol +0 -20
  73. package/peer/Pull.sol +0 -41
  74. package/peer/Push.sol +0 -47
  75. package/utils/State.sol +0 -22
@@ -0,0 +1,1437 @@
1
+ // SPDX-License-Identifier: GPL-3.0-only
2
+ pragma solidity ^0.8.33;
3
+
4
+ import {AssetAmount, AccountAsset, AccountAmount, HostAmount, HostAccountAsset, Tx} from "../core/Types.sol";
5
+ import {Sizes} from "./Schema.sol";
6
+ import {Keys} from "./Keys.sol";
7
+ import {ALLOC_SCALE, Writer, Writers} from "./Writers.sol";
8
+
9
+ /// @notice Zero-copy view into a calldata block stream.
10
+ /// All positions (`i`, `bound`) are byte offsets relative to the start of the source region.
11
+ /// The absolute calldata location of byte `i` is `offset + i`.
12
+ struct Cur {
13
+ /// @dev Absolute calldata byte offset of the source region start.
14
+ uint offset;
15
+ /// @dev Current read position, relative to the source start.
16
+ uint i;
17
+ /// @dev Total byte length of the source region.
18
+ uint len;
19
+ /// @dev Exclusive upper bound for the current iteration group, set by `primeRun`.
20
+ /// Zero until `primeRun` is called.
21
+ uint bound;
22
+ }
23
+
24
+ using Cursors for Cur;
25
+
26
+ /// @title Cursors
27
+ /// @notice Calldata block stream parser for the rootzero protocol.
28
+ /// A `Cur` is a lightweight view into a slice of `msg.data`; no data is copied.
29
+ /// Blocks are encoded as `[bytes4 key][bytes4 payloadLen][payload]`.
30
+ library Cursors {
31
+ /// @dev Source region contains a block whose declared length exceeds the region boundary,
32
+ /// or a header read would go out of bounds.
33
+ error MalformedBlocks();
34
+ /// @dev Current block key does not match the expected key, or payload size is out of range.
35
+ error InvalidBlock();
36
+ /// @dev `complete` called but the cursor has not consumed exactly up to `bound`.
37
+ error IncompleteCursor();
38
+ /// @dev `primeRun` found zero blocks of the expected key; the cursor region is empty.
39
+ error ZeroCursor();
40
+ /// @dev `primeRun` was called with a zero group size.
41
+ error ZeroGroup();
42
+ /// @dev An account field was required but the block or fallback was zero.
43
+ error ZeroAccount();
44
+ /// @dev A node field was required but the block or fallback was zero.
45
+ error ZeroNode();
46
+ /// @dev A field value did not match the expected value.
47
+ error UnexpectedValue();
48
+ /// @dev Input and output block counts are not proportional to their declared group sizes.
49
+ error BadRatio();
50
+ /// @dev A fixed-width low-level unpacker received an invalid final-word keep length.
51
+ error InvalidKeep();
52
+
53
+ // -------------------------------------------------------------------------
54
+ // Cursor construction and navigation
55
+ // -------------------------------------------------------------------------
56
+
57
+ /// @notice Create a cursor backed by a calldata slice.
58
+ /// @param source Calldata slice that forms the block stream.
59
+ /// @return cur Cursor positioned at the beginning of `source`.
60
+ function open(bytes calldata source) internal pure returns (Cur memory cur) {
61
+ uint offset;
62
+ // Extract the absolute calldata offset of `source` using inline assembly,
63
+ // as Solidity does not expose this directly for calldata slices.
64
+ assembly ("memory-safe") {
65
+ offset := source.offset
66
+ }
67
+ cur.offset = offset;
68
+ cur.len = source.length;
69
+ }
70
+
71
+ /// @notice Move the cursor to an absolute position within the source region.
72
+ /// @param cur Cursor to update.
73
+ /// @param i New read position (byte offset relative to source start).
74
+ /// @return Updated cursor with `cur.i == i`.
75
+ function seek(Cur memory cur, uint i) internal pure returns (Cur memory) {
76
+ if (i > cur.len) revert MalformedBlocks();
77
+ cur.i = i;
78
+ return cur;
79
+ }
80
+
81
+ /// @notice Create a subcursor over the half-open range `[from, to)` within the source region.
82
+ /// The returned cursor starts at position zero within that sliced region.
83
+ /// @param cur Source cursor.
84
+ /// @param from Start byte offset within the source region (inclusive).
85
+ /// @param to End byte offset within the source region (exclusive).
86
+ /// @return out Cursor scoped to the requested sub-range.
87
+ function slice(Cur memory cur, uint from, uint to) internal pure returns (Cur memory out) {
88
+ if (from > to || to > cur.len) revert MalformedBlocks();
89
+ out.offset = cur.offset + from;
90
+ out.len = to - from;
91
+ }
92
+
93
+ /// @notice Read a block header at position `i` without advancing the cursor.
94
+ /// @param cur Source cursor.
95
+ /// @param i Byte offset of the block header within the source region.
96
+ /// @return key Four-byte block type identifier.
97
+ /// @return len Payload byte length declared in the header.
98
+ function peek(Cur memory cur, uint i) internal pure returns (bytes4 key, uint len) {
99
+ if (i + 8 > cur.len) revert MalformedBlocks();
100
+ uint abs = cur.offset + i;
101
+ key = bytes4(msg.data[abs:abs + 4]);
102
+ len = uint32(bytes4(msg.data[abs + 4:abs + 8]));
103
+ if (i + 8 + len > cur.len) revert MalformedBlocks();
104
+ }
105
+
106
+ /// @notice Return the byte offset immediately past the block at the current cursor position.
107
+ /// Does not advance the cursor.
108
+ /// @param cur Source cursor.
109
+ /// @return Byte offset immediately past the current block, relative to the source region.
110
+ function past(Cur memory cur) internal pure returns (uint) {
111
+ (, uint len) = peek(cur, cur.i);
112
+ return cur.i + Sizes.Header + len;
113
+ }
114
+
115
+ /// @notice Return true if the current cursor position contains a block header with the given key.
116
+ /// Returns false when `cur.i` is out of bounds or the key differs.
117
+ /// @param cur Source cursor.
118
+ /// @param key Expected block type identifier.
119
+ /// @return Whether the block header at `cur.i` uses `key`.
120
+ function has(Cur memory cur, bytes4 key) internal pure returns (bool) {
121
+ if (cur.i + 8 > cur.len) return false;
122
+ uint abs = cur.offset + cur.i;
123
+ return bytes4(msg.data[abs:abs + 4]) == key;
124
+ }
125
+
126
+ /// @notice Validate a block at position `i` and return its payload location.
127
+ /// Does not advance the cursor.
128
+ /// @param cur Source cursor.
129
+ /// @param i Byte offset of the block within the source region.
130
+ /// @param key Expected block type key; reverts if actual key differs.
131
+ /// @param min Minimum acceptable payload length (inclusive).
132
+ /// @param max Maximum acceptable payload length (inclusive); 0 means unbounded.
133
+ /// @return abs Absolute calldata offset of the payload start.
134
+ /// @return next Byte offset of the block immediately following this one (relative to source start).
135
+ function expect(
136
+ Cur memory cur,
137
+ uint i,
138
+ bytes4 key,
139
+ uint min,
140
+ uint max
141
+ ) internal pure returns (uint abs, uint next) {
142
+ (bytes4 current, uint len) = peek(cur, i);
143
+ if (current != key) revert InvalidBlock();
144
+ abs = cur.offset + i + 8;
145
+ next = i + 8 + len;
146
+ if (len < min || (max != 0 && len > max)) revert InvalidBlock();
147
+ }
148
+
149
+ /// @notice Count consecutive blocks of the same key starting at `i`.
150
+ /// @param cur Source cursor.
151
+ /// @param i Starting byte offset within the source region.
152
+ /// @param key Block type to count.
153
+ /// @return total Number of consecutive matching blocks.
154
+ /// @return next Byte offset immediately after the last counted block.
155
+ function countRun(Cur memory cur, uint i, bytes4 key) internal pure returns (uint total, uint next) {
156
+ next = i;
157
+ while (next < cur.len) {
158
+ (bytes4 current, uint len) = peek(cur, next);
159
+ if (current != key) break;
160
+ next += 8 + len;
161
+
162
+ unchecked {
163
+ ++total;
164
+ }
165
+ }
166
+ }
167
+
168
+ /// @notice Initialise the cursor for a grouped iteration pass.
169
+ /// Reads the key of the first block, counts the consecutive run of that key,
170
+ /// stores the run end in `cur.bound`, validates that the count is a
171
+ /// multiple of `group`, and returns both the raw block count and the
172
+ /// normalized quotient (`count / group`).
173
+ /// @param cur Cursor to prime; `cur.bound` is updated in place.
174
+ /// @param group Expected group size (e.g. 1 for single-asset, 2 for paired input/output).
175
+ /// @return key Block type identifier of the run.
176
+ /// @return count Total number of blocks in the run (always a multiple of `group`).
177
+ /// @return quotient Number of groups represented by the run (`count / group`).
178
+ function primeRun(Cur memory cur, uint group) internal pure returns (bytes4 key, uint count, uint quotient) {
179
+ if (group == 0) revert ZeroGroup();
180
+ key = cur.i + 4 > cur.len ? bytes4(0) : bytes4(msg.data[cur.offset + cur.i:cur.offset + cur.i + 4]);
181
+ (count, cur.bound) = countRun(cur, cur.i, key);
182
+ if (count == 0) revert ZeroCursor();
183
+ if (count % group != 0) revert BadRatio();
184
+ quotient = count / group;
185
+ }
186
+
187
+ /// @notice Scan forward from `i` for the first block matching `key`.
188
+ /// @param cur Source cursor.
189
+ /// @param i Starting byte offset for the search.
190
+ /// @param key Block type to find.
191
+ /// @return Byte offset of the matching block, or `cur.len` if not found.
192
+ function find(Cur memory cur, uint i, bytes4 key) internal pure returns (uint) {
193
+ while (i < cur.len) {
194
+ (bytes4 current, uint len) = peek(cur, i);
195
+ if (current == key) return i;
196
+ i += 8 + len;
197
+ }
198
+ return cur.len;
199
+ }
200
+
201
+ /// @notice Scan forward from the current position for the first block matching `key`.
202
+ /// @param cur Source cursor.
203
+ /// @param key Block type to find.
204
+ /// @return Byte offset of the matching block, or `cur.len` if not found.
205
+ function find(Cur memory cur, bytes4 key) internal pure returns (uint) {
206
+ return find(cur, cur.i, key);
207
+ }
208
+
209
+ /// @notice Validate and consume the current block, advancing `cur.i` past it.
210
+ /// @param cur Cursor to advance.
211
+ /// @param key Expected block type key.
212
+ /// @param min Minimum payload length.
213
+ /// @param max Maximum payload length (0 = unbounded).
214
+ /// @return abs Absolute calldata offset of the payload start.
215
+ function consume(Cur memory cur, bytes4 key, uint min, uint max) internal pure returns (uint abs) {
216
+ uint next;
217
+ (abs, next) = expect(cur, cur.i, key, min, max);
218
+ cur.i = next;
219
+ }
220
+
221
+ /// @notice Load a payload word and mask away any omitted tail bytes on the right.
222
+ /// @param abs Absolute calldata offset of the word start.
223
+ /// @param tail Number of trailing bytes omitted from the logical payload (0..31).
224
+ /// @return value Decoded word with omitted tail bytes zeroed.
225
+ function mask(uint abs, uint tail) internal pure returns (bytes32 value) {
226
+ assembly ("memory-safe") {
227
+ value := calldataload(abs)
228
+ }
229
+ if (tail != 0) value &= bytes32(type(uint256).max << (tail * 8));
230
+ }
231
+
232
+ /// @notice Enter a Bundle block at the current position and return the next offset.
233
+ /// Advances `cur.i` past the bundle header so the bundled members can be parsed
234
+ /// directly from the same cursor. The returned `next` is the byte offset
235
+ /// immediately after the bundle payload, relative to the current cursor region.
236
+ /// @param cur Cursor positioned at a bundle block; advanced past the 8-byte header.
237
+ /// @return next Byte offset immediately after the bundle payload.
238
+ function bundle(Cur memory cur) internal pure returns (uint next) {
239
+ (, next) = expect(cur, cur.i, Keys.Bundle, 0, 0);
240
+ cur.i += 8;
241
+ }
242
+
243
+ /// @notice Enter a List block at the current position and return the next offset.
244
+ /// Advances `cur.i` past the list header so the list members can be parsed
245
+ /// directly from the same cursor. The returned `next` is the byte offset
246
+ /// immediately after the list payload, relative to the current cursor region.
247
+ /// @param cur Cursor positioned at a list block; advanced past the 8-byte header.
248
+ /// @return next Byte offset immediately after the list payload.
249
+ function list(Cur memory cur) internal pure returns (uint next) {
250
+ (, next) = expect(cur, cur.i, Keys.List, 0, 0);
251
+ cur.i += 8;
252
+ }
253
+
254
+ /// @notice Consume a block with the given key at the current position and return a cursor over the full block slice.
255
+ /// Advances `cur.i` past the block while the returned cursor is scoped to the
256
+ /// full block bytes as a fresh zero-based region.
257
+ /// @param cur Cursor positioned at the expected block.
258
+ /// @param key Expected block type key.
259
+ /// @return out Cursor scoped to the full block.
260
+ function take(Cur memory cur, bytes4 key) internal pure returns (Cur memory out) {
261
+ (, uint next) = expect(cur, cur.i, key, 0, 0);
262
+ out = cur.slice(cur.i, next);
263
+ cur.i = next;
264
+ }
265
+
266
+ /// @notice Consume an optional ROUTE block at the current position and return a cursor over the full block slice.
267
+ /// If the current block is not ROUTE, returns an empty cursor and leaves `cur.i` unchanged.
268
+ /// Otherwise behaves like `take(cur, Keys.Route)`.
269
+ /// @param cur Cursor positioned at an optional ROUTE block.
270
+ /// @return out Cursor scoped to the full ROUTE block, or empty when no ROUTE block is present.
271
+ function maybeRoute(Cur memory cur) internal pure returns (Cur memory out) {
272
+ return cur.has(Keys.Route) ? take(cur, Keys.Route) : cur.slice(cur.i, cur.i);
273
+ }
274
+
275
+ /// @notice Enter a List block, prime its member run, and return the raw block count.
276
+ /// @param cur Cursor positioned at a list block; advanced past the 8-byte header.
277
+ /// @param group Expected block group size for the list item stream.
278
+ /// @return count Total number of blocks in the list payload (a multiple of `group`).
279
+ /// @return next Byte offset immediately after the list payload.
280
+ function list(Cur memory cur, uint group) internal pure returns (uint count, uint next) {
281
+ next = list(cur);
282
+ (, count, ) = cur.primeRun(group);
283
+ if (cur.bound != next) revert IncompleteCursor();
284
+ }
285
+
286
+ /// @notice Assert that the cursor has consumed exactly up to `bound`.
287
+ /// Reverts with `IncompleteCursor` if `bound` is zero or `cur.i != cur.bound`.
288
+ /// @param cur Cursor to check.
289
+ function complete(Cur memory cur) internal pure {
290
+ if (cur.bound == 0 || cur.i != cur.bound) revert IncompleteCursor();
291
+ }
292
+
293
+ /// @notice Assert that the cursor has consumed its entire source region.
294
+ /// Reverts with `IncompleteCursor` when `cur.i != cur.len`.
295
+ /// @param cur Cursor to check.
296
+ function end(Cur memory cur) internal pure {
297
+ if (cur.i != cur.len) revert IncompleteCursor();
298
+ }
299
+
300
+ /// @notice Resume parsing after a nested region delimited by `resumeAt`.
301
+ /// Reverts with `IncompleteCursor` if `cur.i` has advanced past `resumeAt` or `resumeAt`
302
+ /// exceeds the cursor region length. Otherwise moves `cur.i` to `end`.
303
+ /// @param cur Cursor to advance.
304
+ /// @param resumeAt Relative end offset of the nested region to resume after.
305
+ function resume(Cur memory cur, uint resumeAt) internal pure {
306
+ if (resumeAt > cur.len || cur.i > resumeAt) revert IncompleteCursor();
307
+ cur.i = resumeAt;
308
+ }
309
+
310
+ /// @notice Ensure that parsing has reached an exact nested-region boundary.
311
+ /// Reverts with `IncompleteCursor` if `ensureAt` exceeds the cursor region length
312
+ /// or `cur.i != ensureAt`.
313
+ /// @param cur Cursor to check.
314
+ /// @param ensureAt Relative offset that `cur.i` must match exactly.
315
+ function ensure(Cur memory cur, uint ensureAt) internal pure {
316
+ if (ensureAt > cur.len || cur.i != ensureAt) revert IncompleteCursor();
317
+ }
318
+
319
+ /// @notice Assert completion and finalise a writer in one step.
320
+ /// @param cur Cursor to check.
321
+ /// @param writer Writer to finalise.
322
+ /// @return Trimmed output bytes from the writer.
323
+ function complete(Cur memory cur, Writer memory writer) internal pure returns (bytes memory) {
324
+ if (cur.bound == 0 || cur.i != cur.bound) revert IncompleteCursor();
325
+ return Writers.finish(writer);
326
+ }
327
+
328
+ // -------------------------------------------------------------------------
329
+ // Block factory helpers
330
+ // -------------------------------------------------------------------------
331
+
332
+ /// @notice Encode a block with a single 32-byte payload word.
333
+ /// @param key Block type key.
334
+ /// @param value 32-byte payload.
335
+ /// @return Encoded block bytes.
336
+ function createBlock32(bytes4 key, bytes32 value) internal pure returns (bytes memory) {
337
+ return bytes.concat(key, bytes4(uint32(0x20)), value);
338
+ }
339
+
340
+ /// @notice Encode a block with two 32-byte payload words (64-byte payload).
341
+ /// @param key Block type key.
342
+ /// @param a First payload word.
343
+ /// @param b Second payload word.
344
+ /// @return Encoded block bytes.
345
+ function createBlock64(bytes4 key, bytes32 a, bytes32 b) internal pure returns (bytes memory) {
346
+ return bytes.concat(key, bytes4(uint32(0x40)), a, b);
347
+ }
348
+
349
+ /// @notice Encode a block with three 32-byte payload words (96-byte payload).
350
+ /// @param key Block type key.
351
+ /// @param a First payload word.
352
+ /// @param b Second payload word.
353
+ /// @param c Third payload word.
354
+ /// @return Encoded block bytes.
355
+ function createBlock96(bytes4 key, bytes32 a, bytes32 b, bytes32 c) internal pure returns (bytes memory) {
356
+ return bytes.concat(key, bytes4(uint32(0x60)), a, b, c);
357
+ }
358
+
359
+ /// @notice Encode a block with four 32-byte payload words (128-byte payload).
360
+ /// @param key Block type key.
361
+ /// @param a First payload word.
362
+ /// @param b Second payload word.
363
+ /// @param c Third payload word.
364
+ /// @param d Fourth payload word.
365
+ /// @return Encoded block bytes.
366
+ function createBlock128(
367
+ bytes4 key,
368
+ bytes32 a,
369
+ bytes32 b,
370
+ bytes32 c,
371
+ bytes32 d
372
+ ) internal pure returns (bytes memory) {
373
+ return bytes.concat(key, bytes4(uint32(0x80)), a, b, c, d);
374
+ }
375
+
376
+ /// @notice Encode a block with five 32-byte payload words (160-byte payload).
377
+ /// @param key Block type key.
378
+ /// @param a First payload word.
379
+ /// @param b Second payload word.
380
+ /// @param c Third payload word.
381
+ /// @param d Fourth payload word.
382
+ /// @param e Fifth payload word.
383
+ /// @return Encoded block bytes.
384
+ function createBlock160(
385
+ bytes4 key,
386
+ bytes32 a,
387
+ bytes32 b,
388
+ bytes32 c,
389
+ bytes32 d,
390
+ bytes32 e
391
+ ) internal pure returns (bytes memory) {
392
+ return bytes.concat(key, bytes4(uint32(0xa0)), a, b, c, d, e);
393
+ }
394
+
395
+ /// @notice Encode a block with a 32-byte fixed head followed by a variable-length tail.
396
+ /// @param key Block type key.
397
+ /// @param head Fixed 32-byte head payload.
398
+ /// @param tail Variable-length payload bytes appended after the head.
399
+ /// @return Encoded block bytes.
400
+ function createBlockHead32(bytes4 key, bytes32 head, bytes memory tail) internal pure returns (bytes memory) {
401
+ return bytes.concat(key, bytes4(uint32(0x20 + tail.length)), head, tail);
402
+ }
403
+
404
+ /// @notice Encode a block with a 64-byte fixed head followed by a variable-length tail.
405
+ /// @param key Block type key.
406
+ /// @param a First fixed payload word.
407
+ /// @param b Second fixed payload word.
408
+ /// @param tail Variable-length payload bytes appended after the fixed head.
409
+ /// @return Encoded block bytes.
410
+ function createBlockHead64(
411
+ bytes4 key,
412
+ bytes32 a,
413
+ bytes32 b,
414
+ bytes memory tail
415
+ ) internal pure returns (bytes memory) {
416
+ return bytes.concat(key, bytes4(uint32(0x40 + tail.length)), a, b, tail);
417
+ }
418
+
419
+ /// @notice Encode a BOUNTY block.
420
+ /// @param bounty Relayer reward amount.
421
+ /// @param relayer Relayer account identifier.
422
+ /// @return Encoded BOUNTY block bytes.
423
+ function toBountyBlock(uint bounty, bytes32 relayer) internal pure returns (bytes memory) {
424
+ return createBlock64(Keys.Bounty, bytes32(bounty), relayer);
425
+ }
426
+
427
+ /// @notice Encode a STEP block.
428
+ /// @param target Command target identifier.
429
+ /// @param value Native value forwarded with the step.
430
+ /// @param request Variable-length nested request payload.
431
+ /// @return Encoded STEP block bytes.
432
+ function toStepBlock(uint target, uint value, bytes memory request) internal pure returns (bytes memory) {
433
+ return createBlockHead64(Keys.Step, bytes32(target), bytes32(value), request);
434
+ }
435
+
436
+ /// @notice Encode a CALL block.
437
+ /// @param target Target node identifier.
438
+ /// @param value Native value forwarded with the call.
439
+ /// @param data Raw calldata payload for the target.
440
+ /// @return Encoded CALL block bytes.
441
+ function toCallBlock(uint target, uint value, bytes memory data) internal pure returns (bytes memory) {
442
+ return createBlockHead64(Keys.Call, bytes32(target), bytes32(value), data);
443
+ }
444
+
445
+ /// @notice Encode a BALANCE block.
446
+ /// @param asset Asset identifier.
447
+ /// @param meta Asset metadata slot.
448
+ /// @param amount Token amount.
449
+ /// @return Encoded BALANCE block bytes.
450
+ function toBalanceBlock(bytes32 asset, bytes32 meta, uint amount) internal pure returns (bytes memory) {
451
+ return createBlock96(Keys.Balance, asset, meta, bytes32(amount));
452
+ }
453
+
454
+ /// @notice Encode a CUSTODY block.
455
+ /// @param host Host node ID holding the custody.
456
+ /// @param asset Asset identifier.
457
+ /// @param meta Asset metadata slot.
458
+ /// @param amount Token amount.
459
+ /// @return Encoded CUSTODY block bytes.
460
+ function toCustodyBlock(
461
+ uint host,
462
+ bytes32 asset,
463
+ bytes32 meta,
464
+ uint amount
465
+ ) internal pure returns (bytes memory) {
466
+ return createBlock128(Keys.Custody, bytes32(host), asset, meta, bytes32(amount));
467
+ }
468
+
469
+ // -------------------------------------------------------------------------
470
+ // Raw calldata loaders
471
+ // -------------------------------------------------------------------------
472
+
473
+ /// @notice Load one 32-byte word from calldata.
474
+ /// @dev Performs no bounds, key, length, or cursor checks.
475
+ /// @param abs Absolute calldata offset of the word start.
476
+ /// @return a Loaded word.
477
+ function load32(uint abs) internal pure returns (bytes32 a) {
478
+ assembly ("memory-safe") {
479
+ a := calldataload(abs)
480
+ }
481
+ }
482
+
483
+ /// @notice Load two 32-byte words from calldata.
484
+ /// @dev Performs no bounds, key, length, or cursor checks.
485
+ /// @param abs Absolute calldata offset of the first word.
486
+ /// @return a First loaded word.
487
+ /// @return b Second loaded word.
488
+ function load64(uint abs) internal pure returns (bytes32 a, bytes32 b) {
489
+ assembly ("memory-safe") {
490
+ a := calldataload(abs)
491
+ b := calldataload(add(abs, 0x20))
492
+ }
493
+ }
494
+
495
+ /// @notice Load three 32-byte words from calldata.
496
+ /// @dev Performs no bounds, key, length, or cursor checks.
497
+ /// @param abs Absolute calldata offset of the first word.
498
+ /// @return a First loaded word.
499
+ /// @return b Second loaded word.
500
+ /// @return c Third loaded word.
501
+ function load96(uint abs) internal pure returns (bytes32 a, bytes32 b, bytes32 c) {
502
+ assembly ("memory-safe") {
503
+ a := calldataload(abs)
504
+ b := calldataload(add(abs, 0x20))
505
+ c := calldataload(add(abs, 0x40))
506
+ }
507
+ }
508
+
509
+ /// @notice Load four 32-byte words from calldata.
510
+ /// @dev Performs no bounds, key, length, or cursor checks.
511
+ /// @param abs Absolute calldata offset of the first word.
512
+ /// @return a First loaded word.
513
+ /// @return b Second loaded word.
514
+ /// @return c Third loaded word.
515
+ /// @return d Fourth loaded word.
516
+ function load128(uint abs) internal pure returns (bytes32 a, bytes32 b, bytes32 c, bytes32 d) {
517
+ assembly ("memory-safe") {
518
+ a := calldataload(abs)
519
+ b := calldataload(add(abs, 0x20))
520
+ c := calldataload(add(abs, 0x40))
521
+ d := calldataload(add(abs, 0x60))
522
+ }
523
+ }
524
+
525
+ /// @notice Load five 32-byte words from calldata.
526
+ /// @dev Performs no bounds, key, length, or cursor checks.
527
+ /// @param abs Absolute calldata offset of the first word.
528
+ /// @return a First loaded word.
529
+ /// @return b Second loaded word.
530
+ /// @return c Third loaded word.
531
+ /// @return d Fourth loaded word.
532
+ /// @return e Fifth loaded word.
533
+ function load160(uint abs) internal pure returns (bytes32 a, bytes32 b, bytes32 c, bytes32 d, bytes32 e) {
534
+ assembly ("memory-safe") {
535
+ a := calldataload(abs)
536
+ b := calldataload(add(abs, 0x20))
537
+ c := calldataload(add(abs, 0x40))
538
+ d := calldataload(add(abs, 0x60))
539
+ e := calldataload(add(abs, 0x80))
540
+ }
541
+ }
542
+
543
+ // -------------------------------------------------------------------------
544
+ // unpack* - consume current block and decode payload fields
545
+ // -------------------------------------------------------------------------
546
+
547
+ // Generic fixed-width decoders
548
+
549
+ /// @notice Consume a dynamic block with the given key and return the raw payload as a calldata slice.
550
+ /// The payload length is variable; the returned slice covers the entire payload.
551
+ /// @param cur Cursor; advanced past the block.
552
+ /// @param key Expected dynamic block key.
553
+ /// @return data Raw block payload bytes.
554
+ function unpackRaw(Cur memory cur, bytes4 key) internal pure returns (bytes calldata data) {
555
+ (uint abs, uint next) = expect(cur, cur.i, key, 0, 0);
556
+ data = msg.data[abs:cur.offset + next];
557
+ cur.i = next;
558
+ }
559
+
560
+ /// @notice Consume a dynamic block with a single bytes32 payload.
561
+ /// @param cur Cursor; advanced past the block.
562
+ /// @param key Expected dynamic block key.
563
+ /// @return value Decoded bytes32.
564
+ /// @param keep Number of bytes to keep from the final payload word (1..32).
565
+ function unpack32(Cur memory cur, bytes4 key, uint keep) internal pure returns (bytes32 value) {
566
+ if (keep == 0 || keep > 32) revert InvalidKeep();
567
+ uint len = keep;
568
+ uint abs = consume(cur, key, len, len);
569
+ value = mask(abs, 32 - keep);
570
+ }
571
+
572
+ /// @notice Consume a dynamic block with two bytes32 payload words.
573
+ /// @param cur Cursor; advanced past the block.
574
+ /// @param key Expected dynamic block key.
575
+ /// @return a First decoded bytes32.
576
+ /// @return b Second decoded bytes32.
577
+ /// @param keep Number of bytes to keep from the final payload word (1..32).
578
+ function unpack64(Cur memory cur, bytes4 key, uint keep) internal pure returns (bytes32 a, bytes32 b) {
579
+ if (keep == 0 || keep > 32) revert InvalidKeep();
580
+ uint len = 32 + keep;
581
+ uint abs = consume(cur, key, len, len);
582
+ a = bytes32(msg.data[abs:abs + 32]);
583
+ b = mask(abs + 32, 32 - keep);
584
+ }
585
+
586
+ /// @notice Consume a dynamic block with three bytes32 payload words.
587
+ /// @param cur Cursor; advanced past the block.
588
+ /// @param key Expected dynamic block key.
589
+ /// @return a First decoded bytes32.
590
+ /// @return b Second decoded bytes32.
591
+ /// @return c Third decoded bytes32.
592
+ /// @param keep Number of bytes to keep from the final payload word (1..32).
593
+ function unpack96(Cur memory cur, bytes4 key, uint keep) internal pure returns (bytes32 a, bytes32 b, bytes32 c) {
594
+ if (keep == 0 || keep > 32) revert InvalidKeep();
595
+ uint len = 64 + keep;
596
+ uint abs = consume(cur, key, len, len);
597
+ a = bytes32(msg.data[abs:abs + 32]);
598
+ b = bytes32(msg.data[abs + 32:abs + 64]);
599
+ c = mask(abs + 64, 32 - keep);
600
+ }
601
+
602
+ /// @notice Consume a dynamic block with a 128-byte payload (four 32-byte words).
603
+ /// @param cur Cursor; advanced past the block.
604
+ /// @param key Expected dynamic block key.
605
+ /// @return a First decoded bytes32.
606
+ /// @return b Second decoded bytes32.
607
+ /// @return c Third decoded bytes32.
608
+ /// @return d Fourth decoded bytes32.
609
+ /// @param keep Number of bytes to keep from the final payload word (1..32).
610
+ function unpack128(
611
+ Cur memory cur,
612
+ bytes4 key,
613
+ uint keep
614
+ ) internal pure returns (bytes32 a, bytes32 b, bytes32 c, bytes32 d) {
615
+ if (keep == 0 || keep > 32) revert InvalidKeep();
616
+ uint len = 96 + keep;
617
+ uint abs = consume(cur, key, len, len);
618
+ a = bytes32(msg.data[abs:abs + 32]);
619
+ b = bytes32(msg.data[abs + 32:abs + 64]);
620
+ c = bytes32(msg.data[abs + 64:abs + 96]);
621
+ d = mask(abs + 96, 32 - keep);
622
+ }
623
+
624
+ /// @notice Consume a dynamic block with a 160-byte payload (five 32-byte words).
625
+ /// @param cur Cursor; advanced past the block.
626
+ /// @param key Expected dynamic block key.
627
+ /// @return a First decoded bytes32.
628
+ /// @return b Second decoded bytes32.
629
+ /// @return c Third decoded bytes32.
630
+ /// @return d Fourth decoded bytes32.
631
+ /// @return e Fifth decoded bytes32.
632
+ /// @param keep Number of bytes to keep from the final payload word (1..32).
633
+ function unpack160(
634
+ Cur memory cur,
635
+ bytes4 key,
636
+ uint keep
637
+ ) internal pure returns (bytes32 a, bytes32 b, bytes32 c, bytes32 d, bytes32 e) {
638
+ if (keep == 0 || keep > 32) revert InvalidKeep();
639
+ uint len = 128 + keep;
640
+ uint abs = consume(cur, key, len, len);
641
+ a = bytes32(msg.data[abs:abs + 32]);
642
+ b = bytes32(msg.data[abs + 32:abs + 64]);
643
+ c = bytes32(msg.data[abs + 64:abs + 96]);
644
+ d = bytes32(msg.data[abs + 96:abs + 128]);
645
+ e = mask(abs + 128, 32 - keep);
646
+ }
647
+
648
+ /// @notice Consume a dynamic block with a single uint payload.
649
+ /// @param cur Cursor; advanced past the block.
650
+ /// @param key Expected dynamic block key.
651
+ /// @return value Decoded uint value.
652
+ function unpackUint(Cur memory cur, bytes4 key) internal pure returns (uint value) {
653
+ value = uint(unpack32(cur, key, 32));
654
+ }
655
+
656
+ /// @notice Consume a dynamic block with two uint payload words.
657
+ /// @param cur Cursor; advanced past the block.
658
+ /// @param key Expected dynamic block key.
659
+ /// @return a First decoded uint.
660
+ /// @return b Second decoded uint.
661
+ function unpack2Uint(Cur memory cur, bytes4 key) internal pure returns (uint a, uint b) {
662
+ (bytes32 x, bytes32 y) = unpack64(cur, key, 32);
663
+ return (uint(x), uint(y));
664
+ }
665
+
666
+ /// @notice Consume a dynamic block with three uint payload words.
667
+ /// @param cur Cursor; advanced past the block.
668
+ /// @param key Expected dynamic block key.
669
+ /// @return a First decoded uint.
670
+ /// @return b Second decoded uint.
671
+ /// @return c Third decoded uint.
672
+ function unpack3Uint(Cur memory cur, bytes4 key) internal pure returns (uint a, uint b, uint c) {
673
+ (bytes32 x, bytes32 y, bytes32 z) = unpack96(cur, key, 32);
674
+ return (uint(x), uint(y), uint(z));
675
+ }
676
+
677
+ // Generic fixed-head decoders
678
+
679
+ /// @notice Consume a dynamic block with a 32-byte fixed head followed by a variable-length tail.
680
+ /// @param cur Cursor; advanced past the block.
681
+ /// @param key Expected dynamic block key.
682
+ /// @return head Fixed 32-byte head.
683
+ /// @return tail Variable-length payload bytes after the fixed head.
684
+ function unpackHead32(Cur memory cur, bytes4 key) internal pure returns (bytes32 head, bytes calldata tail) {
685
+ uint abs = consume(cur, key, 32, 0);
686
+ head = bytes32(msg.data[abs:abs + 32]);
687
+ tail = msg.data[abs + 32:cur.offset + cur.i];
688
+ }
689
+
690
+ /// @notice Consume a dynamic block with a 64-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 a First fixed head word.
694
+ /// @return b Second fixed head word.
695
+ /// @return tail Variable-length payload bytes after the fixed head.
696
+ function unpackHead64(
697
+ Cur memory cur,
698
+ bytes4 key
699
+ ) internal pure returns (bytes32 a, bytes32 b, bytes calldata tail) {
700
+ uint abs = consume(cur, key, 64, 0);
701
+ a = bytes32(msg.data[abs:abs + 32]);
702
+ b = bytes32(msg.data[abs + 32:abs + 64]);
703
+ tail = msg.data[abs + 64:cur.offset + cur.i];
704
+ }
705
+
706
+ /// @notice Consume a dynamic block with a 96-byte fixed head followed by a variable-length tail.
707
+ /// @param cur Cursor; advanced past the block.
708
+ /// @param key Expected dynamic block key.
709
+ /// @return a First fixed head word.
710
+ /// @return b Second fixed head word.
711
+ /// @return c Third fixed head word.
712
+ /// @return tail Variable-length payload bytes after the fixed head.
713
+ function unpackHead96(
714
+ Cur memory cur,
715
+ bytes4 key
716
+ ) internal pure returns (bytes32 a, bytes32 b, bytes32 c, bytes calldata tail) {
717
+ uint abs = consume(cur, key, 96, 0);
718
+ a = bytes32(msg.data[abs:abs + 32]);
719
+ b = bytes32(msg.data[abs + 32:abs + 64]);
720
+ c = bytes32(msg.data[abs + 64:abs + 96]);
721
+ tail = msg.data[abs + 96:cur.offset + cur.i];
722
+ }
723
+
724
+ /// @notice Consume a dynamic block with a 128-byte fixed head followed by a variable-length tail.
725
+ /// @param cur Cursor; advanced past the block.
726
+ /// @param key Expected dynamic block key.
727
+ /// @return a First fixed head word.
728
+ /// @return b Second fixed head word.
729
+ /// @return c Third fixed head word.
730
+ /// @return d Fourth fixed head word.
731
+ /// @return tail Variable-length payload bytes after the fixed head.
732
+ function unpackHead128(
733
+ Cur memory cur,
734
+ bytes4 key
735
+ ) internal pure returns (bytes32 a, bytes32 b, bytes32 c, bytes32 d, bytes calldata tail) {
736
+ uint abs = consume(cur, key, 128, 0);
737
+ a = bytes32(msg.data[abs:abs + 32]);
738
+ b = bytes32(msg.data[abs + 32:abs + 64]);
739
+ c = bytes32(msg.data[abs + 64:abs + 96]);
740
+ d = bytes32(msg.data[abs + 96:abs + 128]);
741
+ tail = msg.data[abs + 128:cur.offset + cur.i];
742
+ }
743
+
744
+ // Generic typed-shape decoders
745
+
746
+ /// @notice Consume a fixed-size asset amount block and return asset, meta, and amount.
747
+ /// @param cur Cursor; advanced past the block.
748
+ /// @param key Expected block key.
749
+ /// @return asset Asset identifier.
750
+ /// @return meta Asset metadata slot.
751
+ /// @return amount Scalar amount value.
752
+ function unpackAssetAmount(
753
+ Cur memory cur,
754
+ bytes4 key
755
+ ) internal pure returns (bytes32 asset, bytes32 meta, uint amount) {
756
+ uint abs = consume(cur, key, 96, 96);
757
+ asset = bytes32(msg.data[abs:abs + 32]);
758
+ meta = bytes32(msg.data[abs + 32:abs + 64]);
759
+ amount = uint(bytes32(msg.data[abs + 64:abs + 96]));
760
+ }
761
+
762
+ /// @notice Consume a fixed-size account amount block and return account, asset, meta, and amount.
763
+ /// @param cur Cursor; advanced past the block.
764
+ /// @param key Expected block key.
765
+ /// @return account Account identifier.
766
+ /// @return asset Asset identifier.
767
+ /// @return meta Asset metadata slot.
768
+ /// @return amount Scalar amount value.
769
+ function unpackAccountAmount(
770
+ Cur memory cur,
771
+ bytes4 key
772
+ ) internal pure returns (bytes32 account, bytes32 asset, bytes32 meta, uint amount) {
773
+ uint abs = consume(cur, key, 128, 128);
774
+ account = bytes32(msg.data[abs:abs + 32]);
775
+ asset = bytes32(msg.data[abs + 32:abs + 64]);
776
+ meta = bytes32(msg.data[abs + 64:abs + 96]);
777
+ amount = uint(bytes32(msg.data[abs + 96:abs + 128]));
778
+ }
779
+
780
+ /// @notice Consume a fixed-size host amount block and return host, asset, meta, and amount.
781
+ /// @param cur Cursor; advanced past the block.
782
+ /// @param key Expected block key.
783
+ /// @return host Host node ID.
784
+ /// @return asset Asset identifier.
785
+ /// @return meta Asset metadata slot.
786
+ /// @return amount Scalar amount value.
787
+ function unpackHostAmount(
788
+ Cur memory cur,
789
+ bytes4 key
790
+ ) internal pure returns (uint host, bytes32 asset, bytes32 meta, uint amount) {
791
+ uint abs = consume(cur, key, 128, 128);
792
+ host = uint(bytes32(msg.data[abs:abs + 32]));
793
+ asset = bytes32(msg.data[abs + 32:abs + 64]);
794
+ meta = bytes32(msg.data[abs + 64:abs + 96]);
795
+ amount = uint(bytes32(msg.data[abs + 96:abs + 128]));
796
+ }
797
+
798
+ /// @notice Consume a fixed-size host account asset block and return host, account, asset, and meta.
799
+ /// @param cur Cursor; advanced past the block.
800
+ /// @param key Expected block key.
801
+ /// @return host Host node ID.
802
+ /// @return account Account identifier.
803
+ /// @return asset Asset identifier.
804
+ /// @return meta Asset metadata slot.
805
+ function unpackHostAccountAsset(
806
+ Cur memory cur,
807
+ bytes4 key
808
+ ) internal pure returns (uint host, bytes32 account, bytes32 asset, bytes32 meta) {
809
+ uint abs = consume(cur, key, 128, 128);
810
+ host = uint(bytes32(msg.data[abs:abs + 32]));
811
+ account = bytes32(msg.data[abs + 32:abs + 64]);
812
+ asset = bytes32(msg.data[abs + 64:abs + 96]);
813
+ meta = bytes32(msg.data[abs + 96:abs + 128]);
814
+ }
815
+
816
+ /// @notice Consume a fixed-size transaction block and return from, to, asset, meta, and amount.
817
+ /// @param cur Cursor; advanced past the block.
818
+ /// @param key Expected block key.
819
+ /// @return from Source account identifier.
820
+ /// @return to Destination account identifier.
821
+ /// @return asset Asset identifier.
822
+ /// @return meta Asset metadata slot.
823
+ /// @return amount Scalar amount value.
824
+ function unpackTransaction(
825
+ Cur memory cur,
826
+ bytes4 key
827
+ ) internal pure returns (bytes32 from, bytes32 to, bytes32 asset, bytes32 meta, uint amount) {
828
+ uint abs = consume(cur, key, 160, 160);
829
+ from = bytes32(msg.data[abs:abs + 32]);
830
+ to = bytes32(msg.data[abs + 32:abs + 64]);
831
+ asset = bytes32(msg.data[abs + 64:abs + 96]);
832
+ meta = bytes32(msg.data[abs + 96:abs + 128]);
833
+ amount = uint(bytes32(msg.data[abs + 128:abs + 160]));
834
+ }
835
+
836
+ // Type-specific fixed-width decoders
837
+
838
+ /// @notice Consume an ACCOUNT block and return the account.
839
+ /// @param cur Cursor; advanced past the block.
840
+ /// @return account Account identifier.
841
+ function unpackAccount(Cur memory cur) internal pure returns (bytes32 account) {
842
+ account = unpack32(cur, Keys.Account, 32);
843
+ }
844
+
845
+ /// @notice Consume a NODE block and return the node ID.
846
+ /// @param cur Cursor; advanced past the block.
847
+ /// @return node Node identifier.
848
+ function unpackNode(Cur memory cur) internal pure returns (uint node) {
849
+ node = uint(unpack32(cur, Keys.Node, 32));
850
+ }
851
+
852
+ /// @notice Consume a RATE block and return the value.
853
+ /// @param cur Cursor; advanced past the block.
854
+ /// @return value Encoded ratio or rate.
855
+ function unpackRate(Cur memory cur) internal pure returns (uint value) {
856
+ value = uint(unpack32(cur, Keys.Rate, 32));
857
+ }
858
+
859
+ /// @notice Consume a QUANTITY block and return the amount.
860
+ /// @param cur Cursor; advanced past the block.
861
+ /// @return amount Scalar quantity value.
862
+ function unpackQuantity(Cur memory cur) internal pure returns (uint amount) {
863
+ amount = uint(unpack32(cur, Keys.Quantity, 32));
864
+ }
865
+
866
+ /// @notice Consume a FEE block and return the amount.
867
+ /// @param cur Cursor; advanced past the block.
868
+ /// @return amount Fee amount.
869
+ function unpackFee(Cur memory cur) internal pure returns (uint amount) {
870
+ amount = uint(unpack32(cur, Keys.Fee, 32));
871
+ }
872
+
873
+ /// @notice Consume an ASSET block and return the asset descriptor fields.
874
+ /// @param cur Cursor; advanced past the block.
875
+ /// @return asset Asset identifier.
876
+ /// @return meta Asset metadata slot.
877
+ function unpackAsset(Cur memory cur) internal pure returns (bytes32 asset, bytes32 meta) {
878
+ (asset, meta) = unpack64(cur, Keys.Asset, 32);
879
+ }
880
+
881
+ /// @notice Consume an ACCOUNT_ASSET form block and return its fields as separate values.
882
+ /// @param cur Cursor; advanced past the block.
883
+ /// @return account Account identifier.
884
+ /// @return asset Asset identifier.
885
+ /// @return meta Asset metadata slot.
886
+ function unpackAccountAsset(Cur memory cur) internal pure returns (bytes32 account, bytes32 asset, bytes32 meta) {
887
+ uint abs = consume(cur, Keys.AccountAsset, 96, 96);
888
+ account = bytes32(msg.data[abs:abs + 32]);
889
+ asset = bytes32(msg.data[abs + 32:abs + 64]);
890
+ meta = bytes32(msg.data[abs + 64:abs + 96]);
891
+ }
892
+
893
+ /// @notice Consume an ACCOUNT_ASSET form block and return its fields as a struct.
894
+ /// @param cur Cursor; advanced past the block.
895
+ /// @return value Decoded account, asset, and meta.
896
+ function unpackAccountAssetValue(Cur memory cur) internal pure returns (AccountAsset memory value) {
897
+ (value.account, value.asset, value.meta) = unpackAccountAsset(cur);
898
+ }
899
+
900
+ /// @notice Consume a RELOCATION block and return the host and amount.
901
+ /// @param cur Cursor; advanced past the block.
902
+ /// @return host Host node ID receiving the funding.
903
+ /// @return amount Funding amount.
904
+ function unpackRelocation(Cur memory cur) internal pure returns (uint host, uint amount) {
905
+ (host, amount) = unpack2Uint(cur, Keys.Relocation);
906
+ }
907
+
908
+ /// @notice Consume a BOUNTY block and return the reward amount and relayer.
909
+ /// @param cur Cursor; advanced past the block.
910
+ /// @return amount Relayer reward amount.
911
+ /// @return relayer Relayer account identifier.
912
+ function unpackBounty(Cur memory cur) internal pure returns (uint amount, bytes32 relayer) {
913
+ (bytes32 x, bytes32 y) = unpack64(cur, Keys.Bounty, 32);
914
+ amount = uint(x);
915
+ relayer = y;
916
+ }
917
+
918
+ /// @notice Consume a BOUNDS block and return the signed min and max values.
919
+ /// @param cur Cursor; advanced past the block.
920
+ /// @return min Lower signed bound.
921
+ /// @return max Upper signed bound.
922
+ function unpackBounds(Cur memory cur) internal pure returns (int min, int max) {
923
+ uint abs = consume(cur, Keys.Bounds, 64, 64);
924
+ assembly ("memory-safe") {
925
+ min := calldataload(abs)
926
+ max := calldataload(add(abs, 0x20))
927
+ }
928
+ }
929
+
930
+ /// @notice Consume an AMOUNT block and return its fields as separate values.
931
+ /// @param cur Cursor; advanced past the block.
932
+ /// @return asset Asset identifier.
933
+ /// @return meta Asset metadata slot.
934
+ /// @return amount Token amount.
935
+ function unpackAmount(Cur memory cur) internal pure returns (bytes32 asset, bytes32 meta, uint amount) {
936
+ return unpackAssetAmount(cur, Keys.Amount);
937
+ }
938
+
939
+ /// @notice Consume an AMOUNT block and return its fields as a struct.
940
+ /// @param cur Cursor; advanced past the block.
941
+ /// @return value Decoded asset, meta, and amount.
942
+ function unpackAmountValue(Cur memory cur) internal pure returns (AssetAmount memory value) {
943
+ (value.asset, value.meta, value.amount) = unpackAssetAmount(cur, Keys.Amount);
944
+ }
945
+
946
+ /// @notice Consume a BALANCE block and return its fields as separate values.
947
+ /// @param cur Cursor; advanced past the block.
948
+ /// @return asset Asset identifier.
949
+ /// @return meta Asset metadata slot.
950
+ /// @return amount Token amount.
951
+ function unpackBalance(Cur memory cur) internal pure returns (bytes32 asset, bytes32 meta, uint amount) {
952
+ return unpackAssetAmount(cur, Keys.Balance);
953
+ }
954
+
955
+ /// @notice Consume a BALANCE block and return its fields as a struct.
956
+ /// @param cur Cursor; advanced past the block.
957
+ /// @return value Decoded asset, meta, and amount.
958
+ function unpackBalanceValue(Cur memory cur) internal pure returns (AssetAmount memory value) {
959
+ (value.asset, value.meta, value.amount) = unpackAssetAmount(cur, Keys.Balance);
960
+ }
961
+
962
+ /// @notice Consume a MINIMUM block and return its fields as separate values.
963
+ /// @param cur Cursor; advanced past the block.
964
+ /// @return asset Asset identifier.
965
+ /// @return meta Asset metadata slot.
966
+ /// @return amount Minimum acceptable amount.
967
+ function unpackMinimum(Cur memory cur) internal pure returns (bytes32 asset, bytes32 meta, uint amount) {
968
+ return unpackAssetAmount(cur, Keys.Minimum);
969
+ }
970
+
971
+ /// @notice Consume a MINIMUM block and return its fields as a struct.
972
+ /// @param cur Cursor; advanced past the block.
973
+ /// @return value Decoded asset, meta, and minimum amount.
974
+ function unpackMinimumValue(Cur memory cur) internal pure returns (AssetAmount memory value) {
975
+ (value.asset, value.meta, value.amount) = unpackAssetAmount(cur, Keys.Minimum);
976
+ }
977
+
978
+ /// @notice Consume a MAXIMUM block and return its fields as separate values.
979
+ /// @param cur Cursor; advanced past the block.
980
+ /// @return asset Asset identifier.
981
+ /// @return meta Asset metadata slot.
982
+ /// @return amount Maximum allowable spend.
983
+ function unpackMaximum(Cur memory cur) internal pure returns (bytes32 asset, bytes32 meta, uint amount) {
984
+ return unpackAssetAmount(cur, Keys.Maximum);
985
+ }
986
+
987
+ /// @notice Consume a MAXIMUM block and return its fields as a struct.
988
+ /// @param cur Cursor; advanced past the block.
989
+ /// @return value Decoded asset, meta, and maximum amount.
990
+ function unpackMaximumValue(Cur memory cur) internal pure returns (AssetAmount memory value) {
991
+ (value.asset, value.meta, value.amount) = unpackAssetAmount(cur, Keys.Maximum);
992
+ }
993
+
994
+ /// @notice Consume a HOST_ACCOUNT_ASSET form block and return its fields as separate values.
995
+ /// @param cur Cursor; advanced past the block.
996
+ /// @return host Host node ID.
997
+ /// @return account Account identifier.
998
+ /// @return asset Asset identifier.
999
+ /// @return meta Asset metadata slot.
1000
+ function unpackHostAccountAsset(
1001
+ Cur memory cur
1002
+ ) internal pure returns (uint host, bytes32 account, bytes32 asset, bytes32 meta) {
1003
+ return unpackHostAccountAsset(cur, Keys.HostAccountAsset);
1004
+ }
1005
+
1006
+ /// @notice Consume a HOST_ACCOUNT_ASSET form block and return its fields as a struct.
1007
+ /// @param cur Cursor; advanced past the block.
1008
+ /// @return value Decoded host, account, asset, and meta.
1009
+ function unpackHostAccountAssetValue(Cur memory cur) internal pure returns (HostAccountAsset memory value) {
1010
+ (value.host, value.account, value.asset, value.meta) = unpackHostAccountAsset(cur, Keys.HostAccountAsset);
1011
+ }
1012
+
1013
+ /// @notice Consume a PAYOUT block and return its fields as separate values.
1014
+ /// @param cur Cursor; advanced past the block.
1015
+ /// @return account Account identifier.
1016
+ /// @return asset Asset identifier.
1017
+ /// @return meta Asset metadata slot.
1018
+ /// @return amount Token amount.
1019
+ function unpackPayout(
1020
+ Cur memory cur
1021
+ ) internal pure returns (bytes32 account, bytes32 asset, bytes32 meta, uint amount) {
1022
+ return unpackAccountAmount(cur, Keys.Payout);
1023
+ }
1024
+
1025
+ /// @notice Consume a PAYOUT block and return its fields as a struct.
1026
+ /// @param cur Cursor; advanced past the block.
1027
+ /// @return value Decoded account, asset, meta, and amount.
1028
+ function unpackPayoutValue(Cur memory cur) internal pure returns (AccountAmount memory value) {
1029
+ (value.account, value.asset, value.meta, value.amount) = unpackAccountAmount(cur, Keys.Payout);
1030
+ }
1031
+
1032
+ /// @notice Consume an ACCOUNT_AMOUNT form block and return its fields as separate values.
1033
+ /// @param cur Cursor; advanced past the block.
1034
+ /// @return account Account identifier.
1035
+ /// @return asset Asset identifier.
1036
+ /// @return meta Asset metadata slot.
1037
+ /// @return amount Token amount.
1038
+ function unpackAccountAmount(
1039
+ Cur memory cur
1040
+ ) internal pure returns (bytes32 account, bytes32 asset, bytes32 meta, uint amount) {
1041
+ return unpackAccountAmount(cur, Keys.AccountAmount);
1042
+ }
1043
+
1044
+ /// @notice Consume an ACCOUNT_AMOUNT form block and return its fields as a struct.
1045
+ /// @param cur Cursor; advanced past the block.
1046
+ /// @return value Decoded account, asset, meta, and amount.
1047
+ function unpackAccountAmountValue(Cur memory cur) internal pure returns (AccountAmount memory value) {
1048
+ (value.account, value.asset, value.meta, value.amount) = unpackAccountAmount(cur, Keys.AccountAmount);
1049
+ }
1050
+
1051
+ /// @notice Consume an ALLOCATION block and return its fields as separate values.
1052
+ /// @param cur Cursor; advanced past the block.
1053
+ /// @return host Host node ID.
1054
+ /// @return asset Asset identifier.
1055
+ /// @return meta Asset metadata slot.
1056
+ /// @return amount Token amount.
1057
+ function unpackAllocation(
1058
+ Cur memory cur
1059
+ ) internal pure returns (uint host, bytes32 asset, bytes32 meta, uint amount) {
1060
+ return unpackHostAmount(cur, Keys.Allocation);
1061
+ }
1062
+
1063
+ /// @notice Consume an ALLOCATION block and return its fields as a struct.
1064
+ /// @param cur Cursor; advanced past the block.
1065
+ /// @return value Decoded host, asset, meta, and amount.
1066
+ function unpackAllocationValue(Cur memory cur) internal pure returns (HostAmount memory value) {
1067
+ (value.host, value.asset, value.meta, value.amount) = unpackHostAmount(cur, Keys.Allocation);
1068
+ }
1069
+
1070
+ /// @notice Consume an ALLOWANCE block and return its fields as separate values.
1071
+ /// @param cur Cursor; advanced past the block.
1072
+ /// @return host Host node ID.
1073
+ /// @return asset Asset identifier.
1074
+ /// @return meta Asset metadata slot.
1075
+ /// @return amount Token amount.
1076
+ function unpackAllowance(
1077
+ Cur memory cur
1078
+ ) internal pure returns (uint host, bytes32 asset, bytes32 meta, uint amount) {
1079
+ return unpackHostAmount(cur, Keys.Allowance);
1080
+ }
1081
+
1082
+ /// @notice Consume an ALLOWANCE block and return its fields as a struct.
1083
+ /// @param cur Cursor; advanced past the block.
1084
+ /// @return value Decoded host, asset, meta, and amount.
1085
+ function unpackAllowanceValue(Cur memory cur) internal pure returns (HostAmount memory value) {
1086
+ (value.host, value.asset, value.meta, value.amount) = unpackHostAmount(cur, Keys.Allowance);
1087
+ }
1088
+
1089
+ /// @notice Consume a CUSTODY block and return its fields as separate values.
1090
+ /// @param cur Cursor; advanced past the block.
1091
+ /// @return host Host node ID.
1092
+ /// @return asset Asset identifier.
1093
+ /// @return meta Asset metadata slot.
1094
+ /// @return amount Token amount.
1095
+ function unpackCustody(
1096
+ Cur memory cur
1097
+ ) internal pure returns (uint host, bytes32 asset, bytes32 meta, uint amount) {
1098
+ return unpackHostAmount(cur, Keys.Custody);
1099
+ }
1100
+
1101
+ /// @notice Consume a CUSTODY block and return its fields as a struct.
1102
+ /// @param cur Cursor; advanced past the block.
1103
+ /// @return value Decoded host, asset, meta, and amount.
1104
+ function unpackCustodyValue(Cur memory cur) internal pure returns (HostAmount memory value) {
1105
+ (value.host, value.asset, value.meta, value.amount) = unpackHostAmount(cur, Keys.Custody);
1106
+ }
1107
+
1108
+ /// @notice Consume a TRANSACTION block and return its fields as separate values.
1109
+ /// @param cur Cursor; advanced past the block.
1110
+ /// @return from Source account identifier.
1111
+ /// @return to Destination account identifier.
1112
+ /// @return asset Asset identifier.
1113
+ /// @return meta Asset metadata slot.
1114
+ /// @return amount Token amount.
1115
+ function unpackTransaction(
1116
+ Cur memory cur
1117
+ ) internal pure returns (bytes32 from, bytes32 to, bytes32 asset, bytes32 meta, uint amount) {
1118
+ return unpackTransaction(cur, Keys.Transaction);
1119
+ }
1120
+
1121
+ /// @notice Consume a TRANSACTION block and return all fields as a struct.
1122
+ /// @param cur Cursor; advanced past the block.
1123
+ /// @return value Decoded from, to, asset, meta, and amount.
1124
+ function unpackTxValue(Cur memory cur) internal pure returns (Tx memory value) {
1125
+ (value.from, value.to, value.asset, value.meta, value.amount) = unpackTransaction(cur);
1126
+ }
1127
+
1128
+ // Type-specific dynamic decoders
1129
+
1130
+ /// @notice Consume a STEP block and return its sub-command invocation fields.
1131
+ /// The `req` slice covers any additional payload bytes after the fixed head.
1132
+ /// @param cur Cursor; advanced past the block.
1133
+ /// @return target Destination node ID for the sub-command.
1134
+ /// @return value Native value to forward with the call.
1135
+ /// @return req Embedded request bytes for the sub-command.
1136
+ function unpackStep(Cur memory cur) internal pure returns (uint target, uint value, bytes calldata req) {
1137
+ (bytes32 a, bytes32 b, bytes calldata tail) = unpackHead64(cur, Keys.Step);
1138
+ return (uint(a), uint(b), tail);
1139
+ }
1140
+
1141
+ /// @notice Consume a CALL block and return its target invocation fields.
1142
+ /// The `data` slice covers any additional payload bytes after the fixed head.
1143
+ /// @param cur Cursor; advanced past the block.
1144
+ /// @return target Target node ID to call.
1145
+ /// @return value Native value to forward with the call.
1146
+ /// @return data Raw calldata payload for the target.
1147
+ function unpackCall(Cur memory cur) internal pure returns (uint target, uint value, bytes calldata data) {
1148
+ (bytes32 a, bytes32 b, bytes calldata tail) = unpackHead64(cur, Keys.Call);
1149
+ return (uint(a), uint(b), tail);
1150
+ }
1151
+
1152
+ // Type-specific validators
1153
+
1154
+ /// @notice Validate an AUTH block at position `i` and extract deadline and proof.
1155
+ /// Does not advance the cursor.
1156
+ /// @param cur Source cursor.
1157
+ /// @param i Byte offset of the AUTH block.
1158
+ /// @param cid Command ID that the AUTH block must reference.
1159
+ /// @return deadline Expiry timestamp.
1160
+ /// @return proof Raw proof bytes (layout: `[bytes20 signer][bytes65 sig]`).
1161
+ function expectAuth(Cur memory cur, uint i, uint cid) internal pure returns (uint deadline, bytes calldata proof) {
1162
+ (uint abs, uint next) = expect(cur, i, Keys.Auth, 149, 0);
1163
+ if (uint(bytes32(msg.data[abs:abs + 32])) != cid) revert UnexpectedValue();
1164
+ deadline = uint(bytes32(msg.data[abs + 32:abs + 64]));
1165
+ proof = msg.data[abs + 64:cur.offset + next];
1166
+ }
1167
+
1168
+ // -------------------------------------------------------------------------
1169
+ // require* - validate + advance (like consume with content checks)
1170
+ // -------------------------------------------------------------------------
1171
+
1172
+ /// @notice Consume an asset block and assert it matches the expected asset.
1173
+ /// @param cur Cursor; advanced past the block.
1174
+ /// @param key Expected block type key.
1175
+ /// @param asset Expected asset identifier.
1176
+ /// @return meta Metadata slot from the block.
1177
+ /// @return amount Amount from the block.
1178
+ function requireAssetAmount(
1179
+ Cur memory cur,
1180
+ bytes4 key,
1181
+ bytes32 asset
1182
+ ) internal pure returns (bytes32 meta, uint amount) {
1183
+ uint abs = consume(cur, key, 96, 96);
1184
+ if (bytes32(msg.data[abs:abs + 32]) != asset) revert UnexpectedValue();
1185
+ meta = bytes32(msg.data[abs + 32:abs + 64]);
1186
+ amount = uint(bytes32(msg.data[abs + 64:abs + 96]));
1187
+ }
1188
+
1189
+ /// @notice Consume an asset amount block and assert it matches the expected asset and meta.
1190
+ /// @param cur Cursor; advanced past the block.
1191
+ /// @param key Expected block type key.
1192
+ /// @param asset Expected asset identifier.
1193
+ /// @param meta Expected metadata slot.
1194
+ /// @return amount Amount from the block.
1195
+ function requireAssetAmount(
1196
+ Cur memory cur,
1197
+ bytes4 key,
1198
+ bytes32 asset,
1199
+ bytes32 meta
1200
+ ) internal pure returns (uint amount) {
1201
+ uint abs = consume(cur, key, 96, 96);
1202
+ if (bytes32(msg.data[abs:abs + 32]) != asset) revert UnexpectedValue();
1203
+ if (bytes32(msg.data[abs + 32:abs + 64]) != meta) revert UnexpectedValue();
1204
+ amount = uint(bytes32(msg.data[abs + 64:abs + 96]));
1205
+ }
1206
+
1207
+ /// @notice Consume an asset amount block, assert it matches the expected asset, and require the amount to be 1.
1208
+ /// @param cur Cursor; advanced past the block.
1209
+ /// @param key Expected block type key.
1210
+ /// @param asset Expected asset identifier.
1211
+ /// @return meta Metadata slot from the block.
1212
+ function requireUnitAssetAmount(Cur memory cur, bytes4 key, bytes32 asset) internal pure returns (bytes32 meta) {
1213
+ uint abs = consume(cur, key, 96, 96);
1214
+ if (bytes32(msg.data[abs:abs + 32]) != asset) revert UnexpectedValue();
1215
+ meta = bytes32(msg.data[abs + 32:abs + 64]);
1216
+ if (uint(bytes32(msg.data[abs + 64:abs + 96])) != 1) revert UnexpectedValue();
1217
+ }
1218
+
1219
+ /// @notice Consume a MINIMUM block and assert it matches the expected asset and meta.
1220
+ /// @param cur Cursor; advanced past the block.
1221
+ /// @param asset Expected asset identifier.
1222
+ /// @param meta Expected metadata slot.
1223
+ /// @return amount Minimum amount from the block.
1224
+ function requireMinimum(Cur memory cur, bytes32 asset, bytes32 meta) internal pure returns (uint amount) {
1225
+ return requireAssetAmount(cur, Keys.Minimum, asset, meta);
1226
+ }
1227
+
1228
+ /// @notice Consume a host amount block and assert it matches the expected host.
1229
+ /// @param cur Cursor; advanced past the block.
1230
+ /// @param key Expected block type key.
1231
+ /// @param host Expected host node ID.
1232
+ /// @return asset Asset identifier from the block.
1233
+ /// @return meta Metadata slot from the block.
1234
+ /// @return amount Amount from the block.
1235
+ function requireHostAmount(
1236
+ Cur memory cur,
1237
+ bytes4 key,
1238
+ uint host
1239
+ ) internal pure returns (bytes32 asset, bytes32 meta, uint amount) {
1240
+ uint abs = consume(cur, key, 128, 128);
1241
+ if (uint(bytes32(msg.data[abs:abs + 32])) != host) revert UnexpectedValue();
1242
+ asset = bytes32(msg.data[abs + 32:abs + 64]);
1243
+ meta = bytes32(msg.data[abs + 64:abs + 96]);
1244
+ amount = uint(bytes32(msg.data[abs + 96:abs + 128]));
1245
+ }
1246
+
1247
+ /// @notice Consume a host amount block and assert it matches the expected host and asset.
1248
+ /// @param cur Cursor; advanced past the block.
1249
+ /// @param key Expected block type key.
1250
+ /// @param host Expected host node ID.
1251
+ /// @param asset Expected asset identifier.
1252
+ /// @return meta Metadata slot from the block.
1253
+ /// @return amount Amount from the block.
1254
+ function requireHostAmount(
1255
+ Cur memory cur,
1256
+ bytes4 key,
1257
+ uint host,
1258
+ bytes32 asset
1259
+ ) internal pure returns (bytes32 meta, uint amount) {
1260
+ uint abs = consume(cur, key, 128, 128);
1261
+ if (uint(bytes32(msg.data[abs:abs + 32])) != host) revert UnexpectedValue();
1262
+ if (bytes32(msg.data[abs + 32:abs + 64]) != asset) revert UnexpectedValue();
1263
+ meta = bytes32(msg.data[abs + 64:abs + 96]);
1264
+ amount = uint(bytes32(msg.data[abs + 96:abs + 128]));
1265
+ }
1266
+
1267
+ /// @notice Consume a host amount block, assert it matches the expected host and asset, and require the amount to be 1.
1268
+ /// @param cur Cursor; advanced past the block.
1269
+ /// @param key Expected block type key.
1270
+ /// @param host Expected host node ID.
1271
+ /// @param asset Expected asset identifier.
1272
+ /// @return meta Metadata slot from the block.
1273
+ function requireUnitHostAmount(
1274
+ Cur memory cur,
1275
+ bytes4 key,
1276
+ uint host,
1277
+ bytes32 asset
1278
+ ) internal pure returns (bytes32 meta) {
1279
+ uint abs = consume(cur, key, 128, 128);
1280
+ if (uint(bytes32(msg.data[abs:abs + 32])) != host) revert UnexpectedValue();
1281
+ if (bytes32(msg.data[abs + 32:abs + 64]) != asset) revert UnexpectedValue();
1282
+ meta = bytes32(msg.data[abs + 64:abs + 96]);
1283
+ if (uint(bytes32(msg.data[abs + 96:abs + 128])) != 1) revert UnexpectedValue();
1284
+ }
1285
+
1286
+ /// @notice Consume a host account asset block and assert it matches the expected host and account.
1287
+ /// @param cur Cursor; advanced past the block.
1288
+ /// @param key Expected block key.
1289
+ /// @param host Expected host node ID.
1290
+ /// @param account Expected account identifier.
1291
+ /// @return asset Asset identifier from the block.
1292
+ /// @return meta Metadata slot from the block.
1293
+ function requireHostAccountAsset(
1294
+ Cur memory cur,
1295
+ bytes4 key,
1296
+ uint host,
1297
+ bytes32 account
1298
+ ) internal pure returns (bytes32 asset, bytes32 meta) {
1299
+ uint abs = consume(cur, key, 128, 128);
1300
+ if (uint(bytes32(msg.data[abs:abs + 32])) != host) revert UnexpectedValue();
1301
+ if (bytes32(msg.data[abs + 32:abs + 64]) != account) revert UnexpectedValue();
1302
+ asset = bytes32(msg.data[abs + 64:abs + 96]);
1303
+ meta = bytes32(msg.data[abs + 96:abs + 128]);
1304
+ }
1305
+
1306
+ /// @notice Consume a host account asset block, assert it targets the expected host, and return account, asset, and meta.
1307
+ /// @param cur Cursor; advanced past the block.
1308
+ /// @param key Expected block key.
1309
+ /// @param host Expected host node ID.
1310
+ /// @return account Account identifier from the block.
1311
+ /// @return asset Asset identifier from the block.
1312
+ /// @return meta Metadata slot from the block.
1313
+ function requireHostAccountAsset(
1314
+ Cur memory cur,
1315
+ bytes4 key,
1316
+ uint host
1317
+ ) internal pure returns (bytes32 account, bytes32 asset, bytes32 meta) {
1318
+ uint abs = consume(cur, key, 128, 128);
1319
+ if (uint(bytes32(msg.data[abs:abs + 32])) != host) revert UnexpectedValue();
1320
+ account = bytes32(msg.data[abs + 32:abs + 64]);
1321
+ asset = bytes32(msg.data[abs + 64:abs + 96]);
1322
+ meta = bytes32(msg.data[abs + 96:abs + 128]);
1323
+ }
1324
+
1325
+ /// @notice Consume a HOST_ACCOUNT_ASSET form block, assert it targets the expected host, and return account, asset, and meta.
1326
+ /// @param cur Cursor; advanced past the block.
1327
+ /// @param host Expected host node ID.
1328
+ /// @return account Account identifier from the block.
1329
+ /// @return asset Asset identifier from the block.
1330
+ /// @return meta Metadata slot from the block.
1331
+ function requireHostAccountAsset(
1332
+ Cur memory cur,
1333
+ uint host
1334
+ ) internal pure returns (bytes32 account, bytes32 asset, bytes32 meta) {
1335
+ return requireHostAccountAsset(cur, Keys.HostAccountAsset, host);
1336
+ }
1337
+
1338
+ /// @notice Consume an AUTH block at the current position and verify the command ID.
1339
+ /// @param cur Cursor; advanced past the block.
1340
+ /// @param cid Expected command ID.
1341
+ /// @return deadline Expiry timestamp.
1342
+ /// @return proof Raw proof bytes.
1343
+ function requireAuth(Cur memory cur, uint cid) internal pure returns (uint deadline, bytes calldata proof) {
1344
+ (deadline, proof) = expectAuth(cur, cur.i, cid);
1345
+ cur.i += Sizes.Header + 64 + proof.length;
1346
+ }
1347
+
1348
+ // -------------------------------------------------------------------------
1349
+ // Trailing-block helpers (search after bound)
1350
+ // -------------------------------------------------------------------------
1351
+
1352
+ /// @notice Look for a NODE block anywhere in a calldata source and return its value.
1353
+ /// Scans from the start of `source` to the end.
1354
+ /// @param source Calldata block stream to search.
1355
+ /// @param backup Value to return if no NODE block is found.
1356
+ /// @return node Node ID from the NODE block, or `backup` if absent.
1357
+ function resolveNode(bytes calldata source, uint backup) internal pure returns (uint node) {
1358
+ Cur memory cur = open(source);
1359
+ uint i = find(cur, 0, Keys.Node);
1360
+ if (i == cur.len) return backup;
1361
+
1362
+ (uint abs, ) = expect(cur, i, Keys.Node, 32, 32);
1363
+ return uint(bytes32(msg.data[abs:abs + 32]));
1364
+ }
1365
+
1366
+ /// @notice Look for a NODE block anywhere in a calldata source and require a non-zero result.
1367
+ /// Scans from the start of `source` to the end.
1368
+ /// @param source Calldata block stream to search.
1369
+ /// @param backup Value to use if no NODE block is found.
1370
+ /// @return node Node ID from the NODE block, or `backup` if absent.
1371
+ function resolveNodeOrRevert(bytes calldata source, uint backup) internal pure returns (uint node) {
1372
+ node = resolveNode(source, backup);
1373
+ if (node == 0) revert ZeroNode();
1374
+ }
1375
+
1376
+ /// @notice Look for an ACCOUNT block anywhere in a calldata source and return its value.
1377
+ /// Scans from the start of `source` to the end.
1378
+ /// @param source Calldata block stream to search.
1379
+ /// @param backup Account to return if no ACCOUNT block is found.
1380
+ /// @return account Account from the ACCOUNT block, or `backup` if absent.
1381
+ function resolveAccount(bytes calldata source, bytes32 backup) internal pure returns (bytes32 account) {
1382
+ Cur memory cur = open(source);
1383
+ uint i = find(cur, 0, Keys.Account);
1384
+ if (i == cur.len) return backup;
1385
+
1386
+ (uint abs, ) = expect(cur, i, Keys.Account, 32, 32);
1387
+ return bytes32(msg.data[abs:abs + 32]);
1388
+ }
1389
+
1390
+ /// @notice Look for a NODE block after the current run boundary and return its value.
1391
+ /// Searches from `cur.bound` to the end of the source region.
1392
+ /// @param cur Source cursor; `bound` marks the end of the primary run.
1393
+ /// @param backup Value to return if no NODE block is found.
1394
+ /// @return node Node ID from the NODE block, or `backup` if absent.
1395
+ function nodeAfter(Cur memory cur, uint backup) internal pure returns (uint node) {
1396
+ uint i = find(cur, cur.bound, Keys.Node);
1397
+ if (i == cur.len) return backup;
1398
+
1399
+ (uint abs, ) = expect(cur, i, Keys.Node, 32, 32);
1400
+ return uint(bytes32(msg.data[abs:abs + 32]));
1401
+ }
1402
+
1403
+ /// @notice Look for an ACCOUNT block after the current run boundary and return its value.
1404
+ /// Searches from `cur.bound` to the end of the source region.
1405
+ /// @param cur Source cursor; `bound` marks the end of the primary run.
1406
+ /// @param backup Account to return if no ACCOUNT block is found.
1407
+ /// @return account Account from the ACCOUNT block, or `backup` if absent.
1408
+ function accountAfter(Cur memory cur, bytes32 backup) internal pure returns (bytes32 account) {
1409
+ uint i = find(cur, cur.bound, Keys.Account);
1410
+ if (i == cur.len) return backup;
1411
+
1412
+ (uint abs, ) = expect(cur, i, Keys.Account, 32, 32);
1413
+ return bytes32(msg.data[abs:abs + 32]);
1414
+ }
1415
+
1416
+ /// @notice Parse the trailing AUTH block and compute the signed message hash.
1417
+ /// The AUTH block must occupy the final `Sizes.Auth` bytes of the source region
1418
+ /// and must begin after `cur.bound`.
1419
+ /// The signed slice covers from `cur.i` up to (but not including) the AUTH proof bytes.
1420
+ /// @param cur Source cursor; `bound` marks the end of the primary data region.
1421
+ /// @param cid Command ID that the signature must be bound to.
1422
+ /// @return hash keccak256 of the signed message slice.
1423
+ /// @return deadline Expiry timestamp from the AUTH block.
1424
+ /// @return proof Raw proof bytes (layout: `[bytes20 signer][bytes65 sig]`).
1425
+ function authLast(
1426
+ Cur memory cur,
1427
+ uint cid
1428
+ ) internal pure returns (bytes32 hash, uint deadline, bytes calldata proof) {
1429
+ if (cur.len - cur.i < Sizes.Auth) revert MalformedBlocks();
1430
+
1431
+ uint i = cur.len - Sizes.Auth;
1432
+ if (i < cur.bound) revert MalformedBlocks();
1433
+
1434
+ (deadline, proof) = expectAuth(cur, i, cid);
1435
+ hash = keccak256(msg.data[cur.offset + cur.i:cur.offset + cur.len - Sizes.Proof]);
1436
+ }
1437
+ }