@rootzero/contracts 0.7.2 → 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 (71) hide show
  1. package/Commands.sol +15 -20
  2. package/Core.sol +3 -4
  3. package/Cursors.sol +3 -2
  4. package/Events.sol +1 -1
  5. package/Queries.sol +3 -3
  6. package/README.md +18 -19
  7. package/Utils.sol +3 -3
  8. package/blocks/Cursors.sol +937 -551
  9. package/blocks/Keys.sol +60 -34
  10. package/blocks/Schema.sol +112 -122
  11. package/blocks/Writers.sol +476 -301
  12. package/commands/Base.sol +32 -22
  13. package/commands/Burn.sol +14 -12
  14. package/commands/Credit.sol +16 -15
  15. package/commands/Debit.sol +14 -12
  16. package/commands/Deposit.sol +30 -37
  17. package/commands/Pipe.sol +14 -20
  18. package/commands/Provision.sol +19 -49
  19. package/commands/Transfer.sol +9 -18
  20. package/commands/Withdraw.sol +15 -14
  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 +10 -8
  26. package/commands/admin/Execute.sol +38 -0
  27. package/commands/admin/Init.sol +10 -8
  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 +11 -10
  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 +33 -31
  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 +18 -16
  53. package/queries/Balances.sol +21 -19
  54. package/queries/Base.sol +2 -3
  55. package/queries/Positions.sol +32 -24
  56. package/utils/Accounts.sol +14 -13
  57. package/utils/Assets.sol +137 -62
  58. package/utils/Ids.sol +9 -9
  59. package/utils/Layout.sol +5 -3
  60. package/utils/Utils.sol +10 -0
  61. package/commands/Create.sol +0 -42
  62. package/commands/Remove.sol +0 -42
  63. package/commands/Settle.sol +0 -38
  64. package/commands/Stake.sol +0 -47
  65. package/commands/Supply.sol +0 -41
  66. package/commands/admin/Allocate.sol +0 -41
  67. package/core/HostBound.sol +0 -14
  68. package/events/Erc721.sol +0 -20
  69. package/peer/Pull.sol +0 -39
  70. package/peer/Push.sol +0 -45
  71. package/utils/State.sol +0 -22
@@ -1,7 +1,9 @@
1
1
  // SPDX-License-Identifier: GPL-3.0-only
2
2
  pragma solidity ^0.8.33;
3
3
 
4
- import {HostAsset, AssetAmount, HostAmount, Tx, Keys, Sizes} from "./Schema.sol";
4
+ import {AssetAmount, AccountAsset, AccountAmount, HostAmount, HostAccountAsset, Tx} from "../core/Types.sol";
5
+ import {Sizes} from "./Schema.sol";
6
+ import {Keys} from "./Keys.sol";
5
7
  import {ALLOC_SCALE, Writer, Writers} from "./Writers.sol";
6
8
 
7
9
  /// @notice Zero-copy view into a calldata block stream.
@@ -31,20 +33,22 @@ library Cursors {
31
33
  error MalformedBlocks();
32
34
  /// @dev Current block key does not match the expected key, or payload size is out of range.
33
35
  error InvalidBlock();
34
- /// @dev `primeRun` found zero blocks of the expected key; the cursor region is empty.
35
- error ZeroCursor();
36
36
  /// @dev `complete` called but the cursor has not consumed exactly up to `bound`.
37
37
  error IncompleteCursor();
38
+ /// @dev `primeRun` found zero blocks of the expected key; the cursor region is empty.
39
+ error ZeroCursor();
38
40
  /// @dev `primeRun` was called with a zero group size.
39
41
  error ZeroGroup();
40
- /// @dev A recipient field was required but the block or fallback was zero.
41
- error ZeroRecipient();
42
+ /// @dev An account field was required but the block or fallback was zero.
43
+ error ZeroAccount();
42
44
  /// @dev A node field was required but the block or fallback was zero.
43
45
  error ZeroNode();
44
46
  /// @dev A field value did not match the expected value.
45
47
  error UnexpectedValue();
46
48
  /// @dev Input and output block counts are not proportional to their declared group sizes.
47
49
  error BadRatio();
50
+ /// @dev A fixed-width low-level unpacker received an invalid final-word keep length.
51
+ error InvalidKeep();
48
52
 
49
53
  // -------------------------------------------------------------------------
50
54
  // Cursor construction and navigation
@@ -74,6 +78,18 @@ library Cursors {
74
78
  return cur;
75
79
  }
76
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
+
77
93
  /// @notice Read a block header at position `i` without advancing the cursor.
78
94
  /// @param cur Source cursor.
79
95
  /// @param i Byte offset of the block header within the source region.
@@ -87,6 +103,26 @@ library Cursors {
87
103
  if (i + 8 + len > cur.len) revert MalformedBlocks();
88
104
  }
89
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
+
90
126
  /// @notice Validate a block at position `i` and return its payload location.
91
127
  /// Does not advance the cursor.
92
128
  /// @param cur Source cursor.
@@ -141,7 +177,7 @@ library Cursors {
141
177
  /// @return quotient Number of groups represented by the run (`count / group`).
142
178
  function primeRun(Cur memory cur, uint group) internal pure returns (bytes4 key, uint count, uint quotient) {
143
179
  if (group == 0) revert ZeroGroup();
144
- key = cur.len < 4 ? bytes4(0) : bytes4(msg.data[cur.offset:cur.offset + 4]);
180
+ key = cur.i + 4 > cur.len ? bytes4(0) : bytes4(msg.data[cur.offset + cur.i:cur.offset + cur.i + 4]);
145
181
  (count, cur.bound) = countRun(cur, cur.i, key);
146
182
  if (count == 0) revert ZeroCursor();
147
183
  if (count % group != 0) revert BadRatio();
@@ -182,18 +218,71 @@ library Cursors {
182
218
  cur.i = next;
183
219
  }
184
220
 
185
- /// @notice Parse a Bundle block at the current position and return an inner cursor.
186
- /// Advances `cur.i` past the bundle block.
187
- /// @param cur Outer cursor; advanced by one bundle block.
188
- /// @return out Inner cursor scoped to the bundle's embedded block stream.
189
- function bundle(Cur memory cur) internal pure returns (Cur memory out) {
190
- (uint abs, uint next) = expect(cur, cur.i, Keys.Bundle, 0, 0);
191
- uint len = next - (abs - cur.offset);
192
- out.offset = abs;
193
- out.len = len;
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);
194
263
  cur.i = next;
195
264
  }
196
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
+
197
286
  /// @notice Assert that the cursor has consumed exactly up to `bound`.
198
287
  /// Reverts with `IncompleteCursor` if `bound` is zero or `cur.i != cur.bound`.
199
288
  /// @param cur Cursor to check.
@@ -201,6 +290,32 @@ library Cursors {
201
290
  if (cur.bound == 0 || cur.i != cur.bound) revert IncompleteCursor();
202
291
  }
203
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
+
204
319
  /// @notice Assert completion and finalise a writer in one step.
205
320
  /// @param cur Cursor to check.
206
321
  /// @param writer Writer to finalise.
@@ -218,45 +333,26 @@ library Cursors {
218
333
  /// @param key Block type key.
219
334
  /// @param value 32-byte payload.
220
335
  /// @return Encoded block bytes.
221
- function create32(bytes4 key, bytes32 value) internal pure returns (bytes memory) {
336
+ function createBlock32(bytes4 key, bytes32 value) internal pure returns (bytes memory) {
222
337
  return bytes.concat(key, bytes4(uint32(0x20)), value);
223
338
  }
224
339
 
225
- /// @notice Encode a block with a 32-byte fixed head followed by a variable-length tail.
226
- /// @param key Block type key.
227
- /// @param head Fixed 32-byte head payload.
228
- /// @param tail Variable-length payload bytes appended after the head.
229
- /// @return Encoded block bytes.
230
- function createHead32(bytes4 key, bytes32 head, bytes memory tail) internal pure returns (bytes memory) {
231
- return bytes.concat(key, bytes4(uint32(0x20 + tail.length)), head, tail);
232
- }
233
-
234
340
  /// @notice Encode a block with two 32-byte payload words (64-byte payload).
235
341
  /// @param key Block type key.
236
342
  /// @param a First payload word.
237
343
  /// @param b Second payload word.
238
344
  /// @return Encoded block bytes.
239
- function create64(bytes4 key, bytes32 a, bytes32 b) internal pure returns (bytes memory) {
345
+ function createBlock64(bytes4 key, bytes32 a, bytes32 b) internal pure returns (bytes memory) {
240
346
  return bytes.concat(key, bytes4(uint32(0x40)), a, b);
241
347
  }
242
348
 
243
- /// @notice Encode a block with a 64-byte fixed head followed by a variable-length tail.
244
- /// @param key Block type key.
245
- /// @param a First fixed payload word.
246
- /// @param b Second fixed payload word.
247
- /// @param tail Variable-length payload bytes appended after the fixed head.
248
- /// @return Encoded block bytes.
249
- function createHead64(bytes4 key, bytes32 a, bytes32 b, bytes memory tail) internal pure returns (bytes memory) {
250
- return bytes.concat(key, bytes4(uint32(0x40 + tail.length)), a, b, tail);
251
- }
252
-
253
349
  /// @notice Encode a block with three 32-byte payload words (96-byte payload).
254
350
  /// @param key Block type key.
255
351
  /// @param a First payload word.
256
352
  /// @param b Second payload word.
257
353
  /// @param c Third payload word.
258
354
  /// @return Encoded block bytes.
259
- function create96(bytes4 key, bytes32 a, bytes32 b, bytes32 c) internal pure returns (bytes memory) {
355
+ function createBlock96(bytes4 key, bytes32 a, bytes32 b, bytes32 c) internal pure returns (bytes memory) {
260
356
  return bytes.concat(key, bytes4(uint32(0x60)), a, b, c);
261
357
  }
262
358
 
@@ -267,39 +363,65 @@ library Cursors {
267
363
  /// @param c Third payload word.
268
364
  /// @param d Fourth payload word.
269
365
  /// @return Encoded block bytes.
270
- function create128(bytes4 key, bytes32 a, bytes32 b, bytes32 c, bytes32 d) internal pure returns (bytes memory) {
366
+ function createBlock128(
367
+ bytes4 key,
368
+ bytes32 a,
369
+ bytes32 b,
370
+ bytes32 c,
371
+ bytes32 d
372
+ ) internal pure returns (bytes memory) {
271
373
  return bytes.concat(key, bytes4(uint32(0x80)), a, b, c, d);
272
374
  }
273
375
 
274
- /// @notice Encode a BOUNTY block.
275
- /// @param bounty Relayer reward amount.
276
- /// @param relayer Relayer account identifier.
277
- /// @return Encoded BOUNTY block bytes.
278
- function toBountyBlock(uint bounty, bytes32 relayer) internal pure returns (bytes memory) {
279
- return create64(Keys.Bounty, bytes32(bounty), relayer);
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);
280
393
  }
281
394
 
282
- /// @notice Encode a MINIMUMS block.
283
- /// @param a First minimum amount.
284
- /// @param b Second minimum amount.
285
- /// @return Encoded MINIMUMS block bytes.
286
- function toMinimumsBlock(uint a, uint b) internal pure returns (bytes memory) {
287
- return create64(Keys.Minimums, bytes32(a), bytes32(b));
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);
288
402
  }
289
403
 
290
- /// @notice Encode a MAXIMUMS block.
291
- /// @param a First maximum amount.
292
- /// @param b Second maximum amount.
293
- /// @return Encoded MAXIMUMS block bytes.
294
- function toMaximumsBlock(uint a, uint b) internal pure returns (bytes memory) {
295
- return create64(Keys.Maximums, bytes32(a), bytes32(b));
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);
296
417
  }
297
418
 
298
- /// @notice Encode a FEE block.
299
- /// @param amount Fee amount.
300
- /// @return Encoded FEE block bytes.
301
- function toFeeBlock(uint amount) internal pure returns (bytes memory) {
302
- return create32(Keys.Fee, bytes32(amount));
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);
303
425
  }
304
426
 
305
427
  /// @notice Encode a STEP block.
@@ -308,7 +430,16 @@ library Cursors {
308
430
  /// @param request Variable-length nested request payload.
309
431
  /// @return Encoded STEP block bytes.
310
432
  function toStepBlock(uint target, uint value, bytes memory request) internal pure returns (bytes memory) {
311
- return createHead64(Keys.Step, bytes32(target), bytes32(value), request);
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);
312
443
  }
313
444
 
314
445
  /// @notice Encode a BALANCE block.
@@ -317,7 +448,7 @@ library Cursors {
317
448
  /// @param amount Token amount.
318
449
  /// @return Encoded BALANCE block bytes.
319
450
  function toBalanceBlock(bytes32 asset, bytes32 meta, uint amount) internal pure returns (bytes memory) {
320
- return create96(Keys.Balance, asset, meta, bytes32(amount));
451
+ return createBlock96(Keys.Balance, asset, meta, bytes32(amount));
321
452
  }
322
453
 
323
454
  /// @notice Encode a CUSTODY block.
@@ -326,562 +457,699 @@ library Cursors {
326
457
  /// @param meta Asset metadata slot.
327
458
  /// @param amount Token amount.
328
459
  /// @return Encoded CUSTODY block bytes.
329
- function toCustodyBlock(uint host, bytes32 asset, bytes32 meta, uint amount) internal pure returns (bytes memory) {
330
- return create128(Keys.Custody, bytes32(host), asset, meta, bytes32(amount));
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));
331
467
  }
332
468
 
333
469
  // -------------------------------------------------------------------------
334
- // Trailing-block helpers (search after bound)
470
+ // Raw calldata loaders
335
471
  // -------------------------------------------------------------------------
336
472
 
337
- /// @notice Look for a NODE block after the current run boundary and return its value.
338
- /// Searches from `cur.bound` to the end of the source region.
339
- /// @param cur Source cursor; `bound` marks the end of the primary run.
340
- /// @param backup Value to return if no NODE block is found.
341
- /// @return node Node ID from the NODE block, or `backup` if absent.
342
- function nodeAfter(Cur memory cur, uint backup) internal pure returns (uint node) {
343
- uint i = find(cur, cur.bound, Keys.Node);
344
- if (i == cur.len) return backup;
345
-
346
- (uint abs, ) = expect(cur, i, Keys.Node, 32, 32);
347
- return uint(bytes32(msg.data[abs:abs + 32]));
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
+ }
348
481
  }
349
482
 
350
- /// @notice Look for a RECIPIENT block after the current run boundary and return its value.
351
- /// Searches from `cur.bound` to the end of the source region.
352
- /// @param cur Source cursor; `bound` marks the end of the primary run.
353
- /// @param backup Account to return if no RECIPIENT block is found.
354
- /// @return account Recipient account from the RECIPIENT block, or `backup` if absent.
355
- function recipientAfter(Cur memory cur, bytes32 backup) internal pure returns (bytes32 account) {
356
- uint i = find(cur, cur.bound, Keys.Recipient);
357
- if (i == cur.len) return backup;
358
-
359
- (uint abs, ) = expect(cur, i, Keys.Recipient, 32, 32);
360
- return bytes32(msg.data[abs:abs + 32]);
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
+ }
361
493
  }
362
494
 
363
- /// @notice Parse the trailing AUTH block and compute the signed message hash.
364
- /// The AUTH block must occupy the final `Sizes.Auth` bytes of the source region
365
- /// and must begin after `cur.bound`.
366
- /// The signed slice covers from `cur.i` up to (but not including) the AUTH proof bytes.
367
- /// @param cur Source cursor; `bound` marks the end of the primary data region.
368
- /// @param cid Command ID that the signature must be bound to.
369
- /// @return hash keccak256 of the signed message slice.
370
- /// @return deadline Expiry timestamp from the AUTH block.
371
- /// @return proof Raw proof bytes (layout: `[bytes20 signer][bytes65 sig]`).
372
- function authLast(
373
- Cur memory cur,
374
- uint cid
375
- ) internal pure returns (bytes32 hash, uint deadline, bytes calldata proof) {
376
- if (cur.len - cur.i < Sizes.Auth) revert MalformedBlocks();
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
+ }
377
508
 
378
- uint i = cur.len - Sizes.Auth;
379
- if (i < cur.bound) revert MalformedBlocks();
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
+ }
380
524
 
381
- (deadline, proof) = expectAuth(cur, i, cid);
382
- hash = keccak256(msg.data[cur.offset + cur.i:cur.offset + cur.len - Sizes.Proof]);
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
+ }
383
541
  }
384
542
 
385
543
  // -------------------------------------------------------------------------
386
- // unpack* consume current block and decode payload fields
544
+ // unpack* - consume current block and decode payload fields
387
545
  // -------------------------------------------------------------------------
388
546
 
389
- /// @notice Consume a BALANCE block and return its fields as a struct.
390
- /// @param cur Cursor; advanced past the block.
391
- /// @return value Decoded asset, meta, and amount.
392
- function unpackBalanceValue(Cur memory cur) internal pure returns (AssetAmount memory value) {
393
- uint abs = consume(cur, Keys.Balance, 96, 96);
394
- value.asset = bytes32(msg.data[abs:abs + 32]);
395
- value.meta = bytes32(msg.data[abs + 32:abs + 64]);
396
- value.amount = uint(bytes32(msg.data[abs + 64:abs + 96]));
397
- }
547
+ // Generic fixed-width decoders
398
548
 
399
- /// @notice Consume an AMOUNT block and return its fields as a struct.
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.
400
551
  /// @param cur Cursor; advanced past the block.
401
- /// @return value Decoded asset, meta, and amount.
402
- function unpackAmountValue(Cur memory cur) internal pure returns (AssetAmount memory value) {
403
- uint abs = consume(cur, Keys.Amount, 96, 96);
404
- value.asset = bytes32(msg.data[abs:abs + 32]);
405
- value.meta = bytes32(msg.data[abs + 32:abs + 64]);
406
- value.amount = uint(bytes32(msg.data[abs + 64:abs + 96]));
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;
407
558
  }
408
559
 
409
- /// @notice Consume an AMOUNT block and return its fields as separate values.
560
+ /// @notice Consume a dynamic block with a single bytes32 payload.
410
561
  /// @param cur Cursor; advanced past the block.
411
- /// @return asset Asset identifier.
412
- /// @return meta Asset metadata slot.
413
- /// @return amount Token amount.
414
- function unpackAmount(Cur memory cur) internal pure returns (bytes32 asset, bytes32 meta, uint amount) {
415
- uint abs = consume(cur, Keys.Amount, 96, 96);
416
- asset = bytes32(msg.data[abs:abs + 32]);
417
- meta = bytes32(msg.data[abs + 32:abs + 64]);
418
- amount = uint(bytes32(msg.data[abs + 64:abs + 96]));
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);
419
570
  }
420
571
 
421
- /// @notice Consume a BALANCE block and return its fields as separate values.
572
+ /// @notice Consume a dynamic block with two bytes32 payload words.
422
573
  /// @param cur Cursor; advanced past the block.
423
- /// @return asset Asset identifier.
424
- /// @return meta Asset metadata slot.
425
- /// @return amount Token amount.
426
- function unpackBalance(Cur memory cur) internal pure returns (bytes32 asset, bytes32 meta, uint amount) {
427
- uint abs = consume(cur, Keys.Balance, 96, 96);
428
- asset = bytes32(msg.data[abs:abs + 32]);
429
- meta = bytes32(msg.data[abs + 32:abs + 64]);
430
- amount = uint(bytes32(msg.data[abs + 64:abs + 96]));
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);
431
584
  }
432
585
 
433
- /// @notice Consume a MINIMUM block and return its fields as separate values.
586
+ /// @notice Consume a dynamic block with three bytes32 payload words.
434
587
  /// @param cur Cursor; advanced past the block.
435
- /// @return asset Asset identifier.
436
- /// @return meta Asset metadata slot.
437
- /// @return amount Minimum acceptable amount.
438
- function unpackMinimum(Cur memory cur) internal pure returns (bytes32 asset, bytes32 meta, uint amount) {
439
- uint abs = consume(cur, Keys.Minimum, 96, 96);
440
- asset = bytes32(msg.data[abs:abs + 32]);
441
- meta = bytes32(msg.data[abs + 32:abs + 64]);
442
- amount = uint(bytes32(msg.data[abs + 64:abs + 96]));
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);
443
600
  }
444
601
 
445
- /// @notice Consume a MINIMUM block and return its fields as a struct.
602
+ /// @notice Consume a dynamic block with a 128-byte payload (four 32-byte words).
446
603
  /// @param cur Cursor; advanced past the block.
447
- /// @return value Decoded asset, meta, and minimum amount.
448
- function unpackMinimumValue(Cur memory cur) internal pure returns (AssetAmount memory value) {
449
- uint abs = consume(cur, Keys.Minimum, 96, 96);
450
- value.asset = bytes32(msg.data[abs:abs + 32]);
451
- value.meta = bytes32(msg.data[abs + 32:abs + 64]);
452
- value.amount = uint(bytes32(msg.data[abs + 64:abs + 96]));
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);
453
622
  }
454
623
 
455
- /// @notice Consume a MINIMUMS block and return the two minimum amounts.
624
+ /// @notice Consume a dynamic block with a 160-byte payload (five 32-byte words).
456
625
  /// @param cur Cursor; advanced past the block.
457
- /// @return a First minimum amount.
458
- /// @return b Second minimum amount.
459
- function unpackMinimums(Cur memory cur) internal pure returns (uint a, uint b) {
460
- uint abs = consume(cur, Keys.Minimums, 64, 64);
461
- a = uint(bytes32(msg.data[abs:abs + 32]));
462
- b = uint(bytes32(msg.data[abs + 32:abs + 64]));
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);
463
646
  }
464
647
 
465
- /// @notice Consume a MAXIMUMS block and return the two maximum amounts.
648
+ /// @notice Consume a dynamic block with a single uint payload.
466
649
  /// @param cur Cursor; advanced past the block.
467
- /// @return a First maximum amount.
468
- /// @return b Second maximum amount.
469
- function unpackMaximums(Cur memory cur) internal pure returns (uint a, uint b) {
470
- uint abs = consume(cur, Keys.Maximums, 64, 64);
471
- a = uint(bytes32(msg.data[abs:abs + 32]));
472
- b = uint(bytes32(msg.data[abs + 32:abs + 64]));
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));
473
654
  }
474
655
 
475
- /// @notice Consume a MAXIMUM block and return its fields as separate values.
656
+ /// @notice Consume a dynamic block with two uint payload words.
476
657
  /// @param cur Cursor; advanced past the block.
477
- /// @return asset Asset identifier.
478
- /// @return meta Asset metadata slot.
479
- /// @return amount Maximum allowable spend.
480
- function unpackMaximum(Cur memory cur) internal pure returns (bytes32 asset, bytes32 meta, uint amount) {
481
- uint abs = consume(cur, Keys.Maximum, 96, 96);
482
- asset = bytes32(msg.data[abs:abs + 32]);
483
- meta = bytes32(msg.data[abs + 32:abs + 64]);
484
- amount = uint(bytes32(msg.data[abs + 64:abs + 96]));
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));
485
664
  }
486
665
 
487
- /// @notice Consume a MAXIMUM block and return its fields as a struct.
666
+ /// @notice Consume a dynamic block with three uint payload words.
488
667
  /// @param cur Cursor; advanced past the block.
489
- /// @return value Decoded asset, meta, and maximum amount.
490
- function unpackMaximumValue(Cur memory cur) internal pure returns (AssetAmount memory value) {
491
- uint abs = consume(cur, Keys.Maximum, 96, 96);
492
- value.asset = bytes32(msg.data[abs:abs + 32]);
493
- value.meta = bytes32(msg.data[abs + 32:abs + 64]);
494
- value.amount = uint(bytes32(msg.data[abs + 64:abs + 96]));
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));
495
675
  }
496
676
 
497
- /// @notice Consume a STEP block and return its sub-command invocation fields.
498
- /// The `req` slice covers any additional payload bytes after the fixed head.
499
- /// @param cur Cursor; advanced past the block.
500
- /// @return target Destination node ID for the sub-command.
501
- /// @return value Native value to forward with the call.
502
- /// @return req Embedded request bytes for the sub-command.
503
- function unpackStep(Cur memory cur) internal pure returns (uint target, uint value, bytes calldata req) {
504
- uint abs = consume(cur, Keys.Step, 64, 0);
505
- target = uint(bytes32(msg.data[abs:abs + 32]));
506
- value = uint(bytes32(msg.data[abs + 32:abs + 64]));
507
- req = msg.data[abs + 64:cur.offset + cur.i];
508
- }
677
+ // Generic fixed-head decoders
509
678
 
510
- /// @notice Consume a RECIPIENT block and return the account.
679
+ /// @notice Consume a dynamic block with a 32-byte fixed head followed by a variable-length tail.
511
680
  /// @param cur Cursor; advanced past the block.
512
- /// @return account Destination account identifier.
513
- function unpackRecipient(Cur memory cur) internal pure returns (bytes32 account) {
514
- uint abs = consume(cur, Keys.Recipient, 32, 32);
515
- account = bytes32(msg.data[abs:abs + 32]);
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];
516
688
  }
517
689
 
518
- /// @notice Consume a RATE block and return the value.
690
+ /// @notice Consume a dynamic block with a 64-byte fixed head followed by a variable-length tail.
519
691
  /// @param cur Cursor; advanced past the block.
520
- /// @return value Encoded ratio or rate.
521
- function unpackRate(Cur memory cur) internal pure returns (uint value) {
522
- uint abs = consume(cur, Keys.Rate, 32, 32);
523
- value = uint(bytes32(msg.data[abs:abs + 32]));
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];
524
704
  }
525
705
 
526
- /// @notice Consume a QUANTITY block and return the amount.
706
+ /// @notice Consume a dynamic block with a 96-byte fixed head followed by a variable-length tail.
527
707
  /// @param cur Cursor; advanced past the block.
528
- /// @return amount Scalar quantity value.
529
- function unpackQuantity(Cur memory cur) internal pure returns (uint amount) {
530
- uint abs = consume(cur, Keys.Quantity, 32, 32);
531
- amount = uint(bytes32(msg.data[abs:abs + 32]));
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];
532
722
  }
533
723
 
534
- /// @notice Consume a FEE block and return the amount.
724
+ /// @notice Consume a dynamic block with a 128-byte fixed head followed by a variable-length tail.
535
725
  /// @param cur Cursor; advanced past the block.
536
- /// @return amount Fee amount.
537
- function unpackFee(Cur memory cur) internal pure returns (uint amount) {
538
- uint abs = consume(cur, Keys.Fee, 32, 32);
539
- amount = uint(bytes32(msg.data[abs:abs + 32]));
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];
540
742
  }
541
743
 
542
- /// @notice Consume a BOUNDS block and return the signed min and max values.
543
- /// @param cur Cursor; advanced past the block.
544
- /// @return min Lower signed bound.
545
- /// @return max Upper signed bound.
546
- function unpackBounds(Cur memory cur) internal pure returns (int min, int max) {
547
- uint abs = consume(cur, Keys.Bounds, 64, 64);
548
- assembly ("memory-safe") {
549
- min := calldataload(abs)
550
- max := calldataload(add(abs, 0x20))
551
- }
552
- }
744
+ // Generic typed-shape decoders
553
745
 
554
- /// @notice Consume an ASSET block and return the asset descriptor fields.
746
+ /// @notice Consume a fixed-size asset amount block and return asset, meta, and amount.
555
747
  /// @param cur Cursor; advanced past the block.
748
+ /// @param key Expected block key.
556
749
  /// @return asset Asset identifier.
557
750
  /// @return meta Asset metadata slot.
558
- function unpackAsset(Cur memory cur) internal pure returns (bytes32 asset, bytes32 meta) {
559
- uint abs = consume(cur, Keys.Asset, 64, 64);
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);
560
757
  asset = bytes32(msg.data[abs:abs + 32]);
561
758
  meta = bytes32(msg.data[abs + 32:abs + 64]);
759
+ amount = uint(bytes32(msg.data[abs + 64:abs + 96]));
562
760
  }
563
761
 
564
- /// @notice Consume a FUNDING block and return the host and amount.
762
+ /// @notice Consume a fixed-size account amount block and return account, asset, meta, and amount.
565
763
  /// @param cur Cursor; advanced past the block.
566
- /// @return host Host node ID receiving the funding.
567
- /// @return amount Funding amount.
568
- function unpackFunding(Cur memory cur) internal pure returns (uint host, uint amount) {
569
- uint abs = consume(cur, Keys.Funding, 64, 64);
570
- host = uint(bytes32(msg.data[abs:abs + 32]));
571
- amount = uint(bytes32(msg.data[abs + 32:abs + 64]));
572
- }
573
-
574
- /// @notice Consume a BOUNTY block and return the reward amount and relayer.
575
- /// @param cur Cursor; advanced past the block.
576
- /// @return amount Relayer reward amount.
577
- /// @return relayer Relayer account identifier.
578
- function unpackBounty(Cur memory cur) internal pure returns (uint amount, bytes32 relayer) {
579
- uint abs = consume(cur, Keys.Bounty, 64, 64);
580
- amount = uint(bytes32(msg.data[abs:abs + 32]));
581
- relayer = bytes32(msg.data[abs + 32:abs + 64]);
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]));
582
778
  }
583
779
 
584
- /// @notice Consume a LISTING block and return its fields as separate values.
780
+ /// @notice Consume a fixed-size host amount block and return host, asset, meta, and amount.
585
781
  /// @param cur Cursor; advanced past the block.
586
- /// @return host Host node ID that lists the asset.
782
+ /// @param key Expected block key.
783
+ /// @return host Host node ID.
587
784
  /// @return asset Asset identifier.
588
785
  /// @return meta Asset metadata slot.
589
- function unpackListing(Cur memory cur) internal pure returns (uint host, bytes32 asset, bytes32 meta) {
590
- uint abs = consume(cur, Keys.Listing, 96, 96);
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);
591
792
  host = uint(bytes32(msg.data[abs:abs + 32]));
592
793
  asset = bytes32(msg.data[abs + 32:abs + 64]);
593
794
  meta = bytes32(msg.data[abs + 64:abs + 96]);
795
+ amount = uint(bytes32(msg.data[abs + 96:abs + 128]));
594
796
  }
595
797
 
596
- /// @notice Consume a LISTING block and return its fields as a struct.
798
+ /// @notice Consume a fixed-size host account asset block and return host, account, asset, and meta.
597
799
  /// @param cur Cursor; advanced past the block.
598
- /// @return value Decoded host, asset, and meta.
599
- function unpackListingValue(Cur memory cur) internal pure returns (HostAsset memory value) {
600
- uint abs = consume(cur, Keys.Listing, 96, 96);
601
- value.host = uint(bytes32(msg.data[abs:abs + 32]));
602
- value.asset = bytes32(msg.data[abs + 32:abs + 64]);
603
- value.meta = bytes32(msg.data[abs + 64:abs + 96]);
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);
604
843
  }
605
844
 
606
845
  /// @notice Consume a NODE block and return the node ID.
607
846
  /// @param cur Cursor; advanced past the block.
608
847
  /// @return node Node identifier.
609
848
  function unpackNode(Cur memory cur) internal pure returns (uint node) {
610
- uint abs = consume(cur, Keys.Node, 32, 32);
611
- node = uint(bytes32(msg.data[abs:abs + 32]));
849
+ node = uint(unpack32(cur, Keys.Node, 32));
612
850
  }
613
851
 
614
- /// @notice Consume a ROUTE block and return the raw payload as a calldata slice.
615
- /// The payload length is variable; the returned slice covers the entire payload.
852
+ /// @notice Consume a RATE block and return the value.
616
853
  /// @param cur Cursor; advanced past the block.
617
- /// @return data Raw route payload bytes.
618
- function unpackRoute(Cur memory cur) internal pure returns (bytes calldata data) {
619
- (uint abs, uint next) = expect(cur, cur.i, Keys.Route, 0, 0);
620
- data = msg.data[abs:cur.offset + next];
621
- cur.i = next;
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));
622
857
  }
623
858
 
624
- /// @notice Consume a QUERY block and return the raw payload as a calldata slice.
625
- /// The payload length is variable; the returned slice covers the entire payload.
859
+ /// @notice Consume a QUANTITY block and return the amount.
626
860
  /// @param cur Cursor; advanced past the block.
627
- /// @return data Raw query payload bytes.
628
- function unpackQuery(Cur memory cur) internal pure returns (bytes calldata data) {
629
- (uint abs, uint next) = expect(cur, cur.i, Keys.Query, 0, 0);
630
- data = msg.data[abs:cur.offset + next];
631
- cur.i = next;
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));
632
864
  }
633
865
 
634
- /// @notice Consume a RESPONSE block and return the raw payload as a calldata slice.
635
- /// The payload length is variable; the returned slice covers the entire payload.
866
+ /// @notice Consume a FEE block and return the amount.
636
867
  /// @param cur Cursor; advanced past the block.
637
- /// @return data Raw response payload bytes.
638
- function unpackResponse(Cur memory cur) internal pure returns (bytes calldata data) {
639
- (uint abs, uint next) = expect(cur, cur.i, Keys.Response, 0, 0);
640
- data = msg.data[abs:cur.offset + next];
641
- cur.i = next;
868
+ /// @return amount Fee amount.
869
+ function unpackFee(Cur memory cur) internal pure returns (uint amount) {
870
+ amount = uint(unpack32(cur, Keys.Fee, 32));
642
871
  }
643
872
 
644
- /// @notice Consume a PATH block and return the raw payload as a calldata slice.
645
- /// The payload length is variable; the returned slice covers the entire payload.
873
+ /// @notice Consume an ASSET block and return the asset descriptor fields.
646
874
  /// @param cur Cursor; advanced past the block.
647
- /// @return data Raw path payload bytes.
648
- function unpackPath(Cur memory cur) internal pure returns (bytes calldata data) {
649
- (uint abs, uint next) = expect(cur, cur.i, Keys.Path, 0, 0);
650
- data = msg.data[abs:cur.offset + next];
651
- cur.i = next;
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);
652
879
  }
653
880
 
654
- /// @notice Consume a ROUTE block with a single uint payload.
881
+ /// @notice Consume an ACCOUNT_ASSET form block and return its fields as separate values.
655
882
  /// @param cur Cursor; advanced past the block.
656
- /// @return value Decoded uint value.
657
- function unpackRouteUint(Cur memory cur) internal pure returns (uint value) {
658
- uint abs = consume(cur, Keys.Route, 32, 32);
659
- value = uint(bytes32(msg.data[abs:abs + 32]));
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]);
660
891
  }
661
892
 
662
- /// @notice Consume a QUERY block with a single uint payload.
893
+ /// @notice Consume an ACCOUNT_ASSET form block and return its fields as a struct.
663
894
  /// @param cur Cursor; advanced past the block.
664
- /// @return value Decoded uint value.
665
- function unpackQueryUint(Cur memory cur) internal pure returns (uint value) {
666
- uint abs = consume(cur, Keys.Query, 32, 32);
667
- value = uint(bytes32(msg.data[abs:abs + 32]));
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);
668
898
  }
669
899
 
670
- /// @notice Consume a RESPONSE block with a single uint payload.
900
+ /// @notice Consume a RELOCATION block and return the host and amount.
671
901
  /// @param cur Cursor; advanced past the block.
672
- /// @return value Decoded uint value.
673
- function unpackResponseUint(Cur memory cur) internal pure returns (uint value) {
674
- uint abs = consume(cur, Keys.Response, 32, 32);
675
- value = uint(bytes32(msg.data[abs:abs + 32]));
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);
676
906
  }
677
907
 
678
- /// @notice Consume a ROUTE block with two uint payload words.
908
+ /// @notice Consume a BOUNTY block and return the reward amount and relayer.
679
909
  /// @param cur Cursor; advanced past the block.
680
- /// @return a First decoded uint.
681
- /// @return b Second decoded uint.
682
- function unpackRoute2Uint(Cur memory cur) internal pure returns (uint a, uint b) {
683
- uint abs = consume(cur, Keys.Route, 64, 64);
684
- a = uint(bytes32(msg.data[abs:abs + 32]));
685
- b = uint(bytes32(msg.data[abs + 32:abs + 64]));
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;
686
916
  }
687
917
 
688
- /// @notice Consume a QUERY block with two uint payload words.
918
+ /// @notice Consume a BOUNDS block and return the signed min and max values.
689
919
  /// @param cur Cursor; advanced past the block.
690
- /// @return a First decoded uint.
691
- /// @return b Second decoded uint.
692
- function unpackQuery2Uint(Cur memory cur) internal pure returns (uint a, uint b) {
693
- uint abs = consume(cur, Keys.Query, 64, 64);
694
- a = uint(bytes32(msg.data[abs:abs + 32]));
695
- b = uint(bytes32(msg.data[abs + 32:abs + 64]));
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
+ }
696
928
  }
697
929
 
698
- /// @notice Consume a RESPONSE block with two uint payload words.
930
+ /// @notice Consume an AMOUNT block and return its fields as separate values.
699
931
  /// @param cur Cursor; advanced past the block.
700
- /// @return a First decoded uint.
701
- /// @return b Second decoded uint.
702
- function unpackResponse2Uint(Cur memory cur) internal pure returns (uint a, uint b) {
703
- uint abs = consume(cur, Keys.Response, 64, 64);
704
- a = uint(bytes32(msg.data[abs:abs + 32]));
705
- b = uint(bytes32(msg.data[abs + 32:abs + 64]));
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);
706
937
  }
707
938
 
708
- /// @notice Consume a ROUTE block with three uint payload words.
939
+ /// @notice Consume an AMOUNT block and return its fields as a struct.
709
940
  /// @param cur Cursor; advanced past the block.
710
- /// @return a First decoded uint.
711
- /// @return b Second decoded uint.
712
- /// @return c Third decoded uint.
713
- function unpackRoute3Uint(Cur memory cur) internal pure returns (uint a, uint b, uint c) {
714
- uint abs = consume(cur, Keys.Route, 96, 96);
715
- a = uint(bytes32(msg.data[abs:abs + 32]));
716
- b = uint(bytes32(msg.data[abs + 32:abs + 64]));
717
- c = uint(bytes32(msg.data[abs + 64:abs + 96]));
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);
718
944
  }
719
945
 
720
- /// @notice Consume a QUERY block with three uint payload words.
946
+ /// @notice Consume a BALANCE block and return its fields as separate values.
721
947
  /// @param cur Cursor; advanced past the block.
722
- /// @return a First decoded uint.
723
- /// @return b Second decoded uint.
724
- /// @return c Third decoded uint.
725
- function unpackQuery3Uint(Cur memory cur) internal pure returns (uint a, uint b, uint c) {
726
- uint abs = consume(cur, Keys.Query, 96, 96);
727
- a = uint(bytes32(msg.data[abs:abs + 32]));
728
- b = uint(bytes32(msg.data[abs + 32:abs + 64]));
729
- c = uint(bytes32(msg.data[abs + 64:abs + 96]));
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);
730
953
  }
731
954
 
732
- /// @notice Consume a RESPONSE block with three uint payload words.
955
+ /// @notice Consume a BALANCE block and return its fields as a struct.
733
956
  /// @param cur Cursor; advanced past the block.
734
- /// @return a First decoded uint.
735
- /// @return b Second decoded uint.
736
- /// @return c Third decoded uint.
737
- function unpackResponse3Uint(Cur memory cur) internal pure returns (uint a, uint b, uint c) {
738
- uint abs = consume(cur, Keys.Response, 96, 96);
739
- a = uint(bytes32(msg.data[abs:abs + 32]));
740
- b = uint(bytes32(msg.data[abs + 32:abs + 64]));
741
- c = uint(bytes32(msg.data[abs + 64:abs + 96]));
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);
742
960
  }
743
961
 
744
- /// @notice Consume a ROUTE block with a single bytes32 payload.
962
+ /// @notice Consume a MINIMUM block and return its fields as separate values.
745
963
  /// @param cur Cursor; advanced past the block.
746
- /// @return value Decoded bytes32.
747
- function unpackRoute32(Cur memory cur) internal pure returns (bytes32 value) {
748
- uint abs = consume(cur, Keys.Route, 32, 32);
749
- value = bytes32(msg.data[abs:abs + 32]);
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);
750
969
  }
751
970
 
752
- /// @notice Consume a QUERY block with a single bytes32 payload.
971
+ /// @notice Consume a MINIMUM block and return its fields as a struct.
753
972
  /// @param cur Cursor; advanced past the block.
754
- /// @return value Decoded bytes32.
755
- function unpackQuery32(Cur memory cur) internal pure returns (bytes32 value) {
756
- uint abs = consume(cur, Keys.Query, 32, 32);
757
- value = bytes32(msg.data[abs:abs + 32]);
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);
758
976
  }
759
977
 
760
- /// @notice Consume a RESPONSE block with a single bytes32 payload.
978
+ /// @notice Consume a MAXIMUM block and return its fields as separate values.
761
979
  /// @param cur Cursor; advanced past the block.
762
- /// @return value Decoded bytes32.
763
- function unpackResponse32(Cur memory cur) internal pure returns (bytes32 value) {
764
- uint abs = consume(cur, Keys.Response, 32, 32);
765
- value = bytes32(msg.data[abs:abs + 32]);
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);
766
985
  }
767
986
 
768
- /// @notice Consume a ROUTE block with two bytes32 payload words.
987
+ /// @notice Consume a MAXIMUM block and return its fields as a struct.
769
988
  /// @param cur Cursor; advanced past the block.
770
- /// @return a First decoded bytes32.
771
- /// @return b Second decoded bytes32.
772
- function unpackRoute64(Cur memory cur) internal pure returns (bytes32 a, bytes32 b) {
773
- uint abs = consume(cur, Keys.Route, 64, 64);
774
- a = bytes32(msg.data[abs:abs + 32]);
775
- b = bytes32(msg.data[abs + 32:abs + 64]);
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);
776
992
  }
777
993
 
778
- /// @notice Consume a QUERY block with two bytes32 payload words.
994
+ /// @notice Consume a HOST_ACCOUNT_ASSET form block and return its fields as separate values.
779
995
  /// @param cur Cursor; advanced past the block.
780
- /// @return a First decoded bytes32.
781
- /// @return b Second decoded bytes32.
782
- function unpackQuery64(Cur memory cur) internal pure returns (bytes32 a, bytes32 b) {
783
- uint abs = consume(cur, Keys.Query, 64, 64);
784
- a = bytes32(msg.data[abs:abs + 32]);
785
- b = bytes32(msg.data[abs + 32:abs + 64]);
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);
786
1004
  }
787
1005
 
788
- /// @notice Consume a RESPONSE block with two bytes32 payload words.
1006
+ /// @notice Consume a HOST_ACCOUNT_ASSET form block and return its fields as a struct.
789
1007
  /// @param cur Cursor; advanced past the block.
790
- /// @return a First decoded bytes32.
791
- /// @return b Second decoded bytes32.
792
- function unpackResponse64(Cur memory cur) internal pure returns (bytes32 a, bytes32 b) {
793
- uint abs = consume(cur, Keys.Response, 64, 64);
794
- a = bytes32(msg.data[abs:abs + 32]);
795
- b = bytes32(msg.data[abs + 32:abs + 64]);
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);
796
1011
  }
797
1012
 
798
- /// @notice Consume a ROUTE block with three bytes32 payload words.
1013
+ /// @notice Consume a PAYOUT block and return its fields as separate values.
799
1014
  /// @param cur Cursor; advanced past the block.
800
- /// @return a First decoded bytes32.
801
- /// @return b Second decoded bytes32.
802
- /// @return c Third decoded bytes32.
803
- function unpackRoute96(Cur memory cur) internal pure returns (bytes32 a, bytes32 b, bytes32 c) {
804
- uint abs = consume(cur, Keys.Route, 96, 96);
805
- a = bytes32(msg.data[abs:abs + 32]);
806
- b = bytes32(msg.data[abs + 32:abs + 64]);
807
- c = bytes32(msg.data[abs + 64:abs + 96]);
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);
808
1023
  }
809
1024
 
810
- /// @notice Consume a QUERY block with three bytes32 payload words.
1025
+ /// @notice Consume a PAYOUT block and return its fields as a struct.
811
1026
  /// @param cur Cursor; advanced past the block.
812
- /// @return a First decoded bytes32.
813
- /// @return b Second decoded bytes32.
814
- /// @return c Third decoded bytes32.
815
- function unpackQuery96(Cur memory cur) internal pure returns (bytes32 a, bytes32 b, bytes32 c) {
816
- uint abs = consume(cur, Keys.Query, 96, 96);
817
- a = bytes32(msg.data[abs:abs + 32]);
818
- b = bytes32(msg.data[abs + 32:abs + 64]);
819
- c = bytes32(msg.data[abs + 64:abs + 96]);
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);
820
1030
  }
821
1031
 
822
- /// @notice Consume a RESPONSE block with three bytes32 payload words.
1032
+ /// @notice Consume an ACCOUNT_AMOUNT form block and return its fields as separate values.
823
1033
  /// @param cur Cursor; advanced past the block.
824
- /// @return a First decoded bytes32.
825
- /// @return b Second decoded bytes32.
826
- /// @return c Third decoded bytes32.
827
- function unpackResponse96(Cur memory cur) internal pure returns (bytes32 a, bytes32 b, bytes32 c) {
828
- uint abs = consume(cur, Keys.Response, 96, 96);
829
- a = bytes32(msg.data[abs:abs + 32]);
830
- b = bytes32(msg.data[abs + 32:abs + 64]);
831
- c = bytes32(msg.data[abs + 64:abs + 96]);
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);
832
1042
  }
833
1043
 
834
- /// @notice Consume a CUSTODY block and return its fields as a struct.
1044
+ /// @notice Consume an ACCOUNT_AMOUNT form block and return its fields as a struct.
835
1045
  /// @param cur Cursor; advanced past the block.
836
- /// @return value Decoded host, asset, meta, and amount.
837
- function unpackCustodyValue(Cur memory cur) internal pure returns (HostAmount memory value) {
838
- uint abs = consume(cur, Keys.Custody, 128, 128);
839
- value.host = uint(bytes32(msg.data[abs:abs + 32]));
840
- value.asset = bytes32(msg.data[abs + 32:abs + 64]);
841
- value.meta = bytes32(msg.data[abs + 64:abs + 96]);
842
- value.amount = uint(bytes32(msg.data[abs + 96:abs + 128]));
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);
843
1049
  }
844
1050
 
845
1051
  /// @notice Consume an ALLOCATION block and return its fields as separate values.
846
1052
  /// @param cur Cursor; advanced past the block.
847
- /// @return host Host node ID of the allocation.
1053
+ /// @return host Host node ID.
848
1054
  /// @return asset Asset identifier.
849
1055
  /// @return meta Asset metadata slot.
850
- /// @return amount Allocated amount.
851
- function unpackAllocation(Cur memory cur) internal pure returns (uint host, bytes32 asset, bytes32 meta, uint amount) {
852
- uint abs = consume(cur, Keys.Allocation, 128, 128);
853
- host = uint(bytes32(msg.data[abs:abs + 32]));
854
- asset = bytes32(msg.data[abs + 32:abs + 64]);
855
- meta = bytes32(msg.data[abs + 64:abs + 96]);
856
- amount = uint(bytes32(msg.data[abs + 96:abs + 128]));
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);
857
1061
  }
858
1062
 
859
1063
  /// @notice Consume an ALLOCATION block and return its fields as a struct.
860
1064
  /// @param cur Cursor; advanced past the block.
861
1065
  /// @return value Decoded host, asset, meta, and amount.
862
1066
  function unpackAllocationValue(Cur memory cur) internal pure returns (HostAmount memory value) {
863
- uint abs = consume(cur, Keys.Allocation, 128, 128);
864
- value.host = uint(bytes32(msg.data[abs:abs + 32]));
865
- value.asset = bytes32(msg.data[abs + 32:abs + 64]);
866
- value.meta = bytes32(msg.data[abs + 64:abs + 96]);
867
- value.amount = uint(bytes32(msg.data[abs + 96:abs + 128]));
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);
868
1119
  }
869
1120
 
870
1121
  /// @notice Consume a TRANSACTION block and return all fields as a struct.
871
1122
  /// @param cur Cursor; advanced past the block.
872
1123
  /// @return value Decoded from, to, asset, meta, and amount.
873
1124
  function unpackTxValue(Cur memory cur) internal pure returns (Tx memory value) {
874
- uint abs = consume(cur, Keys.Transaction, 160, 160);
875
- value.from = bytes32(msg.data[abs:abs + 32]);
876
- value.to = bytes32(msg.data[abs + 32:abs + 64]);
877
- value.asset = bytes32(msg.data[abs + 64:abs + 96]);
878
- value.meta = bytes32(msg.data[abs + 96:abs + 128]);
879
- value.amount = uint(bytes32(msg.data[abs + 128:abs + 160]));
1125
+ (value.from, value.to, value.asset, value.meta, value.amount) = unpackTransaction(cur);
880
1126
  }
881
1127
 
882
- // -------------------------------------------------------------------------
883
- // expect* — validate at given position without advancing cursor
884
- // -------------------------------------------------------------------------
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
885
1153
 
886
1154
  /// @notice Validate an AUTH block at position `i` and extract deadline and proof.
887
1155
  /// Does not advance the cursor.
@@ -897,155 +1165,273 @@ library Cursors {
897
1165
  proof = msg.data[abs + 64:cur.offset + next];
898
1166
  }
899
1167
 
900
- /// @notice Validate an AMOUNT block at position `i` for a specific asset.
901
- /// Does not advance the cursor.
902
- /// @param cur Source cursor.
903
- /// @param i Byte offset of the AMOUNT block.
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.
904
1175
  /// @param asset Expected asset identifier.
905
- /// @param meta Expected metadata slot.
906
- /// @return amount Token amount from the block.
907
- function expectAmount(Cur memory cur, uint i, bytes32 asset, bytes32 meta) internal pure returns (uint amount) {
908
- (uint abs, ) = expect(cur, i, Keys.Amount, 96, 96);
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);
909
1184
  if (bytes32(msg.data[abs:abs + 32]) != asset) revert UnexpectedValue();
910
- if (bytes32(msg.data[abs + 32:abs + 64]) != meta) revert UnexpectedValue();
1185
+ meta = bytes32(msg.data[abs + 32:abs + 64]);
911
1186
  amount = uint(bytes32(msg.data[abs + 64:abs + 96]));
912
1187
  }
913
1188
 
914
- /// @notice Validate a BALANCE block at position `i` for a specific asset.
915
- /// Does not advance the cursor.
916
- /// @param cur Source cursor.
917
- /// @param i Byte offset of the BALANCE block.
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.
918
1192
  /// @param asset Expected asset identifier.
919
1193
  /// @param meta Expected metadata slot.
920
- /// @return amount Token amount from the block.
921
- function expectBalance(Cur memory cur, uint i, bytes32 asset, bytes32 meta) internal pure returns (uint amount) {
922
- (uint abs, ) = expect(cur, i, Keys.Balance, 96, 96);
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);
923
1202
  if (bytes32(msg.data[abs:abs + 32]) != asset) revert UnexpectedValue();
924
1203
  if (bytes32(msg.data[abs + 32:abs + 64]) != meta) revert UnexpectedValue();
925
1204
  amount = uint(bytes32(msg.data[abs + 64:abs + 96]));
926
1205
  }
927
1206
 
928
- /// @notice Validate a MINIMUM block at position `i` for a specific asset.
929
- /// Does not advance the cursor.
930
- /// @param cur Source cursor.
931
- /// @param i Byte offset of the MINIMUM block.
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.
932
1210
  /// @param asset Expected asset identifier.
933
- /// @param meta Expected metadata slot.
934
- /// @return amount Minimum amount from the block.
935
- function expectMinimum(Cur memory cur, uint i, bytes32 asset, bytes32 meta) internal pure returns (uint amount) {
936
- (uint abs, ) = expect(cur, i, Keys.Minimum, 96, 96);
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);
937
1214
  if (bytes32(msg.data[abs:abs + 32]) != asset) revert UnexpectedValue();
938
- if (bytes32(msg.data[abs + 32:abs + 64]) != meta) revert UnexpectedValue();
939
- amount = uint(bytes32(msg.data[abs + 64:abs + 96]));
1215
+ meta = bytes32(msg.data[abs + 32:abs + 64]);
1216
+ if (uint(bytes32(msg.data[abs + 64:abs + 96])) != 1) revert UnexpectedValue();
940
1217
  }
941
1218
 
942
- /// @notice Validate a MAXIMUM block at position `i` for a specific asset.
943
- /// Does not advance the cursor.
944
- /// @param cur Source cursor.
945
- /// @param i Byte offset of the MAXIMUM block.
1219
+ /// @notice Consume a MINIMUM block and assert it matches the expected asset and meta.
1220
+ /// @param cur Cursor; advanced past the block.
946
1221
  /// @param asset Expected asset identifier.
947
1222
  /// @param meta Expected metadata slot.
948
- /// @return amount Maximum amount from the block.
949
- function expectMaximum(Cur memory cur, uint i, bytes32 asset, bytes32 meta) internal pure returns (uint amount) {
950
- (uint abs, ) = expect(cur, i, Keys.Maximum, 96, 96);
951
- if (bytes32(msg.data[abs:abs + 32]) != asset) revert UnexpectedValue();
952
- if (bytes32(msg.data[abs + 32:abs + 64]) != meta) revert UnexpectedValue();
953
- amount = uint(bytes32(msg.data[abs + 64:abs + 96]));
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);
954
1226
  }
955
1227
 
956
- /// @notice Validate a CUSTODY block at position `i` for a specific host.
957
- /// Does not advance the cursor.
958
- /// @param cur Source cursor.
959
- /// @param i Byte offset of the CUSTODY block.
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.
960
1231
  /// @param host Expected host node ID.
961
- /// @return value Decoded asset, meta, and amount (host is not returned; it was validated).
962
- function expectCustody(Cur memory cur, uint i, uint host) internal pure returns (AssetAmount memory value) {
963
- (uint abs, ) = expect(cur, i, Keys.Custody, 128, 128);
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);
964
1241
  if (uint(bytes32(msg.data[abs:abs + 32])) != host) revert UnexpectedValue();
965
- value.asset = bytes32(msg.data[abs + 32:abs + 64]);
966
- value.meta = bytes32(msg.data[abs + 64:abs + 96]);
967
- value.amount = uint(bytes32(msg.data[abs + 96:abs + 128]));
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]));
968
1245
  }
969
1246
 
970
- // -------------------------------------------------------------------------
971
- // require* — validate + advance (like consume with content checks)
972
- // -------------------------------------------------------------------------
973
-
974
- /// @notice Consume an AUTH block at the current position and verify the command ID.
1247
+ /// @notice Consume a host amount block and assert it matches the expected host and asset.
975
1248
  /// @param cur Cursor; advanced past the block.
976
- /// @param cid Expected command ID.
977
- /// @return deadline Expiry timestamp.
978
- /// @return proof Raw proof bytes.
979
- function requireAuth(Cur memory cur, uint cid) internal pure returns (uint deadline, bytes calldata proof) {
980
- (deadline, proof) = expectAuth(cur, cur.i, cid);
981
- cur.i += 64 + proof.length;
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]));
982
1265
  }
983
1266
 
984
- /// @notice Consume an AMOUNT block and assert it matches the expected asset and meta.
1267
+ /// @notice Consume a host amount block, assert it matches the expected host and asset, and require the amount to be 1.
985
1268
  /// @param cur Cursor; advanced past the block.
1269
+ /// @param key Expected block type key.
1270
+ /// @param host Expected host node ID.
986
1271
  /// @param asset Expected asset identifier.
987
- /// @param meta Expected metadata slot.
988
- /// @return amount Token amount from the block.
989
- function requireAmount(Cur memory cur, bytes32 asset, bytes32 meta) internal pure returns (uint amount) {
990
- amount = expectAmount(cur, cur.i, asset, meta);
991
- cur.i += 104;
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();
992
1284
  }
993
1285
 
994
- /// @notice Consume a BALANCE block and assert it matches the expected asset and meta.
1286
+ /// @notice Consume a host account asset block and assert it matches the expected host and account.
995
1287
  /// @param cur Cursor; advanced past the block.
996
- /// @param asset Expected asset identifier.
997
- /// @param meta Expected metadata slot.
998
- /// @return amount Token amount from the block.
999
- function requireBalance(Cur memory cur, bytes32 asset, bytes32 meta) internal pure returns (uint amount) {
1000
- amount = expectBalance(cur, cur.i, asset, meta);
1001
- cur.i += 104;
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]);
1002
1304
  }
1003
1305
 
1004
- /// @notice Consume a MINIMUM block and assert it matches the expected asset and meta.
1306
+ /// @notice Consume a host account asset block, assert it targets the expected host, and return account, asset, and meta.
1005
1307
  /// @param cur Cursor; advanced past the block.
1006
- /// @param asset Expected asset identifier.
1007
- /// @param meta Expected metadata slot.
1008
- /// @return amount Minimum amount from the block.
1009
- function requireMinimum(Cur memory cur, bytes32 asset, bytes32 meta) internal pure returns (uint amount) {
1010
- amount = expectMinimum(cur, cur.i, asset, meta);
1011
- cur.i += 104;
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]);
1012
1323
  }
1013
1324
 
1014
- /// @notice Consume a MINIMUM block, assert it matches the expected asset and meta, and require `amount` to satisfy it.
1325
+ /// @notice Consume a HOST_ACCOUNT_ASSET form block, assert it targets the expected host, and return account, asset, and meta.
1015
1326
  /// @param cur Cursor; advanced past the block.
1016
- /// @param asset Expected asset identifier.
1017
- /// @param meta Expected metadata slot.
1018
- /// @param amount Actual amount that must be at least the minimum from the block.
1019
- function requireMinimum(Cur memory cur, bytes32 asset, bytes32 meta, uint amount) internal pure {
1020
- if (requireMinimum(cur, asset, meta) > amount) revert UnexpectedValue();
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);
1021
1336
  }
1022
1337
 
1023
- /// @notice Consume a MAXIMUM block and assert it matches the expected asset and meta.
1338
+ /// @notice Consume an AUTH block at the current position and verify the command ID.
1024
1339
  /// @param cur Cursor; advanced past the block.
1025
- /// @param asset Expected asset identifier.
1026
- /// @param meta Expected metadata slot.
1027
- /// @return amount Maximum amount from the block.
1028
- function requireMaximum(Cur memory cur, bytes32 asset, bytes32 meta) internal pure returns (uint amount) {
1029
- amount = expectMaximum(cur, cur.i, asset, meta);
1030
- cur.i += 104;
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;
1031
1346
  }
1032
1347
 
1033
- /// @notice Consume a MAXIMUM block, assert it matches the expected asset and meta, and require `amount` to satisfy it.
1034
- /// @param cur Cursor; advanced past the block.
1035
- /// @param asset Expected asset identifier.
1036
- /// @param meta Expected metadata slot.
1037
- /// @param amount Actual amount that must be at most the maximum from the block.
1038
- function requireMaximum(Cur memory cur, bytes32 asset, bytes32 meta, uint amount) internal pure {
1039
- if (requireMaximum(cur, asset, meta) < amount) revert UnexpectedValue();
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]));
1040
1364
  }
1041
1365
 
1042
- /// @notice Consume a CUSTODY block and assert it belongs to the expected host.
1043
- /// @param cur Cursor; advanced past the block.
1044
- /// @param host Expected host node ID.
1045
- /// @return value Decoded asset, meta, and amount.
1046
- function requireCustody(Cur memory cur, uint host) internal pure returns (AssetAmount memory value) {
1047
- value = expectCustody(cur, cur.i, host);
1048
- cur.i += 136;
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]);
1049
1414
  }
1050
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
+ }
1051
1437
  }