@rootzero/contracts 0.9.8 → 1.0.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 (56) hide show
  1. package/Core.sol +1 -1
  2. package/Endpoints.sol +3 -1
  3. package/Events.sol +2 -0
  4. package/README.md +19 -18
  5. package/blocks/Cursors.sol +152 -162
  6. package/blocks/Keys.sol +10 -6
  7. package/blocks/Schema.sol +17 -9
  8. package/blocks/Writers.sol +4 -4
  9. package/commands/Burn.sol +7 -4
  10. package/commands/Credit.sol +9 -9
  11. package/commands/Debit.sol +7 -4
  12. package/commands/Deposit.sol +14 -8
  13. package/commands/Payout.sol +49 -0
  14. package/commands/Provision.sol +14 -8
  15. package/commands/Relay.sol +58 -0
  16. package/commands/Withdraw.sol +10 -9
  17. package/commands/admin/AllowAssets.sol +9 -4
  18. package/commands/admin/Allowance.sol +7 -4
  19. package/commands/admin/Appoint.sol +7 -4
  20. package/commands/admin/Authorize.sol +7 -4
  21. package/commands/admin/DenyAssets.sol +9 -4
  22. package/commands/admin/Destroy.sol +5 -3
  23. package/commands/admin/Dismiss.sol +7 -4
  24. package/commands/admin/Execute.sol +8 -5
  25. package/commands/admin/Init.sol +5 -3
  26. package/commands/admin/Unauthorize.sol +7 -4
  27. package/core/Host.sol +10 -1
  28. package/core/Pipeline.sol +4 -3
  29. package/core/Runtime.sol +3 -36
  30. package/core/Types.sol +1 -1
  31. package/docs/Schema.md +0 -1
  32. package/events/Admin.sol +11 -8
  33. package/events/Chain.sol +20 -0
  34. package/events/Command.sol +11 -8
  35. package/events/Peer.sol +5 -5
  36. package/events/Query.sol +2 -4
  37. package/events/Transfer.sol +22 -0
  38. package/guards/Revoke.sol +3 -3
  39. package/package.json +1 -1
  40. package/peer/AllowAssets.sol +5 -3
  41. package/peer/Allowance.sol +5 -3
  42. package/peer/BalancePull.sol +5 -3
  43. package/peer/DenyAssets.sol +5 -3
  44. package/peer/Dispatch.sol +51 -0
  45. package/peer/Pipe.sol +7 -5
  46. package/peer/Settle.sol +13 -8
  47. package/queries/Assets.sol +3 -3
  48. package/queries/Balances.sol +3 -3
  49. package/queries/Positions.sol +3 -3
  50. package/utils/Accounts.sol +6 -31
  51. package/utils/Actions.sol +11 -9
  52. package/utils/Assets.sol +21 -21
  53. package/utils/Ids.sol +12 -2
  54. package/utils/Layout.sol +21 -17
  55. package/utils/Utils.sol +2 -2
  56. package/commands/Transfer.sol +0 -54
package/peer/Settle.sol CHANGED
@@ -2,15 +2,16 @@
2
2
  pragma solidity ^0.8.33;
3
3
 
4
4
  import { PeerBase } from "./Base.sol";
5
- import { TransferHook } from "../commands/Transfer.sol";
5
+ import { CreditAccountHook } from "../commands/Credit.sol";
6
+ import { DebitAccountHook } from "../commands/Debit.sol";
6
7
  import { Cursors, Cur, Schemas } from "../Cursors.sol";
7
8
 
8
9
  using Cursors for Cur;
9
10
 
10
11
  /// @title PeerSettle
11
- /// @notice Peer that consumes peer-supplied TRANSACTION blocks through the shared transfer hook.
12
- /// Each TRANSACTION block in the request calls `transfer(value)`. Restricted to trusted peers.
13
- abstract contract PeerSettle is PeerBase, TransferHook {
12
+ /// @notice Peer that consumes peer-supplied TRANSACTION blocks through debit and credit hooks.
13
+ /// Each TRANSACTION block calls `debitAccount` for `from` and `creditAccount` for `to`.
14
+ abstract contract PeerSettle is PeerBase, DebitAccountHook, CreditAccountHook {
14
15
  string private constant NAME = "peerSettle";
15
16
  uint internal immutable peerSettleId = peerId(NAME);
16
17
 
@@ -19,14 +20,18 @@ abstract contract PeerSettle is PeerBase, TransferHook {
19
20
  }
20
21
 
21
22
  /// @notice Execute the peer-settle call.
23
+ /// @param request TRANSACTION block stream supplied by the trusted peer.
24
+ /// @return Empty response bytes.
22
25
  function peerSettle(bytes calldata request) external onlyPeer returns (bytes memory) {
23
- (Cur memory state, ) = cursor(request, 1);
26
+ (Cur memory state, , ) = Cursors.init(request, 0, 1);
24
27
 
25
- while (state.i < state.bound) {
26
- transfer(state.unpackTxValue());
28
+ while (state.i < state.len) {
29
+ (bytes32 from, bytes32 to, bytes32 asset, bytes32 meta, uint amount) = state.unpackTransaction();
30
+ if (from != 0) debitAccount(from, asset, meta, amount);
31
+ if (to != 0) creditAccount(to, asset, meta, amount);
27
32
  }
28
33
 
29
- state.close();
34
+ state.complete();
30
35
  return "";
31
36
  }
32
37
  }
@@ -33,16 +33,16 @@ abstract contract AssetStatus is QueryBase, AssetStatusHook {
33
33
  /// @param request Block-stream request consisting of `#asset { bytes32 asset, bytes32 meta }` blocks.
34
34
  /// @return Block-stream response containing one `#status { uint code }` per asset block.
35
35
  function assetStatus(bytes calldata request) external view returns (bytes memory) {
36
- (Cur memory query, uint groups) = cursor(request, 1);
36
+ (Cur memory query, uint groups, ) = Cursors.init(request, 0, 1);
37
37
  Writer memory response = Writers.allocStatuses(groups);
38
38
 
39
- while (query.i < query.bound) {
39
+ while (query.i < query.len) {
40
40
  (bytes32 asset, bytes32 meta) = query.unpackAsset();
41
41
  uint status = assetStatus(asset, meta);
42
42
  response.appendStatus(status);
43
43
  }
44
44
 
45
- query.close();
45
+ query.complete();
46
46
  return response.finish();
47
47
  }
48
48
  }
@@ -33,16 +33,16 @@ abstract contract GetBalances is QueryBase, GetBalancesHook {
33
33
  /// @param request Block-stream request consisting of `accountAsset(account, asset, meta)*`.
34
34
  /// @return Block-stream response containing one `accountAmount(account, asset, meta, amount)` block per request block.
35
35
  function getBalances(bytes calldata request) external view returns (bytes memory) {
36
- (Cur memory query, uint groups) = cursor(request, 1);
36
+ (Cur memory query, uint groups, ) = Cursors.init(request, 0, 1);
37
37
  Writer memory response = Writers.allocAccountAmounts(groups);
38
38
 
39
- while (query.i < query.bound) {
39
+ while (query.i < query.len) {
40
40
  (bytes32 account, bytes32 asset, bytes32 meta) = query.unpackAccountAsset();
41
41
  uint amount = getBalance(account, asset, meta);
42
42
  response.appendAccountAmount(account, asset, meta, amount);
43
43
  }
44
44
 
45
- query.close();
45
+ query.complete();
46
46
  return response.finish();
47
47
  }
48
48
  }
@@ -42,15 +42,15 @@ abstract contract GetPosition is QueryBase, GetPositionHook {
42
42
  /// @param request Block-stream request consisting of `accountAsset(account, asset, meta)*`.
43
43
  /// @return Block-stream response containing one output-schema block per position block.
44
44
  function getPosition(bytes calldata request) external view returns (bytes memory) {
45
- (Cur memory query, uint groups) = cursor(request, 1);
45
+ (Cur memory query, uint groups, ) = Cursors.init(request, 0, 1);
46
46
  Writer memory response = Writers.allocAny(groups);
47
47
 
48
- while (query.i < query.bound) {
48
+ while (query.i < query.len) {
49
49
  (bytes32 account, bytes32 asset, bytes32 meta) = query.unpackAccountAsset();
50
50
  appendPosition(account, asset, meta, response);
51
51
  }
52
52
 
53
- query.close();
53
+ query.complete();
54
54
  return response.finish();
55
55
  }
56
56
  }
@@ -23,8 +23,6 @@ library Accounts {
23
23
  uint32 constant Guardian = (uint32(Layout.Evm32) << 16) | (uint32(Layout.Account) << 8) | uint32(Layout.Guardian);
24
24
  /// @dev Full 4-byte type prefix for user accounts (chain-agnostic EVM address).
25
25
  uint32 constant User = (uint32(Layout.Evm32) << 16) | (uint32(Layout.Account) << 8) | uint32(Layout.User);
26
- /// @dev Full 4-byte type prefix for keccak accounts (opaque 28-byte hash).
27
- uint32 constant Keccak = (uint32(Layout.Opaque32) << 16) | (uint32(Layout.Account) << 8) | uint32(Layout.Keccak);
28
26
 
29
27
  /// @notice Extract the 4-byte type prefix from an account ID.
30
28
  /// @param account Account identifier.
@@ -53,8 +51,12 @@ library Accounts {
53
51
  return prefix(account) == User;
54
52
  }
55
53
 
56
- function isKeccak(bytes32 account) internal pure returns (bool) {
57
- return prefix(account) == Keccak;
54
+ /// @notice Assert that `input` is an account and return it unchanged.
55
+ /// @param input Account identifier to validate.
56
+ /// @return account The same `input` if it is an account.
57
+ function ensure(bytes32 input) internal pure returns (bytes32 account) {
58
+ if (!isAccount(input)) revert InvalidAccount();
59
+ return input;
58
60
  }
59
61
 
60
62
  /// @notice Assert that `input` is an admin account and return it unchanged.
@@ -81,14 +83,6 @@ library Accounts {
81
83
  return input;
82
84
  }
83
85
 
84
- /// @notice Assert that `input` is a keccak account and return it unchanged.
85
- /// @param input Account identifier to validate.
86
- /// @return account The same `input` if it is a keccak account.
87
- function keccak(bytes32 input) internal pure returns (bytes32 account) {
88
- if (!isKeccak(input)) revert InvalidAccount();
89
- return input;
90
- }
91
-
92
86
  /// @notice Encode an EVM address as a chain-local admin account ID.
93
87
  /// @param addr EVM address to embed.
94
88
  /// @return Admin account ID bound to the current chain.
@@ -110,25 +104,6 @@ library Accounts {
110
104
  return bytes32(toUnspecifiedBase(User) | (uint(uint160(addr)) << 32));
111
105
  }
112
106
 
113
- function toKeccak(bytes32 head, bytes32 meta) internal pure returns (bytes32) {
114
- return bytes32(toUnspecifiedBase(Keccak) | uint224(uint256(keccak256(bytes.concat(head, meta)))));
115
- }
116
-
117
- function matchesKeccak(bytes32 account, bytes32 head, bytes32 meta) internal pure returns (bool) {
118
- return account == toKeccak(head, meta);
119
- }
120
-
121
- /// @notice Assert that `account` uses the Account layout tag and return it unchanged.
122
- /// Ignores width, chain binding, and subtype details.
123
- /// @param account Account ID to validate.
124
- /// @return The same `account` value if valid.
125
- function ensure(bytes32 account) internal pure returns (bytes32) {
126
- if (uint8(uint(account) >> 232) != Layout.Account) {
127
- revert InvalidAccount();
128
- }
129
- return account;
130
- }
131
-
132
107
  /// @notice Assert that `account` belongs to the EVM account family and return it unchanged.
133
108
  /// @param account Account ID to validate.
134
109
  /// @return The same `account` value if valid.
package/utils/Actions.sol CHANGED
@@ -4,13 +4,15 @@ pragma solidity ^0.8.33;
4
4
  library Actions {
5
5
  uint32 constant None = 0;
6
6
  uint32 constant Transfer = 1;
7
- uint32 constant Deposit = 2;
8
- uint32 constant Withdraw = 3;
9
- uint32 constant Fee = 4;
10
- uint32 constant Mint = 5;
11
- uint32 constant Burn = 6;
12
- uint32 constant Swap = 7;
13
- uint32 constant Borrow = 8;
14
- uint32 constant Repay = 9;
15
- uint32 constant Liquidate = 10;
7
+ uint32 constant Payout = 2;
8
+ uint32 constant Settle = 3;
9
+ uint32 constant Deposit = 4;
10
+ uint32 constant Withdraw = 5;
11
+ uint32 constant Fee = 6;
12
+ uint32 constant Mint = 7;
13
+ uint32 constant Burn = 8;
14
+ uint32 constant Swap = 9;
15
+ uint32 constant Borrow = 10;
16
+ uint32 constant Repay = 11;
17
+ uint32 constant Liquidate = 12;
16
18
  }
package/utils/Assets.sol CHANGED
@@ -8,10 +8,10 @@ import {matchesBase, toLocalBase} from "./Utils.sol";
8
8
  /// @notice Encoding and decoding helpers for 256-bit asset identifiers.
9
9
  ///
10
10
  /// Asset IDs embed a 4-byte type tag in bits [255:224]:
11
- /// - `Value` native chain value (ETH); no address payload
12
- /// - `Erc20` ERC-20 token; contract address in bits [191:32]
13
- /// - `Erc721` ERC-721 collection; collection address in bits [191:32]
14
- /// - `Erc1155` ERC-1155 collection; collection address in bits [191:32]
11
+ /// - `Native` - native chain coin/token; no address payload
12
+ /// - `Erc20` - ERC-20 token; contract address in bits [191:32]
13
+ /// - `Erc721` - ERC-721 collection; collection address in bits [191:32]
14
+ /// - `Erc1155` - ERC-1155 collection; collection address in bits [191:32]
15
15
  ///
16
16
  /// All asset IDs are chain-local (include `block.chainid` in bits [223:192]).
17
17
  library Assets {
@@ -20,8 +20,8 @@ library Assets {
20
20
  /// @dev Thrown when an asset is not authorized for the requested operation.
21
21
  error UnauthorizedAsset();
22
22
 
23
- /// @dev Full 4-byte type prefix for the native value asset.
24
- uint32 constant Value = (uint32(Layout.Evm32) << 16) | (uint32(Layout.Asset) << 8) | uint32(Layout.Value);
23
+ /// @dev Full 4-byte type prefix for the native chain coin/token asset.
24
+ uint32 constant Native = (uint32(Layout.Evm32) << 16) | (uint32(Layout.Asset) << 8) | uint32(Layout.Native);
25
25
  /// @dev Full 4-byte type prefix for ERC-20 assets.
26
26
  uint32 constant Erc20 = (uint32(Layout.Evm32) << 16) | (uint32(Layout.Asset) << 8) | uint32(Layout.Erc20);
27
27
  /// @dev Full 4-byte type prefix for ERC-721 assets.
@@ -34,19 +34,19 @@ library Assets {
34
34
  return uint8(uint(asset) >> 232) == Layout.Asset;
35
35
  }
36
36
 
37
- /// @notice Return true if `asset` uses the 32-byte asset layout with no metadata identity (top byte is `0x20`).
37
+ /// @notice Return true if `asset` uses a 32-byte asset layout with no metadata identity.
38
38
  function is32(bytes32 asset) internal pure returns (bool) {
39
- return isAsset(asset) && bytes1(asset) == 0x20;
39
+ return isAsset(asset) && uint8(uint(asset) >> 240) == Layout.Width32;
40
40
  }
41
41
 
42
- /// @notice Return true if `asset` uses the 64-byte asset layout with metadata-backed identity (top byte is `0x40`).
42
+ /// @notice Return true if `asset` uses a 64-byte asset layout with metadata-backed identity.
43
43
  function is64(bytes32 asset) internal pure returns (bool) {
44
- return isAsset(asset) && bytes1(asset) == 0x40;
44
+ return isAsset(asset) && uint8(uint(asset) >> 240) == Layout.Width64;
45
45
  }
46
46
 
47
- /// @notice Return true if `asset` is the local native value asset.
48
- function isValue(bytes32 asset) internal view returns (bool) {
49
- return asset == toValue();
47
+ /// @notice Return true if `asset` is the local native chain coin/token asset.
48
+ function isNative(bytes32 asset) internal view returns (bool) {
49
+ return asset == toNative();
50
50
  }
51
51
 
52
52
  /// @notice Return true if `asset` is a local ERC-20 asset.
@@ -64,11 +64,11 @@ library Assets {
64
64
  return matchesBase(asset, toLocalBase(Erc1155));
65
65
  }
66
66
 
67
- /// @notice Assert that `input` is the local native value asset and return it unchanged.
67
+ /// @notice Assert that `input` is the local native chain coin/token asset and return it unchanged.
68
68
  /// @param input Asset identifier to validate.
69
- /// @return asset The same `input` if it is the local native value asset.
70
- function value(bytes32 input) internal view returns (bytes32 asset) {
71
- if (!isValue(input)) revert InvalidAsset();
69
+ /// @return asset The same `input` if it is the local native asset.
70
+ function native(bytes32 input) internal view returns (bytes32 asset) {
71
+ if (!isNative(input)) revert InvalidAsset();
72
72
  return input;
73
73
  }
74
74
 
@@ -96,10 +96,10 @@ library Assets {
96
96
  return input;
97
97
  }
98
98
 
99
- /// @notice Create a chain-local native value asset ID.
99
+ /// @notice Create a chain-local native coin/token asset ID.
100
100
  /// @return Asset ID for the native token on the current chain.
101
- function toValue() internal view returns (bytes32) {
102
- return bytes32(toLocalBase(Value));
101
+ function toNative() internal view returns (bytes32) {
102
+ return bytes32(toLocalBase(Native));
103
103
  }
104
104
 
105
105
  /// @notice Create a chain-local ERC-20 asset ID for `addr`.
@@ -178,7 +178,7 @@ library Assets {
178
178
  }
179
179
 
180
180
  /// @notice Derive a storage slot for an (asset, meta) pair.
181
- /// For 32-byte EVM assets (no meta), the slot is the asset ID itself.
181
+ /// For 32-byte assets (no meta), the slot is the asset ID itself.
182
182
  /// For assets with metadata (e.g. ERC-721 or ERC-1155 token IDs), the slot is
183
183
  /// `keccak256(asset ++ meta)`.
184
184
  /// Reverts only if `asset` is zero.
package/utils/Ids.sol CHANGED
@@ -18,6 +18,8 @@ library Ids {
18
18
 
19
19
  /// @dev 24-bit family tag shared by all node types (Evm32 + Node category).
20
20
  uint24 constant Node = (uint24(Layout.Evm32) << 8) | uint24(Layout.Node);
21
+ /// @dev Full 4-byte type prefix for chain/domain nodes.
22
+ uint32 constant Chain = (uint32(Layout.Evm32) << 16) | (uint32(Layout.Node) << 8) | uint32(Layout.Chain);
21
23
  /// @dev Full 4-byte type prefix for host nodes.
22
24
  uint32 constant Host = (uint32(Layout.Evm32) << 16) | (uint32(Layout.Node) << 8) | uint32(Layout.Host);
23
25
  /// @dev Full 4-byte type prefix for command nodes.
@@ -56,7 +58,7 @@ library Ids {
56
58
 
57
59
  /// @notice Return true if `id` is a local node ID for this contract.
58
60
  function isLocalNode(uint id) internal view returns (bool) {
59
- return isLocalFamily(id, Node) && address(uint160(id)) == address(this);
61
+ return uint32(id >> 224) != Chain && isLocalFamily(id, Node) && address(uint160(id)) == address(this);
60
62
  }
61
63
 
62
64
  /// @notice Assert that `id` is a command ID and return it unchanged.
@@ -128,6 +130,14 @@ library Ids {
128
130
  return id;
129
131
  }
130
132
 
133
+ /// @notice Build the chain node ID for the current chain.
134
+ /// @return Chain node ID with the Chain prefix and `block.chainid` payload.
135
+ function localChain() internal view returns (uint) {
136
+ uint id = block.chainid;
137
+ if (id >> 224 != 0) revert InvalidId();
138
+ return (uint(Chain) << 224) | id;
139
+ }
140
+
131
141
  /// @notice Build a chain-local host ID for `target`.
132
142
  /// @param target Host contract address.
133
143
  /// @return Host node ID on the current chain.
@@ -180,7 +190,7 @@ library Ids {
180
190
  /// @param id Node ID (host, command, or peer).
181
191
  /// @return Contract address in the lower 160 bits of `id`.
182
192
  function nodeAddr(uint id) internal view returns (address) {
183
- if (!isLocalFamily(id, Node)) revert InvalidId();
193
+ if (uint32(id >> 224) == Chain || !isLocalFamily(id, Node)) revert InvalidId();
184
194
  return address(uint160(id));
185
195
  }
186
196
 
package/utils/Layout.sol CHANGED
@@ -6,19 +6,24 @@ pragma solidity ^0.8.33;
6
6
  /// layout of 256-bit IDs used throughout the rootzero protocol.
7
7
  ///
8
8
  /// IDs are structured as:
9
- /// `[uint16 width][uint16 chainId-prefix][uint32 prefix][... payload ...]`
10
- /// where the top bytes carry nested type tags drawn from the constants below.
9
+ /// `[uint32 type][uint32 chainid][192-bit payload]`
10
+ /// where `type` is `[uint8 vm][uint8 width][uint8 category][uint8 subtype]`.
11
11
  library Layout {
12
12
  // -------------------------------------------------------------------------
13
- // Data-width tags (uint16, top 2 bytes of the ID type field)
13
+ // VM and data-width tags (top 2 bytes of the ID type field)
14
14
  // -------------------------------------------------------------------------
15
15
 
16
- /// @dev 32-byte opaque value; no EVM address embedded.
17
- uint16 constant Opaque32 = 0x2000;
16
+ /// @dev EVM-compatible value; payloads are built around 20-byte addresses.
17
+ uint8 constant Evm = 0x01;
18
+ /// @dev 32-byte ID; no paired metadata word is required.
19
+ uint8 constant Width32 = 0x20;
20
+ /// @dev 64-byte ID; a paired metadata word completes the identity.
21
+ uint8 constant Width64 = 0x40;
22
+
18
23
  /// @dev 32-byte EVM-compatible value; lower 20 bytes hold an address.
19
- uint16 constant Evm32 = 0x2001;
24
+ uint16 constant Evm32 = (uint16(Evm) << 8) | uint16(Width32);
20
25
  /// @dev 64-byte EVM-compatible value; used when a paired metadata word completes the identity.
21
- uint16 constant Evm64 = 0x4001;
26
+ uint16 constant Evm64 = (uint16(Evm) << 8) | uint16(Width64);
22
27
 
23
28
  // -------------------------------------------------------------------------
24
29
  // Category tags (uint8, third byte of the ID type field)
@@ -41,30 +46,29 @@ library Layout {
41
46
  uint8 constant Guardian = 0x02;
42
47
  /// @dev User account — chain-agnostic, backed by an EVM address.
43
48
  uint8 constant User = 0x03;
44
- /// @dev Keccak account — opaque 28-byte keccak commitment.
45
- uint8 constant Keccak = 0x04;
46
-
47
49
  // -------------------------------------------------------------------------
48
50
  // Node subtype tags (uint8, fourth byte of the ID type field)
49
51
  // -------------------------------------------------------------------------
50
52
 
53
+ /// @dev Node is a chain/domain identifier.
54
+ uint8 constant Chain = 0x01;
51
55
  /// @dev Node is a host contract.
52
- uint8 constant Host = 0x01;
56
+ uint8 constant Host = 0x02;
53
57
  /// @dev Node is a command contract.
54
- uint8 constant Command = 0x02;
58
+ uint8 constant Command = 0x03;
55
59
  /// @dev Node is a peer contract.
56
- uint8 constant Peer = 0x03;
60
+ uint8 constant Peer = 0x04;
57
61
  /// @dev Node is a query contract.
58
- uint8 constant Query = 0x04;
62
+ uint8 constant Query = 0x05;
59
63
  /// @dev Node is a guardian-only direct action.
60
- uint8 constant Guard = 0x05;
64
+ uint8 constant Guard = 0x06;
61
65
 
62
66
  // -------------------------------------------------------------------------
63
67
  // Asset subtype tags (uint8, fourth byte of the ID type field)
64
68
  // -------------------------------------------------------------------------
65
69
 
66
- /// @dev Native chain value asset (ETH / native token).
67
- uint8 constant Value = 0x01;
70
+ /// @dev Native chain coin/token asset.
71
+ uint8 constant Native = 0x01;
68
72
  /// @dev ERC-20 fungible token; lower 20 bytes of the ID hold the contract address.
69
73
  uint8 constant Erc20 = 0x02;
70
74
  /// @dev ERC-721 non-fungible token; lower 20 bytes of the ID hold the collection address.
package/utils/Utils.sol CHANGED
@@ -4,9 +4,9 @@ pragma solidity ^0.8.33;
4
4
  // Basis-points denominator: 10_000 BPS == 100.00%.
5
5
  uint16 constant MAX_BPS = 10_000;
6
6
 
7
- // Thrown by max* helpers when a value exceeds the target integer width.
7
+ /// @dev Thrown by max* helpers when a value exceeds the target integer width.
8
8
  error ValueOverflow();
9
- // Thrown by `divisible` when `n` is not evenly divisible by `divisor`.
9
+ /// @dev Thrown by `divisible` when `n` is not evenly divisible by `divisor`.
10
10
  error NotDivisible();
11
11
 
12
12
  /// @notice Assert that `value` fits in uint8 and return it unchanged.
@@ -1,54 +0,0 @@
1
- // SPDX-License-Identifier: GPL-3.0-only
2
- pragma solidity ^0.8.33;
3
-
4
- import { CommandContext, CommandBase, Keys } from "./Base.sol";
5
- import { Cursors, Cur, Schemas, Tx } from "../Cursors.sol";
6
- import { Accounts } from "../utils/Accounts.sol";
7
- using Cursors for Cur;
8
-
9
- abstract contract TransferHook {
10
- /// @notice Override to execute a single transfer record from the request pipeline.
11
- /// Called once per PAYOUT block in the request.
12
- /// @param value Decoded transfer record (from, to, asset, meta, amount).
13
- function transfer(Tx memory value) internal virtual;
14
- }
15
-
16
- /// @title Transfer
17
- /// @notice Command that transfers assets from a caller to recipients specified in
18
- /// PAYOUT request blocks. Produces no state output.
19
- /// The virtual `transfer(value)` hook is called once per entry.
20
- abstract contract Transfer is CommandBase, TransferHook {
21
- string private constant NAME = "transfer";
22
-
23
- uint internal immutable transferId = commandId(NAME);
24
-
25
- constructor() {
26
- emit Command(host, transferId, NAME, "1:0:0", Schemas.Payout, Keys.Empty, Keys.Empty, false);
27
- }
28
-
29
- /// @notice Override to customize request parsing or batching for transfers.
30
- /// The default implementation iterates entry blocks and calls `transfer(value)` for each.
31
- /// @param from Source account identifier.
32
- /// @param request Full request bytes.
33
- /// @return Empty bytes (transfers produce no state output).
34
- function transfer(bytes32 from, bytes calldata request) internal virtual returns (bytes memory) {
35
- (Cur memory input, ) = cursor(request, 1);
36
- Tx memory value;
37
- value.from = from;
38
-
39
- while (input.i < input.bound) {
40
- (value.to, value.asset, value.meta, value.amount) = input.unpackPayout();
41
- Accounts.ensure(value.to);
42
- transfer(value);
43
- }
44
-
45
- input.close();
46
- return "";
47
- }
48
-
49
- function transfer(
50
- CommandContext calldata c
51
- ) external onlyCommand returns (bytes memory) {
52
- return transfer(c.account, c.request);
53
- }
54
- }