@rootzero/contracts 1.15.0 → 1.17.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 (53) hide show
  1. package/CHANGELOG.md +90 -0
  2. package/Codec.sol +1 -1
  3. package/Commands.sol +1 -1
  4. package/Core.sol +4 -4
  5. package/Endpoints.sol +9 -7
  6. package/Events.sol +0 -1
  7. package/README.md +88 -30
  8. package/Utils.sol +1 -1
  9. package/codec/Blocks.sol +111 -21
  10. package/codec/Buffers.sol +3 -3
  11. package/codec/Decoders.sol +48 -10
  12. package/codec/Descriptors.sol +0 -1
  13. package/codec/Keys.sol +4 -2
  14. package/codec/Readers.sol +25 -0
  15. package/codec/Schema.sol +6 -1
  16. package/codec/Specs.sol +7 -2
  17. package/codec/Writers.sol +28 -2
  18. package/commands/Base.sol +8 -15
  19. package/commands/Burn.sol +2 -2
  20. package/commands/Credit.sol +38 -3
  21. package/commands/Debit.sol +50 -14
  22. package/commands/Deposit.sol +4 -4
  23. package/commands/Provision.sol +4 -4
  24. package/commands/Recover.sol +15 -8
  25. package/commands/Relay.sol +37 -12
  26. package/commands/Settle.sol +130 -0
  27. package/commands/Withdraw.sol +2 -2
  28. package/commands/admin/AllowAssets.sol +2 -2
  29. package/commands/admin/Allowance.sol +3 -3
  30. package/commands/admin/Annotate.sol +2 -2
  31. package/commands/admin/Appoint.sol +2 -2
  32. package/commands/admin/Authorize.sol +2 -2
  33. package/commands/admin/DenyAssets.sol +2 -2
  34. package/commands/admin/Dismiss.sol +2 -2
  35. package/commands/admin/Execute.sol +3 -3
  36. package/commands/admin/Unauthorize.sol +2 -2
  37. package/core/Endpoint.sol +15 -13
  38. package/core/Pipeline.sol +6 -6
  39. package/core/Settlement.sol +37 -8
  40. package/core/Types.sol +21 -1
  41. package/docs/Schema.md +52 -2
  42. package/execution/Budget.sol +12 -4
  43. package/execution/Execution.sol +119 -11
  44. package/guards/Base.sol +2 -2
  45. package/guards/Revoke.sol +21 -0
  46. package/package.json +1 -1
  47. package/ports/Base.sol +2 -2
  48. package/ports/Dispatch.sol +6 -6
  49. package/ports/{Settle.sol → Post.sol} +10 -10
  50. package/queries/Base.sol +2 -2
  51. package/utils/Actions.sol +1 -0
  52. package/utils/Cursors.sol +16 -30
  53. package/events/Position.sol +0 -22
@@ -1,7 +1,7 @@
1
1
  // SPDX-License-Identifier: GPL-3.0-only
2
2
  pragma solidity ^0.8.33;
3
3
 
4
- import {AssetAmount, AccountAsset, AccountAmount, HostAmount, HostAccountAsset, Tx} from "../core/Types.sol";
4
+ import {AssetAmount, AccountAsset, HostAsset, AccountAmount, HostAmount, HostAccountAsset, Position, Tx} from "../core/Types.sol";
5
5
  import {Blocks} from "../codec/Blocks.sol";
6
6
  import {Buffers} from "../codec/Buffers.sol";
7
7
  import {Sizes, Specs} from "../codec/Specs.sol";
@@ -9,6 +9,7 @@ import {Descriptors} from "../codec/Descriptors.sol";
9
9
  import {Cursors, Cur} from "../utils/Cursors.sol";
10
10
  import {Lanes} from "../utils/Lanes.sol";
11
11
  import {Budget} from "./Budget.sol";
12
+ import {max16} from "../utils/Utils.sol";
12
13
 
13
14
  /// @notice Mutable state shared across one endpoint execution.
14
15
  /// @dev `decoders` contains tagged input and state cursor lanes. Cursor operations
@@ -28,6 +29,10 @@ library Executions {
28
29
 
29
30
  /// @dev Thrown when an execution attempts to spend more value than remains in its budget.
30
31
  error InsufficientValue();
32
+ /// @dev Decoder block counts do not form compatible descriptor groups.
33
+ error BadRatio();
34
+ /// @dev A descriptor-empty lane received a non-empty block source.
35
+ error ZeroStride();
31
36
 
32
37
  // -------------------------------------------------------------------------
33
38
  // Opening
@@ -37,15 +42,39 @@ library Executions {
37
42
  /// @param source Calldata source for the decoder lane.
38
43
  /// @param descriptor Packed endpoint descriptor.
39
44
  /// @param lane Input or state lane identifier.
40
- /// @return cur Tagged packed decoder cursor, or zero for an absent lane.
45
+ /// @return cur Tagged packed decoder cursor carrying its raw block count, or zero for an absent lane.
41
46
  function openDecoder(bytes calldata source, uint descriptor, uint8 lane) private pure returns (uint cur) {
42
47
  uint stride = Descriptors.stride(descriptor, lane);
43
48
  (uint abs, uint limit) = Cursors.bounds(source);
44
49
  if (stride == 0 && abs == limit) return 0;
50
+ if (stride == 0) revert ZeroStride();
45
51
 
46
- bytes4 key = bytes4(source);
47
- (uint groups, uint end) = Blocks.scope(abs, limit, key, stride);
48
- cur = Cursors.create(abs, end - abs, groups, 0, lane);
52
+ bytes4 key = Descriptors.key(descriptor, lane);
53
+ (uint count, uint end) = Blocks.runExact(abs, limit, key);
54
+ if (count == 0) revert Blocks.EmptyRun();
55
+ cur = Cursors.create(abs, end - abs, count, 0, lane);
56
+ }
57
+
58
+ /// @dev Convert one decoder lane's raw block count into descriptor groups.
59
+ function groupCount(uint decoders, uint descriptor, uint8 lane) private pure returns (uint groups) {
60
+ uint stride = Descriptors.stride(descriptor, lane);
61
+ if (stride == 0) return 0;
62
+
63
+ uint count = decoders.select(lane).count();
64
+ if (count % stride != 0) revert BadRatio();
65
+ groups = count / stride;
66
+ }
67
+
68
+ /// @dev Reconcile decoder lane groups once at endpoint execution opening.
69
+ function reconcile(uint decoders, uint descriptor, uint expected) private pure returns (uint groups) {
70
+ uint input = groupCount(decoders, descriptor, Lanes.Input);
71
+ uint state = groupCount(decoders, descriptor, Lanes.State);
72
+ if (input != 0 && state != 0 && input != state) revert BadRatio();
73
+
74
+ groups = input != 0 ? input : state;
75
+ if (groups != 0 && expected != 0 && groups != expected) revert BadRatio();
76
+ if (groups == 0) groups = expected;
77
+ max16(groups);
49
78
  }
50
79
 
51
80
  /// @dev Initialize one tagged writer cursor from a descriptor lane.
@@ -58,7 +87,9 @@ library Executions {
58
87
  (uint capacity, bool growable) = Descriptors.allocation(descriptor, lane, batches);
59
88
  if (capacity == 0) return 0;
60
89
 
61
- cur = Buffers.cursor(capacity + padding, batches, growable, lane);
90
+ uint count = batches * Descriptors.stride(descriptor, lane);
91
+ if (padding != 0) count += padding / Sizes.Transaction;
92
+ cur = Buffers.cursor(capacity + padding, count, growable, lane);
62
93
  }
63
94
 
64
95
  /// @dev Open and pair the input and state decoder cursors.
@@ -91,7 +122,7 @@ library Executions {
91
122
  function open(uint decoders, uint descriptor, uint batches) private view returns (Execution memory exec) {
92
123
  exec.budget = msg.value;
93
124
  exec.decoders = decoders;
94
- batches = Cursors.reconcile(decoders, batches);
125
+ batches = reconcile(decoders, descriptor, batches);
95
126
  exec.writers = writerCursors(descriptor, batches);
96
127
  }
97
128
 
@@ -256,6 +287,31 @@ library Executions {
256
287
  (value.account, value.asset) = unpackAccountAsset(exec, lane);
257
288
  }
258
289
 
290
+ /// @notice Decode and consume one HOST_ASSET block from `lane`.
291
+ /// @param exec Execution whose decoder is advanced.
292
+ /// @param lane Decoder lane to consume.
293
+ /// @return host Decoded host identifier.
294
+ /// @return asset Decoded asset identifier.
295
+ function unpackHostAsset(
296
+ Execution memory exec,
297
+ uint8 lane
298
+ ) internal pure returns (uint host, bytes32 asset) {
299
+ uint abs;
300
+ (exec.decoders, abs) = exec.decoders.consume(lane, Sizes.HostAsset);
301
+ (host, asset) = Blocks.unpackHostAsset(abs);
302
+ }
303
+
304
+ /// @notice Decode one HOST_ASSET block into its structured value.
305
+ /// @param exec Execution whose decoder is advanced.
306
+ /// @param lane Decoder lane to consume.
307
+ /// @return value Decoded host and asset.
308
+ function unpackHostAssetValue(
309
+ Execution memory exec,
310
+ uint8 lane
311
+ ) internal pure returns (HostAsset memory value) {
312
+ (value.host, value.asset) = unpackHostAsset(exec, lane);
313
+ }
314
+
259
315
  /// @notice Decode and consume one AMOUNT block from `lane`.
260
316
  /// @param exec Execution whose decoder is advanced.
261
317
  /// @param lane Decoder lane to consume.
@@ -294,6 +350,24 @@ library Executions {
294
350
  (value.asset, value.amount) = unpackBalance(exec, lane);
295
351
  }
296
352
 
353
+ /// @notice Decode and consume one POSITION block from `lane`.
354
+ function unpackPosition(
355
+ Execution memory exec,
356
+ uint8 lane
357
+ ) internal pure returns (bytes32 asset, uint amount, bytes32 liability, uint debt) {
358
+ uint abs;
359
+ (exec.decoders, abs) = exec.decoders.consume(lane, Sizes.Position);
360
+ (asset, amount, liability, debt) = Blocks.unpackPosition(abs);
361
+ }
362
+
363
+ /// @notice Decode one POSITION block into its structured value.
364
+ function unpackPositionValue(
365
+ Execution memory exec,
366
+ uint8 lane
367
+ ) internal pure returns (Position memory value) {
368
+ (value.asset, value.amount, value.liability, value.debt) = unpackPosition(exec, lane);
369
+ }
370
+
297
371
  /// @notice Decode one BALANCE block and associate it with `host`.
298
372
  /// @param exec Execution whose decoder is advanced.
299
373
  /// @param lane Decoder lane to consume.
@@ -730,6 +804,23 @@ library Executions {
730
804
  outputBalance(exec, value.asset, value.amount);
731
805
  }
732
806
 
807
+ /// @notice Append a POSITION block to execution output.
808
+ function outputPosition(
809
+ Execution memory exec,
810
+ bytes32 asset,
811
+ uint amount,
812
+ bytes32 liability,
813
+ uint debt
814
+ ) internal pure {
815
+ uint i = reserve(exec, Sizes.Position);
816
+ Blocks.writePosition(exec.output, i, asset, amount, liability, debt);
817
+ }
818
+
819
+ /// @notice Append a structured POSITION value to execution output.
820
+ function outputPosition(Execution memory exec, Position memory value) internal pure {
821
+ outputPosition(exec, value.asset, value.amount, value.liability, value.debt);
822
+ }
823
+
733
824
  /// @notice Append an ACCOUNT_ASSET block to execution output.
734
825
  /// @param exec Execution receiving the block.
735
826
  /// @param account Account identifier to encode.
@@ -739,6 +830,15 @@ library Executions {
739
830
  Blocks.writeAccountAsset(exec.output, i, account, asset);
740
831
  }
741
832
 
833
+ /// @notice Append a HOST_ASSET block to execution output.
834
+ /// @param exec Execution receiving the block.
835
+ /// @param host Host identifier to encode.
836
+ /// @param asset Asset identifier to encode.
837
+ function outputHostAsset(Execution memory exec, uint host, bytes32 asset) internal pure {
838
+ uint i = reserve(exec, Sizes.HostAsset);
839
+ Blocks.writeHostAsset(exec.output, i, host, asset);
840
+ }
841
+
742
842
  /// @notice Append an ALLOCATION block to execution output.
743
843
  /// @param exec Execution receiving the block.
744
844
  /// @param host Host identifier to encode.
@@ -1010,15 +1110,23 @@ library Executions {
1010
1110
  exec.budget = 0;
1011
1111
  }
1012
1112
 
1113
+ /// @notice Deduct an exact native value from the execution budget.
1114
+ /// @param exec Mutable execution whose budget is charged.
1115
+ /// @param value Native value to consume in wei.
1116
+ /// @return The consumed native value.
1117
+ function useValue(Execution memory exec, uint value) internal pure returns (uint) {
1118
+ if (value > exec.budget) revert InsufficientValue();
1119
+ exec.budget -= value;
1120
+ return value;
1121
+ }
1122
+
1013
1123
  /// @notice Deduct the EVM value lane of `resources` from the execution budget.
1014
1124
  /// @dev EVM resources use the low 128 bits as native value/endowment.
1015
1125
  /// @param exec Mutable execution whose budget is charged.
1016
1126
  /// @param resources Packed resources whose value lane should be spent.
1017
1127
  /// @return value Native value to forward in wei.
1018
- function useValue(Execution memory exec, uint resources) internal pure returns (uint128 value) {
1019
- value = uint128(resources);
1020
- if (value > exec.budget) revert InsufficientValue();
1021
- exec.budget -= value;
1128
+ function useResourceValue(Execution memory exec, uint resources) internal pure returns (uint128) {
1129
+ return uint128(useValue(exec, uint128(resources)));
1022
1130
  }
1023
1131
 
1024
1132
  /// @notice Append a deferred transaction to the transaction writer lane.
package/guards/Base.sol CHANGED
@@ -1,7 +1,7 @@
1
1
  // SPDX-License-Identifier: GPL-3.0-only
2
2
  pragma solidity ^0.8.33;
3
3
 
4
- import {EndpointBase} from "../core/Endpoint.sol";
4
+ import {InputEndpointBase} from "../core/Endpoint.sol";
5
5
  import {GuardianAccess} from "../core/Access.sol";
6
6
  import {Specs} from "../codec/Specs.sol";
7
7
  import {Nodes} from "../utils/Nodes.sol";
@@ -10,7 +10,7 @@ import {Selectors} from "../utils/Selectors.sol";
10
10
  /// @title GuardBase
11
11
  /// @notice Abstract base for guardian-only direct host actions.
12
12
  /// Guard actions are non-payable direct calls with no command context, state, or response.
13
- abstract contract GuardBase is GuardianAccess, EndpointBase {
13
+ abstract contract GuardBase is GuardianAccess, InputEndpointBase {
14
14
  /// @dev Restrict execution to active guardian addresses.
15
15
  modifier onlyGuardian() {
16
16
  enforceGuardian(msg.sender);
package/guards/Revoke.sol CHANGED
@@ -1,6 +1,7 @@
1
1
  // SPDX-License-Identifier: GPL-3.0-only
2
2
  pragma solidity ^0.8.33;
3
3
 
4
+ import {AllowanceHook} from "../commands/admin/Allowance.sol";
4
5
  import {GuardBase} from "./Base.sol";
5
6
  import {Specs} from "../Codec.sol";
6
7
  import {Execution, Executions, Lanes} from "../execution/Execution.sol";
@@ -25,6 +26,26 @@ abstract contract Revoke is GuardBase {
25
26
  uint node = exec.unpackNode(Lanes.Input);
26
27
  setNode(node, false);
27
28
  }
29
+ }
30
+ }
31
+
32
+ /// @title RevokeAllowance
33
+ /// @notice Guardian action that revokes host-scoped asset allowances.
34
+ /// @dev Opt-in guard. Hosts expose it by inheriting this contract and implementing AllowanceHook.
35
+ abstract contract RevokeAllowance is GuardBase, AllowanceHook {
36
+ uint private immutable descriptor;
28
37
 
38
+ constructor() {
39
+ (, descriptor) = guard("revokeAllowance", Specs.HostAsset);
40
+ }
41
+
42
+ /// @notice Revoke every HOST_ASSET allowance in `input` as the active guardian.
43
+ function revokeAllowance(bytes calldata input) external onlyGuardian {
44
+ Execution memory exec = openInput(input, descriptor, 0);
45
+
46
+ while (exec.more()) {
47
+ (uint peer, bytes32 asset) = exec.unpackHostAsset(Lanes.Input);
48
+ allowance(peer, asset, 0);
49
+ }
29
50
  }
30
51
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rootzero/contracts",
3
- "version": "1.15.0",
3
+ "version": "1.17.0",
4
4
  "description": "Solidity contracts and protocol building blocks for rootzero hosts and commands.",
5
5
  "private": false,
6
6
  "license": "GPL-3.0-only",
package/ports/Base.sol CHANGED
@@ -4,7 +4,7 @@ pragma solidity ^0.8.33;
4
4
  import { NodeCalls } from "../core/Calls.sol";
5
5
  import { NodeAccess } from "../core/Access.sol";
6
6
  import { Specs } from "../codec/Specs.sol";
7
- import { EndpointBase } from "../core/Endpoint.sol";
7
+ import { InputEndpointBase } from "../core/Endpoint.sol";
8
8
  import { Nodes } from "../utils/Nodes.sol";
9
9
  import { Selectors } from "../utils/Selectors.sol";
10
10
  import { Descriptors } from "../codec/Descriptors.sol";
@@ -13,7 +13,7 @@ import { Descriptors } from "../codec/Descriptors.sol";
13
13
  /// @notice Abstract base for peer-facing rootzero ports.
14
14
  /// Ports handle inter-host operations between cooperating hosts.
15
15
  /// Access is restricted to trusted peer callers via `onlyPeer`.
16
- abstract contract PortBase is NodeCalls, NodeAccess, EndpointBase {
16
+ abstract contract PortBase is NodeCalls, NodeAccess, InputEndpointBase {
17
17
 
18
18
  /// @dev Restrict execution to trusted callers, excluding the commander.
19
19
  modifier onlyPeer() {
@@ -2,23 +2,23 @@
2
2
  pragma solidity ^0.8.33;
3
3
 
4
4
  import {PortBase} from "./Base.sol";
5
- import {RoutePayableHook} from "../commands/Relay.sol";
5
+ import {RelayPayableHook} from "../commands/Relay.sol";
6
6
  import {Specs} from "../Codec.sol";
7
7
  import {Execution, Executions, Lanes} from "../execution/Execution.sol";
8
8
 
9
9
  using Executions for Execution;
10
10
 
11
11
  /// @title PortDispatchPayable
12
- /// @notice Port endpoint that forwards DISPATCH blocks to a host-defined route hook.
13
- abstract contract PortDispatchPayable is PortBase, RoutePayableHook {
12
+ /// @notice Port endpoint that forwards DISPATCH blocks to a host-defined relay hook.
13
+ abstract contract PortDispatchPayable is PortBase, RelayPayableHook {
14
14
  uint private immutable descriptor;
15
15
 
16
16
  constructor() {
17
17
  (, descriptor) = port("portDispatchPayable", Specs.Dispatch, Specs.Empty, true);
18
18
  }
19
19
 
20
- /// @notice Forward peer-supplied dispatches to the host-defined route hook.
21
- /// @dev Route hooks receive the shared top-level source value
20
+ /// @notice Forward peer-supplied dispatches to the host-defined relay hook.
21
+ /// @dev Relay hooks receive the shared top-level source value
22
22
  /// budget. Any `msg.value` not spent by the hook remains on this host.
23
23
  /// @param data DISPATCH block stream supplied by the trusted peer.
24
24
  /// @return Empty response bytes.
@@ -27,7 +27,7 @@ abstract contract PortDispatchPayable is PortBase, RoutePayableHook {
27
27
 
28
28
  while (exec.more()) {
29
29
  (uint portal, uint resources, bytes calldata payload) = exec.unpackDispatch(Lanes.Input);
30
- route(portal, resources, bytes(payload), exec);
30
+ relayTo(portal, resources, bytes(payload), exec);
31
31
  }
32
32
 
33
33
  return "";
@@ -2,7 +2,7 @@
2
2
  pragma solidity ^0.8.33;
3
3
 
4
4
  import {PortBase} from "./Base.sol";
5
- import {Settlement} from "../core/Settlement.sol";
5
+ import {PostHook} from "../core/Settlement.sol";
6
6
  import {Specs} from "../Codec.sol";
7
7
  import {Execution, Executions, Lanes} from "../execution/Execution.sol";
8
8
  import {Action} from "../annotations/Action.sol";
@@ -10,29 +10,29 @@ import {Actions} from "../utils/Actions.sol";
10
10
 
11
11
  using Executions for Execution;
12
12
 
13
- /// @title PortSettle
14
- /// @notice Port that consumes peer-supplied TRANSACTION blocks through debit and credit hooks.
13
+ /// @title PortPost
14
+ /// @notice Port that posts peer-supplied TRANSACTION blocks through debit and credit hooks.
15
15
  /// Each TRANSACTION block calls `debitAccount` for `from` and `creditAccount` for `to`.
16
- abstract contract PortSettle is PortBase, Settlement, Action {
16
+ abstract contract PortPost is PortBase, PostHook, Action {
17
17
  uint private immutable descriptor;
18
18
 
19
19
  constructor() {
20
20
  uint id;
21
- (id, descriptor) = port("portSettle", Specs.Transaction, Specs.Empty, false);
22
- action(id, Actions.Settle);
21
+ (id, descriptor) = port("portPost", Specs.Transaction, Specs.Empty, false);
22
+ action(id, Actions.Post);
23
23
  }
24
24
 
25
- /// @notice Execute the port-settle call.
25
+ /// @notice Post peer-supplied transactions.
26
26
  /// @param data TRANSACTION block stream supplied by the trusted peer.
27
27
  /// @return Empty response bytes.
28
- function portSettle(bytes calldata data) external onlyPeer returns (bytes memory) {
28
+ function portPost(bytes calldata data) external onlyPeer returns (bytes memory) {
29
29
  Execution memory exec = openInput(data, descriptor, 0);
30
30
 
31
31
  while (exec.more()) {
32
32
  (bytes32 from, bytes32 to, bytes32 asset, uint amount) = exec.unpackTransaction(Lanes.Input);
33
- settle(from, to, asset, amount);
33
+ post(from, to, asset, amount);
34
34
  }
35
-
35
+
36
36
  return "";
37
37
  }
38
38
  }
package/queries/Base.sol CHANGED
@@ -1,7 +1,7 @@
1
1
  // SPDX-License-Identifier: GPL-3.0-only
2
2
  pragma solidity ^0.8.33;
3
3
 
4
- import { EndpointBase } from "../core/Endpoint.sol";
4
+ import { InputEndpointBase } from "../core/Endpoint.sol";
5
5
  import { Descriptors } from "../codec/Descriptors.sol";
6
6
  import { Specs } from "../codec/Specs.sol";
7
7
  import { Nodes } from "../utils/Nodes.sol";
@@ -11,7 +11,7 @@ import { Selectors } from "../utils/Selectors.sol";
11
11
  /// @notice Abstract base for rootzero query contracts.
12
12
  /// Queries are view-only entry points that consume a block-stream input and
13
13
  /// return a block-stream response.
14
- abstract contract QueryBase is EndpointBase {
14
+ abstract contract QueryBase is InputEndpointBase {
15
15
 
16
16
  /// @notice Publish query metadata and a default label.
17
17
  /// @param name Query entrypoint name and default label. It must exactly
package/utils/Actions.sol CHANGED
@@ -17,4 +17,5 @@ library Actions {
17
17
  uint32 constant Repay = 11;
18
18
  uint32 constant Liquidate = 12;
19
19
  uint32 constant Refund = 13;
20
+ uint32 constant Post = 14;
20
21
  }
package/utils/Cursors.sol CHANGED
@@ -12,12 +12,12 @@ struct Cur {
12
12
  }
13
13
 
14
14
  /// @title Cursors
15
- /// @notice Packed cursor state and navigation for grouped byte regions.
15
+ /// @notice Packed cursor state and navigation for byte regions.
16
16
  /// @dev Each 128-bit cursor uses the following layout:
17
17
  /// bits 0-31 i
18
18
  /// bits 32-63 offset
19
19
  /// bits 64-95 len
20
- /// bits 96-111 groups
20
+ /// bits 96-111 count (opaque consumer metadata)
21
21
  /// bits 112-119 flags (consumer-defined)
22
22
  /// bits 120-127 tag
23
23
  ///
@@ -28,7 +28,7 @@ struct Cur {
28
28
  /// cursor into that position while preserving the pair.
29
29
  ///
30
30
  /// A mark is a standalone cursor value used as an immutable positional
31
- /// reference. It retains the cursor's offset, length, groups, flags, and tag,
31
+ /// reference. It retains the cursor's offset, length, count, flags, and tag,
32
32
  /// but may carry a different `i`. A mark has no intrinsic boundary or movement
33
33
  /// semantics; callers may later use its position for comparison, validation,
34
34
  /// seeking, or another operation. Because it has the ordinary single-cursor
@@ -41,9 +41,6 @@ library Cursors {
41
41
  /// @dev A cursor position exceeds its logical length.
42
42
  error OutOfBounds();
43
43
 
44
- /// @dev Two optional group counts are both set but do not match.
45
- error BadRatio();
46
-
47
44
  /// @dev A cursor is not positioned at the expected offset.
48
45
  error UnexpectedPosition();
49
46
 
@@ -58,14 +55,14 @@ library Cursors {
58
55
  /// @notice Create a cursor positioned at its beginning.
59
56
  /// @param offset Absolute source or buffer offset.
60
57
  /// @param len Logical byte length.
61
- /// @param groups Logical group count.
58
+ /// @param items Opaque item count associated with the region.
62
59
  /// @param flags Consumer-defined flags.
63
60
  /// @param tag Cursor identity tag.
64
61
  /// @return cur Packed cursor.
65
- function create(uint offset, uint len, uint groups, uint8 flags, uint8 tag) internal pure returns (uint cur) {
62
+ function create(uint offset, uint len, uint items, uint8 flags, uint8 tag) internal pure returns (uint cur) {
66
63
  cur |= max32(offset) << 32;
67
64
  cur |= max32(len) << 64;
68
- cur |= max16(groups) << 96;
65
+ cur |= max16(items) << 96;
69
66
  cur |= uint(flags) << 112;
70
67
  cur |= uint(tag) << 120;
71
68
  }
@@ -113,7 +110,7 @@ library Cursors {
113
110
  /// @notice Return the active cursor without its position.
114
111
  /// @dev Ignores the upper cursor when `cur` is a pair.
115
112
  /// @param cur Packed cursor or cursor pair.
116
- /// @return The lower cursor's offset, length, groups, flags, and tag.
113
+ /// @return The lower cursor's offset, length, count, flags, and tag.
117
114
  function frame(uint cur) internal pure returns (uint) {
118
115
  return clear32(uint128(cur), 0);
119
116
  }
@@ -128,15 +125,20 @@ library Cursors {
128
125
 
129
126
  /// @notice Decode the consumer metadata and identity tag from the lower cursor.
130
127
  /// @param cur Packed cursor or cursor pair.
131
- /// @return groups Logical group count.
128
+ /// @return items Opaque item count.
132
129
  /// @return flags Consumer-defined flags.
133
130
  /// @return tag Cursor identity tag.
134
- function meta(uint cur) internal pure returns (uint groups, uint8 flags, uint8 tag) {
135
- groups = uint16(cur >> 96);
131
+ function meta(uint cur) internal pure returns (uint items, uint8 flags, uint8 tag) {
132
+ items = uint16(cur >> 96);
136
133
  flags = uint8(cur >> 112);
137
134
  tag = uint8(cur >> 120);
138
135
  }
139
136
 
137
+ /// @notice Return the opaque item count attached to the active cursor.
138
+ function count(uint cur) internal pure returns (uint) {
139
+ return uint16(cur >> 96);
140
+ }
141
+
140
142
  /// @notice Return whether both packed cursors remain at their initial positions.
141
143
  /// @param cur Packed cursor or cursor pair.
142
144
  /// @return Whether both lane positions are zero.
@@ -233,7 +235,7 @@ library Cursors {
233
235
  }
234
236
 
235
237
  /// @notice Create a child cursor over `[start, end)` within the lower cursor.
236
- /// @dev The child starts at position zero, has no groups, and uses the
238
+ /// @dev The child starts at position zero, has no recorded count, and uses the
237
239
  /// supplied tag. Any higher cursor is omitted.
238
240
  /// @param cur Parent cursor or cursor pair.
239
241
  /// @param start Child start relative to the parent base.
@@ -248,22 +250,6 @@ library Cursors {
248
250
 
249
251
  // Pairing and selection
250
252
 
251
- /// @notice Reconcile both packed cursor lanes with an expected group count.
252
- /// @dev Zero lanes and lanes with zero groups do not constrain the result.
253
- /// @param cur Packed cursor or cursor pair.
254
- /// @param expected Expected group count; zero accepts the encoded count.
255
- /// @return groups Reconciled effective group count.
256
- function reconcile(uint cur, uint expected) internal pure returns (uint groups) {
257
- uint low = uint16(cur >> 96);
258
- uint high = uint16(cur >> 224);
259
- if (low != 0 && high != 0 && low != high) revert BadRatio();
260
-
261
- groups = low != 0 ? low : high;
262
- if (groups != 0 && expected != 0 && groups != expected) revert BadRatio();
263
- if (groups == 0) groups = expected;
264
- max16(groups);
265
- }
266
-
267
253
  /// @notice Combine two cursors into one packed word.
268
254
  /// @dev Zero represents absence and acts as the identity value.
269
255
  /// @param low Cursor placed in the lower lane.
@@ -1,22 +0,0 @@
1
- // SPDX-License-Identifier: GPL-3.0-only
2
- pragma solidity ^0.8.33;
3
-
4
- import {EventEmitter} from "./Emitter.sol";
5
-
6
- /// @notice Emitted when the reported value of an asset-backed position changes or is observed.
7
- /// A value of 0 should be interpreted as a closed position.
8
- abstract contract PositionEvent is EventEmitter {
9
- string private constant ABI = "event Position(bytes32 indexed account, bytes32 asset, uint value, uint32 action, uint context, uint queryId)";
10
-
11
- /// @param account Account identifier that owns or is associated with the position.
12
- /// @param asset Asset identifier for the asset class.
13
- /// @param value Context-specific position value; 0 indicates a closed position.
14
- /// @param action Primary operation hint from `Actions`.
15
- /// @param context Reserved context value for future use.
16
- /// @param queryId Query ID associated with the position lookup or reporting context.
17
- event Position(bytes32 indexed account, bytes32 asset, uint value, uint32 action, uint context, uint queryId);
18
-
19
- constructor() {
20
- emit EventAbi(ABI);
21
- }
22
- }