@rootzero/contracts 1.25.0 → 1.26.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 (68) hide show
  1. package/CHANGELOG.md +101 -0
  2. package/Codec.sol +1 -2
  3. package/Endpoints.sol +2 -0
  4. package/README.md +61 -51
  5. package/Utils.sol +2 -1
  6. package/codec/Blocks.sol +276 -88
  7. package/codec/Buffers.sol +24 -20
  8. package/codec/Decoders.sol +35 -37
  9. package/codec/Descriptors.sol +75 -72
  10. package/codec/Keys.sol +5 -1
  11. package/codec/Schema.sol +5 -1
  12. package/codec/Specs.sol +19 -9
  13. package/codec/Writers.sol +14 -19
  14. package/commands/Allocate.sol +8 -7
  15. package/commands/Base.sol +14 -49
  16. package/commands/Bootstrap.sol +95 -0
  17. package/commands/Burn.sol +7 -7
  18. package/commands/Cashout.sol +85 -0
  19. package/commands/Credit.sol +18 -16
  20. package/commands/Debit.sol +23 -19
  21. package/commands/Deposit.sol +13 -13
  22. package/commands/Payout.sol +8 -8
  23. package/commands/Provision.sol +13 -13
  24. package/commands/Recover.sol +7 -7
  25. package/commands/Relay.sol +22 -23
  26. package/commands/Repay.sol +36 -34
  27. package/commands/Settle.sol +24 -22
  28. package/commands/Withdraw.sol +7 -7
  29. package/commands/admin/AllowAssets.sol +7 -7
  30. package/commands/admin/Allowance.sol +7 -7
  31. package/commands/admin/Annotate.sol +7 -7
  32. package/commands/admin/Appoint.sol +7 -7
  33. package/commands/admin/Authorize.sol +7 -7
  34. package/commands/admin/Base.sol +5 -8
  35. package/commands/admin/DenyAssets.sol +7 -7
  36. package/commands/admin/Dismiss.sol +7 -7
  37. package/commands/admin/Execute.sol +8 -8
  38. package/commands/admin/Unauthorize.sol +7 -7
  39. package/core/Calls.sol +3 -3
  40. package/core/Endpoint.sol +7 -7
  41. package/core/Pipeline.sol +19 -20
  42. package/execution/Budget.sol +2 -3
  43. package/execution/Execution.sol +271 -509
  44. package/guards/Base.sol +1 -1
  45. package/guards/Revoke.sol +7 -7
  46. package/package.json +1 -1
  47. package/ports/Allowance.sol +3 -3
  48. package/ports/Assets.sol +7 -7
  49. package/ports/Base.sol +1 -1
  50. package/ports/Credit.sol +3 -3
  51. package/ports/Debit.sol +3 -3
  52. package/ports/Dispatch.sol +3 -3
  53. package/ports/Pipe.sol +3 -3
  54. package/ports/Post.sol +3 -3
  55. package/ports/Redeem.sol +3 -3
  56. package/queries/Assets.sol +3 -3
  57. package/queries/Balances.sol +3 -3
  58. package/queries/Base.sol +1 -1
  59. package/utils/Accounts.sol +1 -3
  60. package/utils/Actions.sol +2 -0
  61. package/utils/Assets.sol +1 -10
  62. package/utils/Cursors.sol +60 -116
  63. package/utils/Errors.sol +50 -0
  64. package/utils/Ids.sol +2 -5
  65. package/utils/Lanes.sol +0 -2
  66. package/utils/Nodes.sol +1 -3
  67. package/utils/Utils.sol +2 -9
  68. package/codec/Readers.sol +0 -190
package/codec/Buffers.sol CHANGED
@@ -1,17 +1,12 @@
1
1
  // SPDX-License-Identifier: GPL-3.0-only
2
2
  pragma solidity ^0.8.33;
3
3
 
4
- import {Cursors} from "../utils/Cursors.sol";
4
+ import {ValueOverflow} from "../utils/Errors.sol";
5
5
 
6
6
  /// @title Buffers
7
7
  /// @notice Allocation and finalization helpers for mutable memory byte buffers.
8
- /// @dev Packed buffer positions use `Cursors`; flag bit 0 denotes growth policy.
9
- /// `write` copies from memory, while `copy` copies directly from calldata.
8
+ /// @dev `write` copies from memory, while `copy` copies directly from calldata.
10
9
  library Buffers {
11
- using Cursors for uint;
12
-
13
- uint8 internal constant Growable = 1;
14
-
15
10
  /// @dev A reserved memory write exceeds the physical backing buffer.
16
11
  error BufferOverflow();
17
12
  /// @dev A buffer cursor does not match its logical or physical capacity.
@@ -19,12 +14,12 @@ library Buffers {
19
14
 
20
15
  /// @notice Create a packed buffer cursor at write position zero.
21
16
  /// @param len Initial logical byte capacity.
22
- /// @param count Number of logical items represented by the buffer.
23
- /// @param growable Whether writes may expand the logical capacity.
17
+ /// @param stride Optional blocks per group represented by the buffer.
24
18
  /// @param tag Cursor identity tag.
25
19
  /// @return cur Packed buffer cursor.
26
- function cursor(uint len, uint count, bool growable, uint8 tag) internal pure returns (uint cur) {
27
- cur = Cursors.create(0, len, count, growable ? Growable : 0, tag);
20
+ function cursor(uint len, uint8 stride, uint8 tag) internal pure returns (uint cur) {
21
+ if (len > type(uint32).max) revert ValueOverflow();
22
+ cur = (len << 64) | (uint(stride) << 96) | (uint(tag) << 120);
28
23
  }
29
24
 
30
25
  /// @notice Reserve relative write space and return the updated packed buffer cursor.
@@ -43,24 +38,28 @@ library Buffers {
43
38
  uint advance,
44
39
  uint touch
45
40
  ) internal pure returns (uint updated, bytes memory dst, uint i) {
46
- uint len;
47
- (i, , len) = cur.decode();
41
+ i = uint32(cur);
42
+ uint len = uint32(cur >> 64);
48
43
  uint required = i + (advance > touch ? advance : touch);
49
44
  bool empty = buffer.length == 0;
50
45
  dst = buffer;
51
46
 
52
- if (cur.flagged(Growable) && required > len) {
47
+ if (required > len) {
53
48
  len = len == 0 ? 64 : len * 2;
54
49
  while (len < required) {
55
50
  len *= 2;
56
51
  }
57
- cur = cur.resize(len);
52
+ if (len > type(uint32).max) revert ValueOverflow();
53
+ cur = (cur & ~(uint(type(uint32).max) << 64)) | (len << 64);
58
54
  if (!empty) dst = resize(dst, i, len);
59
55
  }
60
56
 
61
- updated = cur.advance(advance);
57
+ updated = cur + advance;
62
58
 
63
- if (empty) dst = alloc(len);
59
+ if (empty) {
60
+ uint padded = ((len + 31) & ~uint(31)) + 32;
61
+ dst = new bytes(padded);
62
+ }
64
63
  if (required > dst.length) revert BufferOverflow();
65
64
  }
66
65
 
@@ -81,7 +80,8 @@ library Buffers {
81
80
  /// @param capacity Requested capacity of the new buffer.
82
81
  /// @return resized Newly allocated buffer containing the written prefix.
83
82
  function resize(bytes memory buffer, uint written, uint capacity) internal pure returns (bytes memory resized) {
84
- resized = alloc(capacity);
83
+ uint padded = ((capacity + 31) & ~uint(31)) + 32;
84
+ resized = new bytes(padded);
85
85
  assembly ("memory-safe") {
86
86
  mcopy(add(resized, 0x20), add(buffer, 0x20), written)
87
87
  }
@@ -171,9 +171,13 @@ library Buffers {
171
171
  /// @param buffer Backing byte buffer.
172
172
  /// @return out Empty bytes when unused, otherwise the written prefix.
173
173
  function finish(uint cur, bytes memory buffer) internal pure returns (bytes memory out) {
174
- (uint i, , uint len) = cur.decode();
174
+ uint i = uint32(cur);
175
+ uint len = uint32(cur >> 64);
175
176
  if (i == 0) return new bytes(0);
176
177
  if (i > len || i > buffer.length) revert IncompleteBuffer();
177
- out = trim(buffer, i);
178
+ assembly ("memory-safe") {
179
+ mstore(buffer, i)
180
+ }
181
+ out = buffer;
178
182
  }
179
183
  }
@@ -5,6 +5,7 @@ import {AssetAmount, AccountAsset, HostAsset, AccountAmount, HostAmount, HostAcc
5
5
  import {Blocks} from "./Blocks.sol";
6
6
  import {Sizes, Specs} from "./Specs.sol";
7
7
  import {Cursors, Cur} from "../utils/Cursors.sol";
8
+ import {UnconsumedData} from "../utils/Errors.sol";
8
9
 
9
10
  using Decoders for Cur;
10
11
 
@@ -17,40 +18,11 @@ library Decoders {
17
18
  // Cur memory adapters
18
19
  // -------------------------------------------------------------------------
19
20
 
20
- /// @notice Wrap a calldata source in an ungrouped cursor.
21
- /// @param source Calldata region to wrap.
22
- /// @return cur Cursor spanning the complete source.
23
- function wrap(bytes calldata source) internal pure returns (Cur memory cur) {
24
- cur.state = Cursors.wrap(source, 0, 0);
25
- }
26
-
27
- /// @notice Wrap the calldata tail beginning at relative position `i`.
28
- /// @param source Calldata region containing the tail.
29
- /// @param i Relative tail position.
30
- /// @return cur Cursor spanning `source[i:]`.
31
- function wrap(bytes calldata source, uint i) internal pure returns (Cur memory cur) {
32
- cur.state = Cursors.wrap(source[i:], 0, 0);
33
- }
34
-
35
- /// @notice Open a non-empty calldata source as an ungrouped cursor.
21
+ /// @notice Open a calldata source without inspecting its contents.
36
22
  /// @param source Calldata region to open.
37
23
  /// @return cur Cursor spanning the complete source.
38
24
  function open(bytes calldata source) internal pure returns (Cur memory cur) {
39
- if (source.length == 0) revert Blocks.EmptyRun();
40
- cur.state = Cursors.wrap(source, 0, 0);
41
- }
42
-
43
- /// @notice Open the first homogeneous batch in `source`.
44
- /// @param source Calldata block stream to open.
45
- /// @return cur Counted cursor spanning the first homogeneous batch.
46
- function batch(bytes calldata source) internal pure returns (Cur memory cur) {
47
- (uint abs, uint limit) = Cursors.bounds(source);
48
- if (abs == limit) revert Blocks.EmptyRun();
49
-
50
- bytes4 key = bytes4(source);
51
- (uint count, uint end) = Blocks.run(abs, limit, key);
52
- if (count == 0) revert Blocks.EmptyRun();
53
- cur.state = Cursors.create(abs, end - abs, count, 0, 0);
25
+ cur.state = Cursors.wrap(source, 0);
54
26
  }
55
27
 
56
28
  /// @notice Return whether `cur` has unread bytes.
@@ -60,6 +32,12 @@ library Decoders {
60
32
  return cur.state.more();
61
33
  }
62
34
 
35
+ /// @notice Require the complete decoder source to have been consumed.
36
+ /// @param cur Decoder cursor to close.
37
+ function close(Cur memory cur) internal pure {
38
+ if (cur.state.more()) revert UnconsumedData();
39
+ }
40
+
63
41
  /// @notice Return the cursor's current absolute calldata position.
64
42
  /// @param cur Cursor to inspect.
65
43
  /// @return Current absolute calldata position.
@@ -164,9 +142,9 @@ library Decoders {
164
142
  /// @param cur Cursor whose calldata is returned.
165
143
  /// @return Complete cursor region.
166
144
  function raw(Cur memory cur) internal pure returns (bytes calldata) {
167
- (, uint offset, uint len) = cur.state.decode();
168
- if (len > msg.data.length || offset > msg.data.length - len) revert Blocks.MalformedBlocks();
169
- return msg.data[offset:offset + len];
145
+ (uint abs, uint end) = cur.state.bounds();
146
+ if (end > msg.data.length) revert Blocks.MalformedBlocks();
147
+ return msg.data[abs:end];
170
148
  }
171
149
 
172
150
  /// @notice Return relative calldata range `[from, to)` from `cur`.
@@ -398,6 +376,26 @@ library Decoders {
398
376
  node = Blocks.unpackNode(abs);
399
377
  }
400
378
 
379
+ /// @notice Decode and consume one CASHOUT block.
380
+ /// @param cur Cursor advanced past the block.
381
+ /// @return amount Native-asset amount to withdraw.
382
+ function unpackCashout(Cur memory cur) internal pure returns (uint amount) {
383
+ uint abs;
384
+ (cur.state, abs) = cur.state.consume(Sizes.Cashout);
385
+ amount = Blocks.unpackCashout(abs);
386
+ }
387
+
388
+ /// @notice Decode and consume one BOOTSTRAP block.
389
+ /// @param cur Cursor advanced past the block.
390
+ /// @return asset Decoded asset identifier.
391
+ /// @return amount Decoded balance amount.
392
+ /// @return budget Decoded native-value budget contribution.
393
+ function unpackBootstrap(Cur memory cur) internal pure returns (bytes32 asset, uint amount, uint budget) {
394
+ uint abs;
395
+ (cur.state, abs) = cur.state.consume(Sizes.Bootstrap);
396
+ return Blocks.unpackBootstrap(abs);
397
+ }
398
+
401
399
  /// @notice Decode and consume one ASSET block.
402
400
  /// @param cur Cursor advanced past the block.
403
401
  /// @return asset Decoded asset identifier.
@@ -738,14 +736,14 @@ library Decoders {
738
736
  /// @notice Decode and consume one STEP block.
739
737
  /// @param cur Cursor advanced past the block.
740
738
  /// @return cmd Decoded command identifier.
741
- /// @return resources Decoded packed resources.
739
+ /// @return value Decoded native value.
742
740
  /// @return input Decoded command input.
743
741
  function unpackStep(
744
742
  Cur memory cur
745
- ) internal pure returns (uint cmd, uint resources, bytes calldata input) {
743
+ ) internal pure returns (uint cmd, uint128 value, bytes calldata input) {
746
744
  uint abs = cur.state.absolute();
747
745
  uint end;
748
- (cmd, resources, input, end) = Blocks.unpackStep(abs);
746
+ (cmd, value, input, end) = Blocks.unpackStep(abs);
749
747
  cur.state = cur.state.seekAbs(end);
750
748
  }
751
749
 
@@ -1,7 +1,9 @@
1
1
  // SPDX-License-Identifier: GPL-3.0-only
2
2
  pragma solidity ^0.8.33;
3
3
 
4
- import {Specs} from "./Specs.sol";
4
+ import {Blocks} from "./Blocks.sol";
5
+ import {Buffers} from "./Buffers.sol";
6
+ import {Sizes, Specs} from "./Specs.sol";
5
7
  import {Lanes} from "../utils/Lanes.sol";
6
8
 
7
9
  /// @title Flags
@@ -19,107 +21,108 @@ library Flags {
19
21
  /// @title Descriptors
20
22
  /// @notice Packing and lane metadata helpers for endpoint descriptors.
21
23
  library Descriptors {
22
- /// @dev The requested lane is not part of an endpoint descriptor.
23
- error InvalidLane();
24
-
25
24
  /// @notice Create a descriptor from endpoint lane specifications and flags.
26
25
  /// @dev Layout: `[state key:4][stride:1]`
27
26
  /// `[input key:4][stride:1]`
28
27
  /// `[output key:4][min:4][max:4][hint:3][stride:1]`
29
- /// `[reserved:4]`
30
- /// `[transactions:1]`
28
+ /// `[reserved:5]`
31
29
  /// `[flags:1]`. Flag bits: funded = 0, admin = 1; bits 6 and 7 are
32
30
  /// reserved for endpoint-defined custom flags.
31
+ /// @dev TODO: Add per-lane cardinality metadata so off-chain consumers can
32
+ /// distinguish exactly-one lanes from batches. Command decoding must remain
33
+ /// the runtime source of truth; this metadata should not restore eager scans.
33
34
  /// @param state State lane specification.
34
35
  /// @param input Direct input lane specification.
35
36
  /// @param output Output writer specification.
36
- /// @param transactions Transactions produced per batch.
37
37
  /// @param flags Endpoint behavior flags.
38
38
  /// @return descriptor Packed endpoint descriptor.
39
39
  function create(
40
40
  uint state,
41
41
  uint input,
42
42
  uint output,
43
- uint8 transactions,
44
43
  uint8 flags
45
44
  ) internal pure returns (uint descriptor) {
46
45
  output = Specs.normalize(output);
47
- descriptor = pack(state, input, output, transactions, flags);
48
- }
49
-
50
- /// @dev Pack endpoint specs and flags into a descriptor.
51
- /// @param state State specification.
52
- /// @param input Input specification.
53
- /// @param output Normalized direct output specification.
54
- /// @param transactions Transactions produced per batch.
55
- /// @param flags Endpoint behavior flags.
56
- /// @return descriptor Packed endpoint descriptor.
57
- function pack(
58
- uint state,
59
- uint input,
60
- uint output,
61
- uint8 transactions,
62
- uint8 flags
63
- ) private pure returns (uint descriptor) {
64
46
  descriptor |= uint(Specs.lane(state)) << 216;
65
47
  descriptor |= uint(Specs.lane(input)) << 176;
66
48
  descriptor |= (output >> 128) << 48;
67
- descriptor |= uint(transactions) << 8;
68
49
  descriptor |= flags;
69
50
  }
70
51
 
71
- /// @notice Return whether a descriptor contains `flag`.
72
- /// @param descriptor Packed endpoint descriptor.
73
- /// @param flag Flag bit or bit set to test.
74
- /// @return Whether any requested flag bit is present.
75
- function flagged(uint descriptor, uint8 flag) internal pure returns (bool) {
76
- return uint8(descriptor) & flag != 0;
52
+ /// @dev Wrap endpoint input with its descriptor stride and lane tag.
53
+ function inputCursor(bytes calldata source, uint descriptor) private pure returns (uint cur) {
54
+ uint tag = Lanes.Input;
55
+ assembly ("memory-safe") {
56
+ cur := or(
57
+ or(or(shl(32, source.offset), shl(64, source.length)), shl(96, byte(9, descriptor))),
58
+ shl(120, tag)
59
+ )
60
+ }
61
+ }
62
+
63
+ /// @dev Wrap command state with its descriptor stride and lane tag.
64
+ function stateCursor(bytes calldata source, uint descriptor) private pure returns (uint cur) {
65
+ uint tag = Lanes.State;
66
+ assembly ("memory-safe") {
67
+ cur := or(
68
+ or(or(shl(32, source.offset), shl(64, source.length)), shl(96, byte(4, descriptor))),
69
+ shl(120, tag)
70
+ )
71
+ }
72
+ }
73
+
74
+ /// @dev Initialize the output writer from the selected lower decoder lane.
75
+ /// The run count is an allocation hint only; decoding and finalization validate the source.
76
+ function writerCursor(uint decoders, uint descriptor) private pure returns (uint writer) {
77
+ uint outputStride = uint8(descriptor >> 48);
78
+ if (outputStride == 0) return 0;
79
+
80
+ uint decoderStride = uint8(decoders >> 96);
81
+ uint count;
82
+ if (decoderStride != 0) {
83
+ uint abs = uint32(decoders >> 32);
84
+ uint end = abs + uint32(decoders >> 64);
85
+ uint laneShift = uint8(decoders >> 120) == Lanes.Input ? 176 : 216;
86
+ count = Blocks.runCount(
87
+ abs,
88
+ end,
89
+ bytes4(uint32(descriptor >> (laneShift + 8)))
90
+ ) / decoderStride * outputStride;
91
+ }
92
+
93
+ uint capacity = count * (Sizes.Header + uint24(descriptor >> 56));
94
+ writer = Buffers.cursor(capacity, uint8(outputStride), 0);
77
95
  }
78
96
 
79
- /// @notice Return the effective per-batch stride for `lane`.
80
- /// @dev Descriptor creation resolves implicit spec strides, so every lane
81
- /// stores its effective value directly.
82
- /// @param descriptor Packed endpoint descriptor.
83
- /// @param lane Lane identifier from `Lanes`.
84
- /// @return Effective blocks per batch for the lane.
85
- function stride(uint descriptor, uint8 lane) internal pure returns (uint8) {
86
- if (lane == Lanes.State) return uint8(descriptor >> 216);
87
- if (lane == Lanes.Input) return uint8(descriptor >> 176);
88
- if (lane == Lanes.Output) return uint8(descriptor >> 48);
89
- if (lane == Lanes.Transactions) return uint8(descriptor >> 8);
90
- revert InvalidLane();
97
+ /// @notice Open descriptor-backed input and output cursors.
98
+ function openInput(
99
+ uint descriptor,
100
+ bytes calldata input
101
+ ) internal pure returns (uint decoders, uint writer) {
102
+ decoders = inputCursor(input, descriptor);
103
+ writer = writerCursor(decoders, descriptor);
91
104
  }
92
105
 
93
- /// @notice Return the effective block key for `lane`.
94
- /// @dev Transaction blocks have a fixed protocol key that is not stored in
95
- /// the descriptor.
96
- /// @param descriptor Packed endpoint descriptor.
97
- /// @param lane Lane identifier from `Lanes`.
98
- /// @return Effective block key for the lane.
99
- function key(uint descriptor, uint8 lane) internal pure returns (bytes4) {
100
- if (lane == Lanes.State) return bytes4(uint32(descriptor >> 224));
101
- if (lane == Lanes.Input) return bytes4(uint32(descriptor >> 184));
102
- if (lane == Lanes.Output) return bytes4(uint32(descriptor >> 144));
103
- if (lane == Lanes.Transactions) return Specs.key(Specs.Transaction);
104
- revert InvalidLane();
106
+ /// @notice Open descriptor-backed state and output cursors.
107
+ function openState(
108
+ uint descriptor,
109
+ bytes calldata state
110
+ ) internal pure returns (uint decoders, uint writer) {
111
+ decoders = stateCursor(state, descriptor);
112
+ writer = writerCursor(decoders, descriptor);
105
113
  }
106
114
 
107
- /// @notice Return the buffer configuration for descriptor writer `lane`.
108
- /// @param descriptor Packed endpoint descriptor.
109
- /// @param lane Output or transaction writer lane.
110
- /// @param groups Number of execution batches to allocate for.
111
- /// @return capacity Initial logical byte capacity.
112
- /// @return growable Whether the writer may grow beyond that capacity.
113
- function allocation(uint descriptor, uint8 lane, uint groups) internal pure returns (uint capacity, bool growable) {
114
- if (lane == Lanes.Output) {
115
- uint spec = uint(uint128(descriptor >> 48)) << 128;
116
- return Specs.allocation(spec, groups);
115
+ /// @notice Open paired descriptor-backed state, input, and output cursors.
116
+ /// @dev Input remains low when present; otherwise state becomes the active lower lane.
117
+ function open(
118
+ uint descriptor,
119
+ bytes calldata state,
120
+ bytes calldata input
121
+ ) internal pure returns (uint decoders, uint writer) {
122
+ decoders = inputCursor(input, descriptor) | (stateCursor(state, descriptor) << 128);
123
+ if (uint8(decoders >> 96) == 0) {
124
+ decoders = (decoders << 128) | (decoders >> 128);
117
125
  }
118
- if (lane == Lanes.Transactions) {
119
- uint count = groups * stride(descriptor, lane);
120
- return Specs.allocation(Specs.Transaction, count);
121
- }
122
-
123
- revert InvalidLane();
126
+ writer = writerCursor(decoders, descriptor);
124
127
  }
125
128
  }
package/codec/Keys.sol CHANGED
@@ -11,6 +11,10 @@ library Keys {
11
11
  bytes4 constant Empty = bytes4(0);
12
12
  /// @dev Input amount - (bytes32 asset, uint amount)
13
13
  bytes4 constant Amount = bytes4(keccak256("#amount"));
14
+ /// @dev Native-asset cashout request - (uint amount)
15
+ bytes4 constant Cashout = bytes4(keccak256("#cashout"));
16
+ /// @dev Pipeline bootstrap request - (bytes32 asset, uint amount, uint budget)
17
+ bytes4 constant Bootstrap = bytes4(keccak256("#bootstrap"));
14
18
  /// @dev Ledger balance - (bytes32 asset, uint amount)
15
19
  bytes4 constant Balance = bytes4(keccak256("#balance"));
16
20
  /// @dev Liability-only debt state - (bytes32 liability, uint debt)
@@ -35,7 +39,7 @@ library Keys {
35
39
  bytes4 constant Account = bytes4(keccak256("#account"));
36
40
  /// @dev Transfer record passed through the pipeline - (bytes32 from, bytes32 to, bytes32 asset, uint amount)
37
41
  bytes4 constant Transaction = bytes4(keccak256("#transaction"));
38
- /// @dev Sub-command invocation - (uint cmd, uint resources, #bytes as input)
42
+ /// @dev Sub-command invocation - (uint cmd, uint128 value, #bytes as input)
39
43
  bytes4 constant Step = bytes4(keccak256("#step"));
40
44
  /// @dev Portal relay input - (uint portal, uint resources, #bytes as input)
41
45
  bytes4 constant Relay = bytes4(keccak256("#relay"));
package/codec/Schema.sol CHANGED
@@ -32,6 +32,8 @@ pragma solidity ^0.8.33;
32
32
  // - `resources` fields are chain-specific resource words. A portal adapter
33
33
  // interprets them for the destination runtime. EVM resources use the low
34
34
  // 128 bits as native value.
35
+ // - STEP encodes native `value` directly as uint128; it does not carry a
36
+ // chain-specific resources word
35
37
  // - dotted field names and aliases, e.g. `dst.portal` or `#bytes as dst.payload`,
36
38
  // are offchain projection metadata only and do not change runtime encoding
37
39
  // - `at N` assigns an offchain presentation position to one sibling; explicit
@@ -84,6 +86,7 @@ library Schemas {
84
86
  string constant Account = "bytes32 account";
85
87
  string constant Asset = "bytes32 asset";
86
88
  string constant Status = "uint code";
89
+ string constant Cashout = "uint amount";
87
90
 
88
91
  // Two-word payloads
89
92
 
@@ -95,6 +98,7 @@ library Schemas {
95
98
 
96
99
  // Three-word payloads
97
100
 
101
+ string constant Bootstrap = "bytes32 asset, uint amount, uint budget";
98
102
  string constant Allocation = "uint host, bytes32 asset, uint amount";
99
103
  string constant Allowance = "uint host, bytes32 asset, uint amount";
100
104
  string constant Custody = "uint host, bytes32 asset, uint amount";
@@ -110,8 +114,8 @@ library Schemas {
110
114
 
111
115
  // Composite payloads
112
116
 
117
+ string constant Step = "uint cmd, uint128 value, #bytes as input";
113
118
  string constant Call = "uint target, uint resources, #bytes as payload";
114
- string constant Step = "uint cmd, uint resources, #bytes as input";
115
119
  string constant Relay = "uint portal, uint resources, #bytes as input";
116
120
  string constant Dispatch = "uint portal, uint resources, #bytes as payload";
117
121
  string constant Context = "bytes32 account, #bytes as state, #bytes as input";
package/codec/Specs.sol CHANGED
@@ -21,8 +21,14 @@ library Sizes {
21
21
  uint constant B128 = Header + 4 * Word;
22
22
  /// @dev 8 header + 160 payload = 168 bytes total.
23
23
  uint constant B160 = Header + 5 * Word;
24
+ /// @dev Minimum STEP size: 8 header + 32 command + 16 value + 8 nested BYTES header.
25
+ uint constant Step = 2 * Header + Word + 16;
24
26
  /// @dev STATUS block: 8 header + 32 status code = 40 bytes
25
27
  uint constant Status = B32;
28
+ /// @dev CASHOUT block: 8 header + 32 native-asset amount = 40 bytes
29
+ uint constant Cashout = B32;
30
+ /// @dev BOOTSTRAP block: 8 header + 32 asset + 32 amount + 32 budget = 104 bytes
31
+ uint constant Bootstrap = B96;
26
32
  /// @dev AMOUNT block: 8 header + 32 asset + 32 amount = 72 bytes
27
33
  uint constant Amount = B64;
28
34
  /// @dev BALANCE block: 8 header + 32 asset + 32 amount = 72 bytes
@@ -44,7 +50,7 @@ library Sizes {
44
50
  /// `[key:4][min:4][max:4][hint:3][stride:1][reserved:16]`.
45
51
  /// The upper eight bytes of a fixed-layout spec are its encoded block header,
46
52
  /// allowing the entire spec word to be written directly as that header.
47
- /// A maximum of zero means unbounded and requires a growable writer.
53
+ /// A maximum of zero means the payload size is unbounded.
48
54
  library Specs {
49
55
  /// @dev A payload is incompatible with its block specification.
50
56
  error InvalidSpec();
@@ -58,11 +64,14 @@ library Specs {
58
64
  uint private constant Exact128 = 128 * SizeFields;
59
65
  uint private constant UnboundedHint128 = uint(128) << 136;
60
66
  uint private constant UnboundedMin40Hint256 = (uint(40) << 192) | (uint(256) << 136);
67
+ uint private constant UnboundedMin56Hint256 = (uint(56) << 192) | (uint(256) << 136);
61
68
  uint private constant UnboundedMin72Hint256 = (uint(72) << 192) | (uint(256) << 136);
62
69
  uint private constant UnboundedMin48Hint512 = (uint(48) << 192) | (uint(512) << 136);
63
70
  uint private constant UnboundedMin104Hint256 = (uint(104) << 192) | (uint(256) << 136);
64
71
 
65
72
  uint constant Empty = uint(bytes32(Keys.Empty));
73
+ uint constant Cashout = uint(bytes32(Keys.Cashout)) | Exact32;
74
+ uint constant Bootstrap = uint(bytes32(Keys.Bootstrap)) | Exact96;
66
75
  uint constant Amount = uint(bytes32(Keys.Amount)) | Exact64;
67
76
  uint constant Balance = uint(bytes32(Keys.Balance)) | Exact64;
68
77
  uint constant Debt = uint(bytes32(Keys.Debt)) | Exact64;
@@ -76,7 +85,7 @@ library Specs {
76
85
  uint constant String = uint(bytes32(Keys.String)) | UnboundedHint128;
77
86
  uint constant Account = uint(bytes32(Keys.Account)) | Exact32;
78
87
  uint constant Transaction = uint(bytes32(Keys.Transaction)) | Exact128;
79
- uint constant Step = uint(bytes32(Keys.Step)) | UnboundedMin72Hint256;
88
+ uint constant Step = uint(bytes32(Keys.Step)) | UnboundedMin56Hint256;
80
89
  uint constant Relay = uint(bytes32(Keys.Relay)) | UnboundedMin72Hint256;
81
90
  uint constant Context = uint(bytes32(Keys.Context)) | UnboundedMin48Hint512;
82
91
  uint constant Recover = uint(bytes32(Keys.Recover)) | UnboundedMin104Hint256;
@@ -205,18 +214,19 @@ library Specs {
205
214
  /// @param groups Number of groups.
206
215
  /// @return Number of blocks across all groups.
207
216
  function count(uint spec, uint groups) internal pure returns (uint) {
208
- return groups * stride(normalize(spec));
217
+ uint8 n = uint8(spec >> 128);
218
+ if (n == 0 && uint32(spec >> 224) != 0) n = 1;
219
+ return groups * n;
209
220
  }
210
221
 
211
- /// @notice Return the buffer configuration for `groups` of `spec`.
212
- /// @dev Dynamic specs use their allocation hint and a maximum of zero means growable.
222
+ /// @notice Return the initial buffer capacity for `groups` of `spec`.
213
223
  /// @param spec Packed block specification.
214
224
  /// @param groups Number of groups to allocate.
215
225
  /// @return capacity Initial encoded byte capacity.
216
- /// @return growable Whether the buffer may grow beyond its initial capacity.
217
- function allocation(uint spec, uint groups) internal pure returns (uint capacity, bool growable) {
218
- capacity = count(spec, groups) * (Sizes.Header + uint24(spec >> 136));
219
- growable = uint32(spec >> 160) == 0;
226
+ function allocation(uint spec, uint groups) internal pure returns (uint capacity) {
227
+ uint8 n = uint8(spec >> 128);
228
+ if (n == 0 && uint32(spec >> 224) != 0) n = 1;
229
+ capacity = groups * n * (Sizes.Header + uint24(spec >> 136));
220
230
  }
221
231
 
222
232
  /// @notice Return `spec` grouped with an explicit stride.
package/codec/Writers.sol CHANGED
@@ -8,8 +8,7 @@ import {Sizes, Specs} from "./Specs.sol";
8
8
 
9
9
  /// @notice Sequential block stream writer backed by a pre-allocated memory buffer.
10
10
  struct Writer {
11
- /// @dev Packed cursor metadata. `len` is logical capacity and flag bit 0
12
- /// indicates whether the backing buffer may grow.
11
+ /// @dev Packed cursor metadata. `len` is the current logical capacity.
13
12
  uint cur;
14
13
  /// @dev Destination buffer. Physical capacity may be padded up to a full 32-byte word;
15
14
  /// final length is set to the packed write position by `finish`.
@@ -30,22 +29,18 @@ library Writers {
30
29
 
31
30
  /// @notice Initialize writer metadata without allocating a backing buffer.
32
31
  /// @param len Initial logical byte capacity of the writer.
33
- /// @param growable Whether append helpers may expand the capacity.
34
32
  /// @return writer Unallocated writer positioned at index 0.
35
- function init(uint len, bool growable) internal pure returns (Writer memory writer) {
36
- writer.cur = Buffers.cursor(len, 0, growable, 0);
33
+ function init(uint len) internal pure returns (Writer memory writer) {
34
+ writer.cur = Buffers.cursor(len, 0, 0);
37
35
  }
38
36
 
39
37
  /// @notice Initialize writer metadata for `groups` of blocks described by `spec`.
40
38
  /// @param spec Packed block key, payload bounds, allocation hint, and flags.
41
39
  /// @param groups Number of descriptor groups the writer is expected to encode.
42
- /// @return writer Unallocated writer with capacity derived from the specification,
43
- /// or an inert empty writer when its capacity is zero.
40
+ /// @return writer Unallocated writer with initial capacity derived from the specification.
44
41
  function init(uint spec, uint groups) internal pure returns (Writer memory writer) {
45
- (uint capacity, bool growable) = Specs.allocation(spec, groups);
46
- if (capacity == 0) return writer;
47
-
48
- writer.cur = Buffers.cursor(capacity, Specs.count(spec, groups), growable, 0);
42
+ uint capacity = Specs.allocation(spec, groups);
43
+ writer.cur = Buffers.cursor(capacity, Specs.stride(Specs.normalize(spec)), 0);
49
44
  }
50
45
 
51
46
  // -------------------------------------------------------------------------
@@ -66,7 +61,7 @@ library Writers {
66
61
  /// @param size Number of bytes to reserve and touch.
67
62
  /// @return i Relative offset reserved for the write.
68
63
  function reserve(Writer memory writer, uint size) private pure returns (uint i) {
69
- return reserve(writer, size, size);
64
+ (writer.cur, writer.dst, i) = Buffers.reserve(writer.cur, writer.dst, size, size);
70
65
  }
71
66
 
72
67
  // -------------------------------------------------------------------------
@@ -417,12 +412,12 @@ library Writers {
417
412
  /// @notice Append a STEP block.
418
413
  /// @param writer Destination writer.
419
414
  /// @param cmd Command identifier to encode.
420
- /// @param resources Packed resources to encode.
415
+ /// @param value Native value to encode.
421
416
  /// @param input Command input to encode.
422
- function appendStep(Writer memory writer, uint cmd, uint resources, bytes memory input) internal pure {
423
- uint size = Sizes.B64 + Sizes.Header + input.length;
417
+ function appendStep(Writer memory writer, uint cmd, uint128 value, bytes memory input) internal pure {
418
+ uint size = Sizes.Step + input.length;
424
419
  uint i = reserve(writer, size);
425
- Blocks.writeStep(writer.dst, i, cmd, resources, input);
420
+ Blocks.writeStep(writer.dst, i, cmd, value, input);
426
421
  }
427
422
 
428
423
  /// @notice Append a CALL block.
@@ -566,10 +561,10 @@ library Writers {
566
561
  }
567
562
 
568
563
  /// @notice Append a STEP block by copying its nested input from calldata.
569
- function copyStep(Writer memory writer, uint cmd, uint resources, bytes calldata input) internal pure {
570
- uint size = Sizes.B64 + Sizes.Header + input.length;
564
+ function copyStep(Writer memory writer, uint cmd, uint128 value, bytes calldata input) internal pure {
565
+ uint size = Sizes.Step + input.length;
571
566
  uint i = reserve(writer, size);
572
- Blocks.copyStep(writer.dst, i, cmd, resources, input);
567
+ Blocks.copyStep(writer.dst, i, cmd, value, input);
573
568
  }
574
569
 
575
570
  /// @notice Append a CALL block by copying its nested payload from calldata.