@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,15 +1,19 @@
1
1
  // SPDX-License-Identifier: GPL-3.0-only
2
2
  pragma solidity ^0.8.33;
3
3
 
4
- import { AssetAmount, HostAmount, Tx, Keys, Sizes } from "./Schema.sol";
4
+ import {AssetAmount, AccountAmount, HostAmount, Tx} from "../core/Types.sol";
5
+ import {Sizes} from "./Schema.sol";
6
+ import {Keys} from "./Keys.sol";
7
+ import {max32} from "../utils/Utils.sol";
5
8
 
6
9
  /// @notice Sequential block stream writer backed by a pre-allocated memory buffer.
7
10
  struct Writer {
8
11
  /// @dev Current write position: number of bytes written so far.
9
12
  uint i;
10
- /// @dev Allocated buffer capacity in bytes.
13
+ /// @dev Logical buffer capacity in bytes; writers should not advance past this limit.
11
14
  uint end;
12
- /// @dev Destination buffer; final length is set to `i` by `finish`.
15
+ /// @dev Destination buffer. Physical capacity may be padded up to a full 32-byte word;
16
+ /// final length is set to `i` by `finish`.
13
17
  bytes dst;
14
18
  }
15
19
 
@@ -21,8 +25,10 @@ uint constant ALLOC_SCALE = 10_000;
21
25
  /// @title Writers
22
26
  /// @notice Response block stream builder for the rootzero protocol.
23
27
  /// Allocates a fixed-size memory buffer up front and writes binary-encoded
24
- /// blocks into it sequentially. Call `finish` to trim the buffer to the
25
- /// number of bytes actually written and return the result.
28
+ /// blocks into it sequentially. Physical allocation is rounded up to whole
29
+ /// 32-byte words for scratch space, while `Writer.end` tracks the logical
30
+ /// requested capacity. Call `finish` to trim the buffer to the number of
31
+ /// bytes actually written and return the result.
26
32
  library Writers {
27
33
  /// @dev `append` or a `write*` function tried to write past the end of `dst`.
28
34
  error WriterOverflow();
@@ -30,55 +36,43 @@ library Writers {
30
36
  error IncompleteWriter();
31
37
  /// @dev An alloc function received a zero count, or `finish` found no bytes written.
32
38
  error EmptyRequest();
33
- /// @dev Block payload length exceeds `uint32` max; cannot be encoded in the 4-byte header field.
34
- error BlockLengthOverflow();
35
39
  /// @dev `scaledRatio * count` is not evenly divisible by `ALLOC_SCALE`.
36
40
  error BadWriterRatio();
41
+ /// @dev A fixed-width low-level writer received an invalid final-word keep length.
42
+ error InvalidKeep();
37
43
 
38
44
  // -------------------------------------------------------------------------
39
- // Header encoding
45
+ // Allocation helpers
40
46
  // -------------------------------------------------------------------------
41
47
 
42
- /// @notice Encode an 8-byte block header into a single uint for efficient `mstore`.
43
- /// The header occupies the most-significant 8 bytes; the payload starts at `offset + 8`.
44
- /// @param key Four-byte block type identifier.
45
- /// @param len Payload byte length.
46
- /// @return Packed header as a uint (key in bits [255:224], len in bits [223:192]).
47
- function toBlockHeader(bytes4 key, uint len) internal pure returns (uint) {
48
- if (len > type(uint32).max) revert BlockLengthOverflow();
49
- return (uint(uint32(key)) << 224) | (uint(uint32(len)) << 192);
50
- }
51
-
52
- // -------------------------------------------------------------------------
53
- // Allocation
54
- // -------------------------------------------------------------------------
55
-
56
- /// @notice Allocate a writer with an exact byte capacity.
57
- /// @param len Number of bytes to pre-allocate.
48
+ /// @notice Allocate a writer with a logical byte capacity.
49
+ /// The backing buffer is rounded up to whole 32-byte words, while
50
+ /// `writer.end` remains the exact logical byte capacity requested.
51
+ /// @param len Number of logical bytes to pre-allocate.
58
52
  /// @return writer Freshly allocated writer positioned at index 0.
59
53
  function alloc(uint len) internal pure returns (Writer memory writer) {
60
- writer = Writer({i: 0, end: len, dst: new bytes(len)});
61
- }
62
-
63
- /// @notice Allocate a writer sized for exactly `count` BALANCE blocks (1:1 ratio).
64
- /// @param count Number of balance blocks to allocate space for.
65
- /// @return writer Allocated writer.
66
- function allocBalances(uint count) internal pure returns (Writer memory writer) {
67
- return alloc96s(count);
54
+ // Extra 32 bytes ensure mstore in write/append32 never reaches past allocated memory,
55
+ // even when a sub-word packed write starts within the last 31 bytes of the logical region.
56
+ uint padded = ((len + 31) & ~uint(31)) + 32;
57
+ writer = Writer({i: 0, end: len, dst: new bytes(padded)});
68
58
  }
69
59
 
70
- /// @notice Allocate a writer sized for exactly `count` AMOUNT blocks (1:1 ratio).
71
- /// @param count Number of amount blocks to allocate space for.
72
- /// @return writer Allocated writer.
73
- function allocAmounts(uint count) internal pure returns (Writer memory writer) {
74
- return alloc96s(count);
75
- }
76
-
77
- /// @notice Allocate a writer sized for exactly `count` ASSET blocks (1:1 ratio).
78
- /// @param count Number of asset blocks to allocate space for.
60
+ /// @notice Core allocation routine used by all counted `alloc*` helpers.
61
+ /// Computes `(count * scaledRatio / ALLOC_SCALE) * blockLen` and allocates that many bytes.
62
+ /// @param count Number of input blocks.
63
+ /// @param scaledRatio Output-to-input ratio in `ALLOC_SCALE` units.
64
+ /// @param blockLen Logical byte size of each output block (including 8-byte header).
79
65
  /// @return writer Allocated writer.
80
- function allocAssets(uint count) internal pure returns (Writer memory writer) {
81
- return alloc64s(count);
66
+ function allocFromScaledCount(
67
+ uint count,
68
+ uint scaledRatio,
69
+ uint blockLen
70
+ ) internal pure returns (Writer memory writer) {
71
+ if (count == 0) revert EmptyRequest();
72
+ uint scaledCount = count * scaledRatio;
73
+ if (scaledCount % ALLOC_SCALE != 0) revert BadWriterRatio();
74
+ uint len = (scaledCount / ALLOC_SCALE) * blockLen;
75
+ writer = alloc(len);
82
76
  }
83
77
 
84
78
  /// @notice Allocate a writer sized for exactly `count` dynamic blocks with a shared payload length.
@@ -125,40 +119,17 @@ library Writers {
125
119
  return allocFromScaledCount(count, ALLOC_SCALE, Sizes.B160);
126
120
  }
127
121
 
128
- /// @notice Allocate a writer for BALANCE blocks with a custom output-to-input ratio.
129
- /// @param count Number of input blocks.
130
- /// @param scaledRatio Output count multiplier expressed in `ALLOC_SCALE` units
131
- /// (e.g. `ALLOC_SCALE` = 1:1, `2 * ALLOC_SCALE` = 2:1).
132
- /// @return writer Allocated writer.
133
- function allocScaledBalances(uint count, uint scaledRatio) internal pure returns (Writer memory writer) {
134
- return allocScaled96s(count, scaledRatio);
135
- }
136
-
137
- /// @notice Allocate a writer for AMOUNT blocks with a custom output-to-input ratio.
138
- /// @param count Number of input blocks.
139
- /// @param scaledRatio Output count multiplier expressed in `ALLOC_SCALE` units
140
- /// (e.g. `ALLOC_SCALE` = 1:1, `2 * ALLOC_SCALE` = 2:1).
141
- /// @return writer Allocated writer.
142
- function allocScaledAmounts(uint count, uint scaledRatio) internal pure returns (Writer memory writer) {
143
- return allocScaled96s(count, scaledRatio);
144
- }
145
-
146
- /// @notice Allocate a writer for ASSET blocks with a custom output-to-input ratio.
147
- /// @param count Number of input blocks.
148
- /// @param scaledRatio Output count multiplier expressed in `ALLOC_SCALE` units
149
- /// (e.g. `ALLOC_SCALE` = 1:1, `2 * ALLOC_SCALE` = 2:1).
150
- /// @return writer Allocated writer.
151
- function allocScaledAssets(uint count, uint scaledRatio) internal pure returns (Writer memory writer) {
152
- return allocScaled64s(count, scaledRatio);
153
- }
154
-
155
122
  /// @notice Allocate a writer for dynamic blocks with a shared payload length and custom output ratio.
156
123
  /// Each block reserves `Sizes.Header + payloadLen` bytes.
157
124
  /// @param count Number of input blocks.
158
125
  /// @param scaledRatio Output count multiplier expressed in `ALLOC_SCALE` units.
159
126
  /// @param payloadLen Payload byte length for each block.
160
127
  /// @return writer Allocated writer.
161
- function allocScaledBytes(uint count, uint scaledRatio, uint payloadLen) internal pure returns (Writer memory writer) {
128
+ function allocScaledBytes(
129
+ uint count,
130
+ uint scaledRatio,
131
+ uint payloadLen
132
+ ) internal pure returns (Writer memory writer) {
162
133
  return allocFromScaledCount(count, scaledRatio, Sizes.Header + payloadLen);
163
134
  }
164
135
 
@@ -202,11 +173,39 @@ library Writers {
202
173
  return allocFromScaledCount(count, scaledRatio, Sizes.B160);
203
174
  }
204
175
 
205
- /// @notice Allocate a writer sized for exactly `count` TRANSACTION blocks (1:1 ratio).
206
- /// @param count Number of transaction blocks to allocate space for.
176
+ /// @notice Allocate a writer sized for exactly `count` STATUS form blocks.
177
+ /// @param count Number of blocks to allocate space for.
207
178
  /// @return writer Allocated writer.
208
- function allocTxs(uint count) internal pure returns (Writer memory writer) {
209
- return alloc160s(count);
179
+ function allocStatuses(uint count) internal pure returns (Writer memory writer) {
180
+ return alloc32s(count);
181
+ }
182
+
183
+ /// @notice Allocate a writer sized for exactly `count` ASSET blocks (1:1 ratio).
184
+ /// @param count Number of asset blocks to allocate space for.
185
+ /// @return writer Allocated writer.
186
+ function allocAssets(uint count) internal pure returns (Writer memory writer) {
187
+ return alloc64s(count);
188
+ }
189
+
190
+ /// @notice Allocate a writer sized for exactly `count` AMOUNT blocks (1:1 ratio).
191
+ /// @param count Number of amount blocks to allocate space for.
192
+ /// @return writer Allocated writer.
193
+ function allocAmounts(uint count) internal pure returns (Writer memory writer) {
194
+ return alloc96s(count);
195
+ }
196
+
197
+ /// @notice Allocate a writer sized for exactly `count` BALANCE blocks (1:1 ratio).
198
+ /// @param count Number of balance blocks to allocate space for.
199
+ /// @return writer Allocated writer.
200
+ function allocBalances(uint count) internal pure returns (Writer memory writer) {
201
+ return alloc96s(count);
202
+ }
203
+
204
+ /// @notice Allocate a writer sized for exactly `count` ACCOUNT_AMOUNT form blocks (1:1 ratio).
205
+ /// @param count Number of account amount blocks to allocate space for.
206
+ /// @return writer Allocated writer.
207
+ function allocAccountAmounts(uint count) internal pure returns (Writer memory writer) {
208
+ return alloc128s(count);
210
209
  }
211
210
 
212
211
  /// @notice Allocate a writer sized for exactly `count` CUSTODY blocks (1:1 ratio).
@@ -216,105 +215,247 @@ library Writers {
216
215
  return alloc128s(count);
217
216
  }
218
217
 
219
- /// @notice Allocate a writer for CUSTODY blocks with a custom output-to-input ratio.
218
+ /// @notice Allocate a writer sized for exactly `count` TRANSACTION blocks (1:1 ratio).
219
+ /// @param count Number of transaction blocks to allocate space for.
220
+ /// @return writer Allocated writer.
221
+ function allocTransactions(uint count) internal pure returns (Writer memory writer) {
222
+ return alloc160s(count);
223
+ }
224
+
225
+ /// @notice Allocate a writer for ASSET blocks with a custom output-to-input ratio.
220
226
  /// @param count Number of input blocks.
221
- /// @param scaledRatio Output count multiplier in `ALLOC_SCALE` units.
227
+ /// @param scaledRatio Output count multiplier expressed in `ALLOC_SCALE` units
228
+ /// (e.g. `ALLOC_SCALE` = 1:1, `2 * ALLOC_SCALE` = 2:1).
222
229
  /// @return writer Allocated writer.
223
- function allocScaledCustodies(uint count, uint scaledRatio) internal pure returns (Writer memory writer) {
224
- return allocScaled128s(count, scaledRatio);
230
+ function allocScaledAssets(uint count, uint scaledRatio) internal pure returns (Writer memory writer) {
231
+ return allocScaled64s(count, scaledRatio);
225
232
  }
226
233
 
227
- /// @notice Core allocation routine used by all typed `alloc*` helpers.
228
- /// Computes `(count * scaledRatio / ALLOC_SCALE) * blockLen` and allocates that many bytes.
234
+ /// @notice Allocate a writer for AMOUNT blocks with a custom output-to-input ratio.
229
235
  /// @param count Number of input blocks.
230
- /// @param scaledRatio Output-to-input ratio in `ALLOC_SCALE` units.
231
- /// @param blockLen Byte size of each output block (including 8-byte header).
236
+ /// @param scaledRatio Output count multiplier expressed in `ALLOC_SCALE` units
237
+ /// (e.g. `ALLOC_SCALE` = 1:1, `2 * ALLOC_SCALE` = 2:1).
232
238
  /// @return writer Allocated writer.
233
- function allocFromScaledCount(
234
- uint count,
235
- uint scaledRatio,
236
- uint blockLen
237
- ) internal pure returns (Writer memory writer) {
238
- if (count == 0) revert EmptyRequest();
239
- uint scaledCount = count * scaledRatio;
240
- if (scaledCount % ALLOC_SCALE != 0) revert BadWriterRatio();
241
- uint len = (scaledCount / ALLOC_SCALE) * blockLen;
242
- writer = Writer({i: 0, end: len, dst: new bytes(len)});
239
+ function allocScaledAmounts(uint count, uint scaledRatio) internal pure returns (Writer memory writer) {
240
+ return allocScaled96s(count, scaledRatio);
241
+ }
242
+
243
+ /// @notice Allocate a writer for BALANCE blocks with a custom output-to-input ratio.
244
+ /// @param count Number of input blocks.
245
+ /// @param scaledRatio Output count multiplier expressed in `ALLOC_SCALE` units
246
+ /// (e.g. `ALLOC_SCALE` = 1:1, `2 * ALLOC_SCALE` = 2:1).
247
+ /// @return writer Allocated writer.
248
+ function allocScaledBalances(uint count, uint scaledRatio) internal pure returns (Writer memory writer) {
249
+ return allocScaled96s(count, scaledRatio);
250
+ }
251
+
252
+ /// @notice Allocate a writer for CUSTODY blocks with a custom output-to-input ratio.
253
+ /// @param count Number of input blocks.
254
+ /// @param scaledRatio Output count multiplier in `ALLOC_SCALE` units.
255
+ /// @return writer Allocated writer.
256
+ function allocScaledCustodies(uint count, uint scaledRatio) internal pure returns (Writer memory writer) {
257
+ return allocScaled128s(count, scaledRatio);
243
258
  }
244
259
 
245
260
  // -------------------------------------------------------------------------
246
261
  // Fixed-width write helpers
247
262
  // -------------------------------------------------------------------------
248
263
 
249
- /// @notice Write a fixed-width 32-byte-payload block directly into `dst` at byte offset `i`.
250
- /// @param dst Destination buffer; must have at least `i + Sizes.B32` bytes.
264
+ /// @notice Write a low-level block header and return its memory pointer.
265
+ /// The header occupies the most-significant 8 bytes of the first stored word.
266
+ /// @param dst Destination buffer.
267
+ /// @param i Write offset within `dst`.
268
+ /// @param key Four-byte block type identifier.
269
+ /// @param len Payload byte length.
270
+ /// @return p Memory pointer to the start of the block header within `dst`.
271
+ function writeHeader(bytes memory dst, uint i, bytes4 key, uint32 len) private pure returns (uint p) {
272
+ uint header = (uint(uint32(key)) << 224) | (uint(len) << 192);
273
+ assembly ("memory-safe") {
274
+ p := add(add(dst, 0x20), i)
275
+ mstore(p, header)
276
+ }
277
+ }
278
+
279
+ /// @notice Commit a logical writer advance after a low-level write.
280
+ /// @dev Low-level write helpers validate the padded backing buffer. This
281
+ /// enforces the caller-requested logical capacity recorded in `end`.
282
+ function commit(Writer memory writer, uint next) private pure {
283
+ if (next > writer.end) revert WriterOverflow();
284
+ writer.i = next;
285
+ }
286
+
287
+ /// @notice Write raw bytes directly into `dst` at byte offset `i` without a block header.
288
+ /// @param dst Destination buffer.
289
+ /// @param i Write offset within `dst`.
290
+ /// @param data Bytes to copy.
291
+ /// @return next Byte offset immediately after the copied bytes.
292
+ function write(bytes memory dst, uint i, bytes memory data) internal pure returns (uint next) {
293
+ next = i + data.length;
294
+ if (next > dst.length) revert WriterOverflow();
295
+ assembly ("memory-safe") {
296
+ mcopy(add(add(dst, 0x20), i), add(data, 0x20), mload(data))
297
+ }
298
+ }
299
+
300
+ /// @notice Write a raw 32-byte word directly into `dst` at byte offset `i` without a block header.
301
+ /// `keep` controls how many leading bytes of the word are included in the logical write.
302
+ /// @param dst Destination buffer; must have at least `i + 32` bytes.
303
+ /// @param i Write offset within `dst`.
304
+ /// @param value Word to write.
305
+ /// @param keep Number of bytes to keep from the word (1..32).
306
+ /// @return next Byte offset immediately after the written bytes.
307
+ function write32(bytes memory dst, uint i, bytes32 value, uint keep) internal pure returns (uint next) {
308
+ if (keep == 0 || keep > 32) revert InvalidKeep();
309
+ if (i + 32 > dst.length) revert WriterOverflow();
310
+ next = i + keep;
311
+ assembly ("memory-safe") {
312
+ mstore(add(add(dst, 0x20), i), value)
313
+ }
314
+ }
315
+
316
+ /// @notice Write two raw 32-byte words directly into `dst` at byte offset `i` without a block header.
317
+ /// `keep` controls how many leading bytes of the final word are included in the logical write.
318
+ /// @param dst Destination buffer; must have at least `i + 64` bytes.
319
+ /// @param i Write offset within `dst`.
320
+ /// @param a First word to write.
321
+ /// @param b Second word to write.
322
+ /// @param keep Number of bytes to keep from the final word (1..32).
323
+ /// @return next Byte offset immediately after the written bytes.
324
+ function write64(bytes memory dst, uint i, bytes32 a, bytes32 b, uint keep) internal pure returns (uint next) {
325
+ if (keep == 0 || keep > 32) revert InvalidKeep();
326
+ if (i + 64 > dst.length) revert WriterOverflow();
327
+ next = i + 32 + keep;
328
+ assembly ("memory-safe") {
329
+ let p := add(add(dst, 0x20), i)
330
+ mstore(p, a)
331
+ mstore(add(p, 0x20), b)
332
+ }
333
+ }
334
+
335
+ /// @notice Write three raw 32-byte words directly into `dst` at byte offset `i` without a block header.
336
+ /// `keep` controls how many leading bytes of the final word are included in the logical write.
337
+ /// @param dst Destination buffer; must have at least `i + 96` bytes.
338
+ /// @param i Write offset within `dst`.
339
+ /// @param a First word to write.
340
+ /// @param b Second word to write.
341
+ /// @param c Third word to write.
342
+ /// @param keep Number of bytes to keep from the final word (1..32).
343
+ /// @return next Byte offset immediately after the written bytes.
344
+ function write96(
345
+ bytes memory dst,
346
+ uint i,
347
+ bytes32 a,
348
+ bytes32 b,
349
+ bytes32 c,
350
+ uint keep
351
+ ) internal pure returns (uint next) {
352
+ if (keep == 0 || keep > 32) revert InvalidKeep();
353
+ if (i + 96 > dst.length) revert WriterOverflow();
354
+ next = i + 64 + keep;
355
+ assembly ("memory-safe") {
356
+ let p := add(add(dst, 0x20), i)
357
+ mstore(p, a)
358
+ mstore(add(p, 0x20), b)
359
+ mstore(add(p, 0x40), c)
360
+ }
361
+ }
362
+
363
+ /// @notice Write a dynamic block directly into `dst` at byte offset `i`.
364
+ /// @param dst Destination buffer; must have at least `i + Sizes.Header + data.length` bytes.
251
365
  /// @param i Write offset within `dst`.
252
366
  /// @param key Block type key.
253
- /// @param a First payload word.
367
+ /// @param data Dynamic payload bytes.
254
368
  /// @return next Byte offset immediately after the written block.
255
- function write32(bytes memory dst, uint i, bytes4 key, bytes32 a) internal pure returns (uint next) {
256
- next = i + Sizes.B32;
369
+ function writeBlock(bytes memory dst, uint i, bytes4 key, bytes memory data) internal pure returns (uint next) {
370
+ next = i + Sizes.Header + data.length;
257
371
  if (next > dst.length) revert WriterOverflow();
258
- uint header = toBlockHeader(key, 32);
372
+ uint p = writeHeader(dst, i, key, uint32(max32(data.length)));
259
373
  assembly ("memory-safe") {
260
- let p := add(add(dst, 0x20), i)
261
- mstore(p, header)
262
- mstore(add(p, 0x08), a)
374
+ mcopy(add(p, 0x08), add(data, 0x20), mload(data))
263
375
  }
264
376
  }
265
377
 
266
- /// @notice Write an ABI-style boolean as a 32-byte scalar payload block.
267
- /// Encodes `false` as `bytes32(0)` and `true` as `bytes32(uint(1))`.
378
+ /// @notice Write a fixed-width 32-byte-payload block directly into `dst` at byte offset `i`.
379
+ /// `keep` controls how many leading bytes of the final payload word are included.
380
+ /// Writes still use full-word stores, so `dst` must have capacity for the untrimmed block.
268
381
  /// @param dst Destination buffer; must have at least `i + Sizes.B32` bytes.
269
382
  /// @param i Write offset within `dst`.
270
383
  /// @param key Block type key.
271
- /// @param value Boolean value to encode.
384
+ /// @param a First payload word.
385
+ /// @param keep Number of bytes to keep from the final payload word (1..32).
272
386
  /// @return next Byte offset immediately after the written block.
273
- function writeBool(bytes memory dst, uint i, bytes4 key, bool value) internal pure returns (uint next) {
274
- return write32(dst, i, key, value ? bytes32(uint(1)) : bytes32(0));
387
+ function writeBlock32(
388
+ bytes memory dst,
389
+ uint i,
390
+ bytes4 key,
391
+ bytes32 a,
392
+ uint keep
393
+ ) internal pure returns (uint next) {
394
+ if (keep == 0 || keep > 32) revert InvalidKeep();
395
+ if (i + Sizes.B32 > dst.length) revert WriterOverflow();
396
+ uint len = keep;
397
+ next = i + Sizes.Header + len;
398
+ uint p = writeHeader(dst, i, key, uint32(len));
399
+ assembly ("memory-safe") {
400
+ mstore(add(p, 0x08), a)
401
+ }
275
402
  }
276
403
 
277
404
  /// @notice Write a fixed-width 64-byte-payload block directly into `dst` at byte offset `i`.
405
+ /// `keep` controls how many leading bytes of the final payload word are included.
406
+ /// Writes still use full-word stores, so `dst` must have capacity for the untrimmed block.
278
407
  /// @param dst Destination buffer; must have at least `i + Sizes.B64` bytes.
279
408
  /// @param i Write offset within `dst`.
280
409
  /// @param key Block type key.
281
410
  /// @param a First payload word.
282
411
  /// @param b Second payload word.
412
+ /// @param keep Number of bytes to keep from the final payload word (1..32).
283
413
  /// @return next Byte offset immediately after the written block.
284
- function write64(bytes memory dst, uint i, bytes4 key, bytes32 a, bytes32 b) internal pure returns (uint next) {
285
- next = i + Sizes.B64;
286
- if (next > dst.length) revert WriterOverflow();
287
- uint header = toBlockHeader(key, 64);
414
+ function writeBlock64(
415
+ bytes memory dst,
416
+ uint i,
417
+ bytes4 key,
418
+ bytes32 a,
419
+ bytes32 b,
420
+ uint keep
421
+ ) internal pure returns (uint next) {
422
+ if (keep == 0 || keep > 32) revert InvalidKeep();
423
+ if (i + Sizes.B64 > dst.length) revert WriterOverflow();
424
+ uint len = 32 + keep;
425
+ next = i + Sizes.Header + len;
426
+ uint p = writeHeader(dst, i, key, uint32(len));
288
427
  assembly ("memory-safe") {
289
- let p := add(add(dst, 0x20), i)
290
- mstore(p, header)
291
428
  mstore(add(p, 0x08), a)
292
429
  mstore(add(p, 0x28), b)
293
430
  }
294
431
  }
295
432
 
296
433
  /// @notice Write a fixed-width 96-byte-payload block directly into `dst` at byte offset `i`.
434
+ /// `keep` controls how many leading bytes of the final payload word are included.
435
+ /// Writes still use full-word stores, so `dst` must have capacity for the untrimmed block.
297
436
  /// @param dst Destination buffer; must have at least `i + Sizes.B96` bytes.
298
437
  /// @param i Write offset within `dst`.
299
438
  /// @param key Block type key.
300
439
  /// @param a First payload word.
301
440
  /// @param b Second payload word.
302
441
  /// @param c Third payload word.
442
+ /// @param keep Number of bytes to keep from the final payload word (1..32).
303
443
  /// @return next Byte offset immediately after the written block.
304
- function write96(
444
+ function writeBlock96(
305
445
  bytes memory dst,
306
446
  uint i,
307
447
  bytes4 key,
308
448
  bytes32 a,
309
449
  bytes32 b,
310
- bytes32 c
450
+ bytes32 c,
451
+ uint keep
311
452
  ) internal pure returns (uint next) {
312
- next = i + Sizes.B96;
313
- if (next > dst.length) revert WriterOverflow();
314
- uint header = toBlockHeader(key, 96);
453
+ if (keep == 0 || keep > 32) revert InvalidKeep();
454
+ if (i + Sizes.B96 > dst.length) revert WriterOverflow();
455
+ uint len = 64 + keep;
456
+ next = i + Sizes.Header + len;
457
+ uint p = writeHeader(dst, i, key, uint32(len));
315
458
  assembly ("memory-safe") {
316
- let p := add(add(dst, 0x20), i)
317
- mstore(p, header)
318
459
  mstore(add(p, 0x08), a)
319
460
  mstore(add(p, 0x28), b)
320
461
  mstore(add(p, 0x48), c)
@@ -322,6 +463,8 @@ library Writers {
322
463
  }
323
464
 
324
465
  /// @notice Write a fixed-width 128-byte-payload block directly into `dst` at byte offset `i`.
466
+ /// `keep` controls how many leading bytes of the final payload word are included.
467
+ /// Writes still use full-word stores, so `dst` must have capacity for the untrimmed block.
325
468
  /// @param dst Destination buffer; must have at least `i + Sizes.B128` bytes.
326
469
  /// @param i Write offset within `dst`.
327
470
  /// @param key Block type key.
@@ -329,22 +472,24 @@ library Writers {
329
472
  /// @param b Second payload word.
330
473
  /// @param c Third payload word.
331
474
  /// @param d Fourth payload word.
475
+ /// @param keep Number of bytes to keep from the final payload word (1..32).
332
476
  /// @return next Byte offset immediately after the written block.
333
- function write128(
477
+ function writeBlock128(
334
478
  bytes memory dst,
335
479
  uint i,
336
480
  bytes4 key,
337
481
  bytes32 a,
338
482
  bytes32 b,
339
483
  bytes32 c,
340
- bytes32 d
484
+ bytes32 d,
485
+ uint keep
341
486
  ) internal pure returns (uint next) {
342
- next = i + Sizes.B128;
343
- if (next > dst.length) revert WriterOverflow();
344
- uint header = toBlockHeader(key, 128);
487
+ if (keep == 0 || keep > 32) revert InvalidKeep();
488
+ if (i + Sizes.B128 > dst.length) revert WriterOverflow();
489
+ uint len = 96 + keep;
490
+ next = i + Sizes.Header + len;
491
+ uint p = writeHeader(dst, i, key, uint32(len));
345
492
  assembly ("memory-safe") {
346
- let p := add(add(dst, 0x20), i)
347
- mstore(p, header)
348
493
  mstore(add(p, 0x08), a)
349
494
  mstore(add(p, 0x28), b)
350
495
  mstore(add(p, 0x48), c)
@@ -353,6 +498,8 @@ library Writers {
353
498
  }
354
499
 
355
500
  /// @notice Write a fixed-width 160-byte-payload block directly into `dst` at byte offset `i`.
501
+ /// `keep` controls how many leading bytes of the final payload word are included.
502
+ /// Writes still use full-word stores, so `dst` must have capacity for the untrimmed block.
356
503
  /// @param dst Destination buffer; must have at least `i + Sizes.B160` bytes.
357
504
  /// @param i Write offset within `dst`.
358
505
  /// @param key Block type key.
@@ -361,8 +508,9 @@ library Writers {
361
508
  /// @param c Third payload word.
362
509
  /// @param d Fourth payload word.
363
510
  /// @param e Fifth payload word.
511
+ /// @param keep Number of bytes to keep from the final payload word (1..32).
364
512
  /// @return next Byte offset immediately after the written block.
365
- function write160(
513
+ function writeBlock160(
366
514
  bytes memory dst,
367
515
  uint i,
368
516
  bytes4 key,
@@ -370,14 +518,15 @@ library Writers {
370
518
  bytes32 b,
371
519
  bytes32 c,
372
520
  bytes32 d,
373
- bytes32 e
521
+ bytes32 e,
522
+ uint keep
374
523
  ) internal pure returns (uint next) {
375
- next = i + Sizes.B160;
376
- if (next > dst.length) revert WriterOverflow();
377
- uint header = toBlockHeader(key, 160);
524
+ if (keep == 0 || keep > 32) revert InvalidKeep();
525
+ if (i + Sizes.B160 > dst.length) revert WriterOverflow();
526
+ uint len = 128 + keep;
527
+ next = i + Sizes.Header + len;
528
+ uint p = writeHeader(dst, i, key, uint32(len));
378
529
  assembly ("memory-safe") {
379
- let p := add(add(dst, 0x20), i)
380
- mstore(p, header)
381
530
  mstore(add(p, 0x08), a)
382
531
  mstore(add(p, 0x28), b)
383
532
  mstore(add(p, 0x48), c)
@@ -393,19 +542,18 @@ library Writers {
393
542
  /// @param a Fixed head word.
394
543
  /// @param tail Dynamic payload bytes appended after the head.
395
544
  /// @return next Byte offset immediately after the written block.
396
- function writeHead32(
545
+ function writeBlockHead32(
397
546
  bytes memory dst,
398
547
  uint i,
399
548
  bytes4 key,
400
549
  bytes32 a,
401
550
  bytes memory tail
402
551
  ) internal pure returns (uint next) {
403
- next = i + Sizes.B32 + tail.length;
404
- if (next > dst.length) revert WriterOverflow();
405
- uint header = toBlockHeader(key, 32 + tail.length);
552
+ uint len = 32 + tail.length;
553
+ next = i + Sizes.Header + len;
554
+ if (i + Sizes.B32 + tail.length > dst.length) revert WriterOverflow();
555
+ uint p = writeHeader(dst, i, key, uint32(max32(len)));
406
556
  assembly ("memory-safe") {
407
- let p := add(add(dst, 0x20), i)
408
- mstore(p, header)
409
557
  mstore(add(p, 0x08), a)
410
558
  mcopy(add(p, 0x28), add(tail, 0x20), mload(tail))
411
559
  }
@@ -419,7 +567,7 @@ library Writers {
419
567
  /// @param b Second fixed head word.
420
568
  /// @param tail Dynamic payload bytes appended after the head.
421
569
  /// @return next Byte offset immediately after the written block.
422
- function writeHead64(
570
+ function writeBlockHead64(
423
571
  bytes memory dst,
424
572
  uint i,
425
573
  bytes4 key,
@@ -427,187 +575,133 @@ library Writers {
427
575
  bytes32 b,
428
576
  bytes memory tail
429
577
  ) internal pure returns (uint next) {
430
- next = i + Sizes.B64 + tail.length;
431
- if (next > dst.length) revert WriterOverflow();
432
- uint header = toBlockHeader(key, 64 + tail.length);
578
+ uint len = 64 + tail.length;
579
+ next = i + Sizes.Header + len;
580
+ if (i + Sizes.B64 + tail.length > dst.length) revert WriterOverflow();
581
+ uint p = writeHeader(dst, i, key, uint32(max32(len)));
433
582
  assembly ("memory-safe") {
434
- let p := add(add(dst, 0x20), i)
435
- mstore(p, header)
436
583
  mstore(add(p, 0x08), a)
437
584
  mstore(add(p, 0x28), b)
438
585
  mcopy(add(p, 0x48), add(tail, 0x20), mload(tail))
439
586
  }
440
587
  }
441
588
 
442
- /// @notice Write a dynamic block directly into `dst` at byte offset `i`.
443
- /// @param dst Destination buffer; must have at least `i + Sizes.Header + data.length` bytes.
444
- /// @param i Write offset within `dst`.
445
- /// @param key Block type key.
446
- /// @param data Dynamic payload bytes.
447
- /// @return next Byte offset immediately after the written block.
448
- function write(bytes memory dst, uint i, bytes4 key, bytes memory data) internal pure returns (uint next) {
449
- next = i + Sizes.Header + data.length;
450
- if (next > dst.length) revert WriterOverflow();
451
- uint header = toBlockHeader(key, data.length);
452
- assembly ("memory-safe") {
453
- let p := add(add(dst, 0x20), i)
454
- mstore(p, header)
455
- mcopy(add(p, 0x08), add(data, 0x20), mload(data))
456
- }
457
- }
458
-
459
589
  // -------------------------------------------------------------------------
460
- // Typed write helpers
590
+ // Append helpers
461
591
  // -------------------------------------------------------------------------
462
592
 
463
- /// @notice Write a BALANCE block directly into `dst` at byte offset `i`.
464
- /// @param dst Destination buffer; must have at least `i + Sizes.Amount` bytes.
465
- /// @param i Write offset within `dst`.
466
- /// @param value Balance fields to encode.
467
- /// @return next Byte offset immediately after the written block.
468
- function writeBalanceBlock(bytes memory dst, uint i, AssetAmount memory value) internal pure returns (uint next) {
469
- return write96(dst, i, Keys.Balance, value.asset, value.meta, bytes32(value.amount));
470
- }
471
-
472
- /// @notice Write an AMOUNT block directly into `dst` at byte offset `i`.
473
- /// @param dst Destination buffer; must have at least `i + Sizes.Balance` bytes.
474
- /// @param i Write offset within `dst`.
475
- /// @param value Amount fields to encode.
476
- /// @return next Byte offset immediately after the written block.
477
- function writeAmountBlock(bytes memory dst, uint i, AssetAmount memory value) internal pure returns (uint next) {
478
- return write96(dst, i, Keys.Amount, value.asset, value.meta, bytes32(value.amount));
479
- }
480
-
481
- /// @notice Write an ASSET block directly into `dst` at byte offset `i`.
482
- /// @param dst Destination buffer; must have at least `i + Sizes.B64` bytes.
483
- /// @param i Write offset within `dst`.
484
- /// @param asset Asset identifier.
485
- /// @param meta Asset metadata slot.
486
- /// @return next Byte offset immediately after the written block.
487
- function writeAssetBlock(bytes memory dst, uint i, bytes32 asset, bytes32 meta) internal pure returns (uint next) {
488
- return write64(dst, i, Keys.Asset, asset, meta);
593
+ /// @notice Append arbitrary bytes to the writer.
594
+ /// @param writer Destination writer; `i` is advanced by `data.length`.
595
+ /// @param data Bytes to append.
596
+ function append(Writer memory writer, bytes memory data) internal pure {
597
+ commit(writer, write(writer.dst, writer.i, data));
489
598
  }
490
599
 
491
- /// @notice Write a BOUNTY block directly into `dst` at byte offset `i`.
492
- /// @param dst Destination buffer; must have at least `i + Sizes.Bounty` bytes.
493
- /// @param i Write offset within `dst`.
494
- /// @param amount Relayer reward amount.
495
- /// @param relayer Relayer account identifier.
496
- /// @return next Byte offset immediately after the written block.
497
- function writeBountyBlock(bytes memory dst, uint i, uint amount, bytes32 relayer) internal pure returns (uint next) {
498
- return write64(dst, i, Keys.Bounty, bytes32(amount), relayer);
600
+ /// @notice Append a raw 32-byte word without a block header.
601
+ /// @param writer Destination writer; `i` is advanced by `keep`.
602
+ /// @param value Word to append.
603
+ /// @param keep Number of bytes to keep from the word (1..32).
604
+ function append32(Writer memory writer, bytes32 value, uint keep) internal pure {
605
+ commit(writer, write32(writer.dst, writer.i, value, keep));
499
606
  }
500
607
 
501
- /// @notice Write a CUSTODY block directly into `dst` at byte offset `i`.
502
- /// @param dst Destination buffer; must have at least `i + Sizes.Custody` bytes.
503
- /// @param i Write offset within `dst`.
504
- /// @param value Custody fields to encode.
505
- /// @return next Byte offset immediately after the written block.
506
- function writeCustodyBlock(bytes memory dst, uint i, HostAmount memory value) internal pure returns (uint next) {
507
- return write128(dst, i, Keys.Custody, bytes32(value.host), value.asset, value.meta, bytes32(value.amount));
608
+ /// @notice Append two raw 32-byte words without a block header.
609
+ /// @param writer Destination writer; `i` is advanced by `32 + keep`.
610
+ /// @param a First word to append.
611
+ /// @param b Second word to append.
612
+ /// @param keep Number of bytes to keep from the final word (1..32).
613
+ function append64(Writer memory writer, bytes32 a, bytes32 b, uint keep) internal pure {
614
+ commit(writer, write64(writer.dst, writer.i, a, b, keep));
508
615
  }
509
616
 
510
- /// @notice Write a TRANSACTION block directly into `dst` at byte offset `i`.
511
- /// @param dst Destination buffer; must have at least `i + Sizes.Transaction` bytes.
512
- /// @param i Write offset within `dst`.
513
- /// @param value Transfer record fields to encode.
514
- /// @return next Byte offset immediately after the written block.
515
- function writeTxBlock(bytes memory dst, uint i, Tx memory value) internal pure returns (uint next) {
516
- return write160(
517
- dst,
518
- i,
519
- Keys.Transaction,
520
- bytes32(value.from),
521
- bytes32(value.to),
522
- value.asset,
523
- value.meta,
524
- bytes32(value.amount)
525
- );
617
+ /// @notice Append three raw 32-byte words without a block header.
618
+ /// @param writer Destination writer; `i` is advanced by `64 + keep`.
619
+ /// @param a First word to append.
620
+ /// @param b Second word to append.
621
+ /// @param c Third word to append.
622
+ /// @param keep Number of bytes to keep from the final word (1..32).
623
+ function append96(Writer memory writer, bytes32 a, bytes32 b, bytes32 c, uint keep) internal pure {
624
+ commit(writer, write96(writer.dst, writer.i, a, b, c, keep));
526
625
  }
527
626
 
528
- // -------------------------------------------------------------------------
529
- // Append helpers
530
- // -------------------------------------------------------------------------
531
-
532
- /// @notice Append arbitrary bytes to the writer.
533
- /// @param writer Destination writer; `i` is advanced by `data.length`.
534
- /// @param data Bytes to append.
535
- function append(Writer memory writer, bytes memory data) internal pure {
536
- uint next = writer.i + data.length;
537
- if (next > writer.dst.length) revert WriterOverflow();
538
- // Copy `data` into `dst` starting at the current write position.
539
- assembly ("memory-safe") {
540
- mcopy(add(add(mload(add(writer, 0x40)), 0x20), mload(writer)), add(data, 0x20), mload(data))
541
- }
542
- writer.i = next;
627
+ /// @notice Append a dynamic block.
628
+ /// @param writer Destination writer; `i` is advanced by `Sizes.Header + data.length`.
629
+ /// @param key Block type key.
630
+ /// @param data Dynamic payload bytes.
631
+ function appendBlock(Writer memory writer, bytes4 key, bytes memory data) internal pure {
632
+ commit(writer, writeBlock(writer.dst, writer.i, key, data));
543
633
  }
544
634
 
545
635
  /// @notice Append a fixed-width 32-byte-payload block.
546
- /// @param writer Destination writer; `i` is advanced by `Sizes.B32`.
636
+ /// @param writer Destination writer; `i` is advanced by the kept logical block length.
547
637
  /// @param key Block type key.
548
638
  /// @param a First payload word.
549
- function append32(Writer memory writer, bytes4 key, bytes32 a) internal pure {
550
- writer.i = write32(writer.dst, writer.i, key, a);
551
- }
552
-
553
- /// @notice Append an ABI-style boolean as a 32-byte scalar payload block.
554
- /// Encodes `false` as `bytes32(0)` and `true` as `bytes32(uint(1))`.
555
- /// @param writer Destination writer; `i` is advanced by `Sizes.B32`.
556
- /// @param key Block type key.
557
- /// @param value Boolean value to encode.
558
- function appendBool(Writer memory writer, bytes4 key, bool value) internal pure {
559
- writer.i = writeBool(writer.dst, writer.i, key, value);
639
+ /// @param keep Number of bytes to keep from the final payload word (1..32).
640
+ function appendBlock32(Writer memory writer, bytes4 key, bytes32 a, uint keep) internal pure {
641
+ commit(writer, writeBlock32(writer.dst, writer.i, key, a, keep));
560
642
  }
561
643
 
562
-
563
644
  /// @notice Append a fixed-width 64-byte-payload block.
564
- /// @param writer Destination writer; `i` is advanced by `Sizes.B64`.
645
+ /// @param writer Destination writer; `i` is advanced by the kept logical block length.
565
646
  /// @param key Block type key.
566
647
  /// @param a First payload word.
567
648
  /// @param b Second payload word.
568
- function append64(Writer memory writer, bytes4 key, bytes32 a, bytes32 b) internal pure {
569
- writer.i = write64(writer.dst, writer.i, key, a, b);
649
+ /// @param keep Number of bytes to keep from the final payload word (1..32).
650
+ function appendBlock64(Writer memory writer, bytes4 key, bytes32 a, bytes32 b, uint keep) internal pure {
651
+ commit(writer, writeBlock64(writer.dst, writer.i, key, a, b, keep));
570
652
  }
571
653
 
572
654
  /// @notice Append a fixed-width 96-byte-payload block.
573
- /// @param writer Destination writer; `i` is advanced by `Sizes.B96`.
655
+ /// @param writer Destination writer; `i` is advanced by the kept logical block length.
574
656
  /// @param key Block type key.
575
657
  /// @param a First payload word.
576
658
  /// @param b Second payload word.
577
659
  /// @param c Third payload word.
578
- function append96(Writer memory writer, bytes4 key, bytes32 a, bytes32 b, bytes32 c) internal pure {
579
- writer.i = write96(writer.dst, writer.i, key, a, b, c);
660
+ /// @param keep Number of bytes to keep from the final payload word (1..32).
661
+ function appendBlock96(Writer memory writer, bytes4 key, bytes32 a, bytes32 b, bytes32 c, uint keep) internal pure {
662
+ commit(writer, writeBlock96(writer.dst, writer.i, key, a, b, c, keep));
580
663
  }
581
664
 
582
665
  /// @notice Append a fixed-width 128-byte-payload block.
583
- /// @param writer Destination writer; `i` is advanced by `Sizes.B128`.
666
+ /// @param writer Destination writer; `i` is advanced by the kept logical block length.
584
667
  /// @param key Block type key.
585
668
  /// @param a First payload word.
586
669
  /// @param b Second payload word.
587
670
  /// @param c Third payload word.
588
671
  /// @param d Fourth payload word.
589
- function append128(Writer memory writer, bytes4 key, bytes32 a, bytes32 b, bytes32 c, bytes32 d) internal pure {
590
- writer.i = write128(writer.dst, writer.i, key, a, b, c, d);
672
+ /// @param keep Number of bytes to keep from the final payload word (1..32).
673
+ function appendBlock128(
674
+ Writer memory writer,
675
+ bytes4 key,
676
+ bytes32 a,
677
+ bytes32 b,
678
+ bytes32 c,
679
+ bytes32 d,
680
+ uint keep
681
+ ) internal pure {
682
+ commit(writer, writeBlock128(writer.dst, writer.i, key, a, b, c, d, keep));
591
683
  }
592
684
 
593
685
  /// @notice Append a fixed-width 160-byte-payload block.
594
- /// @param writer Destination writer; `i` is advanced by `Sizes.B160`.
686
+ /// @param writer Destination writer; `i` is advanced by the kept logical block length.
595
687
  /// @param key Block type key.
596
688
  /// @param a First payload word.
597
689
  /// @param b Second payload word.
598
690
  /// @param c Third payload word.
599
691
  /// @param d Fourth payload word.
600
692
  /// @param e Fifth payload word.
601
- function append160(
693
+ /// @param keep Number of bytes to keep from the final payload word (1..32).
694
+ function appendBlock160(
602
695
  Writer memory writer,
603
696
  bytes4 key,
604
697
  bytes32 a,
605
698
  bytes32 b,
606
699
  bytes32 c,
607
700
  bytes32 d,
608
- bytes32 e
701
+ bytes32 e,
702
+ uint keep
609
703
  ) internal pure {
610
- writer.i = write160(writer.dst, writer.i, key, a, b, c, d, e);
704
+ commit(writer, writeBlock160(writer.dst, writer.i, key, a, b, c, d, e, keep));
611
705
  }
612
706
 
613
707
  /// @notice Append a dynamic block with a fixed 32-byte head word.
@@ -615,8 +709,8 @@ library Writers {
615
709
  /// @param key Block type key.
616
710
  /// @param a Fixed head word.
617
711
  /// @param tail Dynamic payload bytes appended after the head.
618
- function appendHead32(Writer memory writer, bytes4 key, bytes32 a, bytes memory tail) internal pure {
619
- writer.i = writeHead32(writer.dst, writer.i, key, a, tail);
712
+ function appendBlockHead32(Writer memory writer, bytes4 key, bytes32 a, bytes memory tail) internal pure {
713
+ commit(writer, writeBlockHead32(writer.dst, writer.i, key, a, tail));
620
714
  }
621
715
 
622
716
  /// @notice Append a dynamic block with a fixed 64-byte head.
@@ -625,16 +719,21 @@ library Writers {
625
719
  /// @param a First fixed head word.
626
720
  /// @param b Second fixed head word.
627
721
  /// @param tail Dynamic payload bytes appended after the head.
628
- function appendHead64(Writer memory writer, bytes4 key, bytes32 a, bytes32 b, bytes memory tail) internal pure {
629
- writer.i = writeHead64(writer.dst, writer.i, key, a, b, tail);
722
+ function appendBlockHead64(
723
+ Writer memory writer,
724
+ bytes4 key,
725
+ bytes32 a,
726
+ bytes32 b,
727
+ bytes memory tail
728
+ ) internal pure {
729
+ commit(writer, writeBlockHead64(writer.dst, writer.i, key, a, b, tail));
630
730
  }
631
731
 
632
- /// @notice Append a dynamic block.
633
- /// @param writer Destination writer; `i` is advanced by `Sizes.Header + data.length`.
634
- /// @param key Block type key.
635
- /// @param data Dynamic payload bytes.
636
- function appendBytes(Writer memory writer, bytes4 key, bytes memory data) internal pure {
637
- writer.i = write(writer.dst, writer.i, key, data);
732
+ /// @notice Append a STATUS form block.
733
+ /// @param writer Destination writer; `i` is advanced by `Sizes.B32`.
734
+ /// @param ok Status value to encode.
735
+ function appendStatus(Writer memory writer, bool ok) internal pure {
736
+ commit(writer, writeBlock32(writer.dst, writer.i, Keys.Status, ok ? bytes32(uint(1)) : bytes32(0), 32));
638
737
  }
639
738
 
640
739
  /// @notice Append a BALANCE block using separate field values.
@@ -643,14 +742,17 @@ library Writers {
643
742
  /// @param meta Asset metadata slot.
644
743
  /// @param amount Token amount.
645
744
  function appendBalance(Writer memory writer, bytes32 asset, bytes32 meta, uint amount) internal pure {
646
- appendBalance(writer, AssetAmount(asset, meta, amount));
745
+ commit(writer, writeBlock96(writer.dst, writer.i, Keys.Balance, asset, meta, bytes32(amount), 32));
647
746
  }
648
747
 
649
748
  /// @notice Append a BALANCE block from a struct.
650
749
  /// @param writer Destination writer; `i` is advanced by `Sizes.Balance`.
651
750
  /// @param value Balance fields to encode.
652
751
  function appendBalance(Writer memory writer, AssetAmount memory value) internal pure {
653
- writer.i = writeBalanceBlock(writer.dst, writer.i, value);
752
+ commit(
753
+ writer,
754
+ writeBlock96(writer.dst, writer.i, Keys.Balance, value.asset, value.meta, bytes32(value.amount), 32)
755
+ );
654
756
  }
655
757
 
656
758
  /// @notice Append an AMOUNT block using separate field values.
@@ -659,38 +761,63 @@ library Writers {
659
761
  /// @param meta Asset metadata slot.
660
762
  /// @param amount Token amount.
661
763
  function appendAmount(Writer memory writer, bytes32 asset, bytes32 meta, uint amount) internal pure {
662
- appendAmount(writer, AssetAmount(asset, meta, amount));
764
+ commit(writer, writeBlock96(writer.dst, writer.i, Keys.Amount, asset, meta, bytes32(amount), 32));
663
765
  }
664
766
 
665
767
  /// @notice Append an AMOUNT block from a struct.
666
768
  /// @param writer Destination writer; `i` is advanced by `Sizes.Balance`.
667
769
  /// @param value Amount fields to encode.
668
770
  function appendAmount(Writer memory writer, AssetAmount memory value) internal pure {
669
- writer.i = writeAmountBlock(writer.dst, writer.i, value);
771
+ commit(
772
+ writer,
773
+ writeBlock96(writer.dst, writer.i, Keys.Amount, value.asset, value.meta, bytes32(value.amount), 32)
774
+ );
670
775
  }
671
776
 
672
- /// @notice Append an ASSET block.
673
- /// @param writer Destination writer; `i` is advanced by `Sizes.B64`.
777
+ /// @notice Append an ACCOUNT_AMOUNT form block using separate field values.
778
+ /// @param writer Destination writer; `i` is advanced by `Sizes.B128`.
779
+ /// @param account Account identifier.
674
780
  /// @param asset Asset identifier.
675
781
  /// @param meta Asset metadata slot.
676
- function appendAsset(Writer memory writer, bytes32 asset, bytes32 meta) internal pure {
677
- writer.i = writeAssetBlock(writer.dst, writer.i, asset, meta);
782
+ /// @param amount Token amount.
783
+ function appendAccountAmount(
784
+ Writer memory writer,
785
+ bytes32 account,
786
+ bytes32 asset,
787
+ bytes32 meta,
788
+ uint amount
789
+ ) internal pure {
790
+ commit(
791
+ writer,
792
+ writeBlock128(writer.dst, writer.i, Keys.AccountAmount, account, asset, meta, bytes32(amount), 32)
793
+ );
678
794
  }
679
795
 
680
- /// @notice Append a BALANCE block only if `amount > 0`; silently skips zero amounts.
681
- /// @param writer Destination writer.
682
- /// @param asset Asset identifier.
683
- /// @param meta Asset metadata slot.
684
- /// @param amount Token amount; block is not written if this is zero.
685
- function appendNonZeroBalance(Writer memory writer, bytes32 asset, bytes32 meta, uint amount) internal pure {
686
- if (amount > 0) appendBalance(writer, asset, meta, amount);
796
+ /// @notice Append an ACCOUNT_AMOUNT form block from a struct.
797
+ /// @param writer Destination writer; `i` is advanced by `Sizes.B128`.
798
+ /// @param value Account amount fields to encode.
799
+ function appendAccountAmount(Writer memory writer, AccountAmount memory value) internal pure {
800
+ commit(
801
+ writer,
802
+ writeBlock128(
803
+ writer.dst,
804
+ writer.i,
805
+ Keys.AccountAmount,
806
+ value.account,
807
+ value.asset,
808
+ value.meta,
809
+ bytes32(value.amount),
810
+ 32
811
+ )
812
+ );
687
813
  }
688
814
 
689
- /// @notice Append a BALANCE block from a struct only if the amount is non-zero.
690
- /// @param writer Destination writer.
691
- /// @param value Balance fields; block is not written if `value.amount == 0`.
692
- function appendNonZeroBalance(Writer memory writer, AssetAmount memory value) internal pure {
693
- if (value.amount > 0) appendBalance(writer, value);
815
+ /// @notice Append an ASSET block.
816
+ /// @param writer Destination writer; `i` is advanced by `Sizes.B64`.
817
+ /// @param asset Asset identifier.
818
+ /// @param meta Asset metadata slot.
819
+ function appendAsset(Writer memory writer, bytes32 asset, bytes32 meta) internal pure {
820
+ commit(writer, writeBlock64(writer.dst, writer.i, Keys.Asset, asset, meta, 32));
694
821
  }
695
822
 
696
823
  /// @notice Append a BOUNTY block to the writer.
@@ -698,31 +825,79 @@ library Writers {
698
825
  /// @param amount Relayer reward amount.
699
826
  /// @param relayer Relayer account identifier.
700
827
  function appendBounty(Writer memory writer, uint amount, bytes32 relayer) internal pure {
701
- writer.i = writeBountyBlock(writer.dst, writer.i, amount, relayer);
828
+ commit(writer, writeBlock64(writer.dst, writer.i, Keys.Bounty, bytes32(amount), relayer, 32));
702
829
  }
703
830
 
704
831
  /// @notice Append a CUSTODY block using separate field values.
705
- /// @param writer Destination writer; `i` is advanced by `Sizes.Custody`.
832
+ /// @param writer Destination writer; `i` is advanced by `Sizes.HostAmount`.
706
833
  /// @param host Host node ID.
707
834
  /// @param asset Asset identifier.
708
835
  /// @param meta Asset metadata slot.
709
836
  /// @param amount Token amount.
710
837
  function appendCustody(Writer memory writer, uint host, bytes32 asset, bytes32 meta, uint amount) internal pure {
711
- appendCustody(writer, HostAmount(host, asset, meta, amount));
838
+ commit(
839
+ writer,
840
+ writeBlock128(writer.dst, writer.i, Keys.Custody, bytes32(host), asset, meta, bytes32(amount), 32)
841
+ );
842
+ }
843
+
844
+ /// @notice Append a CUSTODY block from a host and asset amount.
845
+ /// @param writer Destination writer; `i` is advanced by `Sizes.HostAmount`.
846
+ /// @param host Host node ID.
847
+ /// @param value Custody fields to encode.
848
+ function appendCustody(Writer memory writer, uint host, AssetAmount memory value) internal pure {
849
+ commit(
850
+ writer,
851
+ writeBlock128(
852
+ writer.dst,
853
+ writer.i,
854
+ Keys.Custody,
855
+ bytes32(host),
856
+ value.asset,
857
+ value.meta,
858
+ bytes32(value.amount),
859
+ 32
860
+ )
861
+ );
712
862
  }
713
863
 
714
- /// @notice Append a CUSTODY block from a struct.
715
- /// @param writer Destination writer; `i` is advanced by `Sizes.Custody`.
864
+ /// @notice Append a CUSTODY block from a host amount struct.
865
+ /// @param writer Destination writer; `i` is advanced by `Sizes.HostAmount`.
716
866
  /// @param value Custody fields to encode.
717
867
  function appendCustody(Writer memory writer, HostAmount memory value) internal pure {
718
- writer.i = writeCustodyBlock(writer.dst, writer.i, value);
868
+ commit(
869
+ writer,
870
+ writeBlock128(
871
+ writer.dst,
872
+ writer.i,
873
+ Keys.Custody,
874
+ bytes32(value.host),
875
+ value.asset,
876
+ value.meta,
877
+ bytes32(value.amount),
878
+ 32
879
+ )
880
+ );
719
881
  }
720
882
 
721
883
  /// @notice Append a TRANSACTION block from a struct.
722
884
  /// @param writer Destination writer; `i` is advanced by `Sizes.Transaction`.
723
885
  /// @param value Transfer record fields to encode.
724
- function appendTx(Writer memory writer, Tx memory value) internal pure {
725
- writer.i = writeTxBlock(writer.dst, writer.i, value);
886
+ function appendTransaction(Writer memory writer, Tx memory value) internal pure {
887
+ commit(
888
+ writer,
889
+ writeBlock160(
890
+ writer.dst,
891
+ writer.i,
892
+ Keys.Transaction,
893
+ bytes32(value.from),
894
+ bytes32(value.to),
895
+ value.asset,
896
+ value.meta,
897
+ bytes32(value.amount),
898
+ 32
899
+ )
900
+ );
726
901
  }
727
902
 
728
903
  // -------------------------------------------------------------------------
@@ -735,7 +910,7 @@ library Writers {
735
910
  /// @return out The written block stream; length equals `writer.i`.
736
911
  function finish(Writer memory writer) internal pure returns (bytes memory out) {
737
912
  if (writer.i == 0) revert EmptyRequest();
738
- if (writer.i > writer.dst.length) revert IncompleteWriter();
913
+ if (writer.i > writer.end || writer.i > writer.dst.length) revert IncompleteWriter();
739
914
  out = writer.dst;
740
915
  // Overwrite the memory length word of `out` with the actual written length.
741
916
  assembly ("memory-safe") {