@rootzero/contracts 0.2.0 → 0.4.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 (161) hide show
  1. package/{contracts/Commands.sol → Commands.sol} +7 -2
  2. package/{contracts/Core.sol → Core.sol} +7 -1
  3. package/{contracts/Blocks.sol → Cursors.sol} +8 -1
  4. package/{contracts/Events.sol → Events.sol} +6 -0
  5. package/README.md +59 -59
  6. package/Utils.sol +18 -0
  7. package/blocks/Cursors.sol +800 -0
  8. package/blocks/Keys.sol +51 -0
  9. package/blocks/Mem.sol +188 -0
  10. package/blocks/Schema.sol +158 -0
  11. package/blocks/Writers.sol +304 -0
  12. package/commands/Base.sol +65 -0
  13. package/commands/Borrow.sol +88 -0
  14. package/commands/Burn.sol +45 -0
  15. package/commands/Create.sol +41 -0
  16. package/commands/Credit.sol +49 -0
  17. package/commands/Debit.sol +58 -0
  18. package/commands/Deposit.sol +57 -0
  19. package/commands/Liquidate.sol +98 -0
  20. package/commands/Liquidity.sol +179 -0
  21. package/commands/Mint.sol +53 -0
  22. package/{contracts/commands → commands}/Pipe.sol +28 -12
  23. package/commands/Provision.sol +84 -0
  24. package/commands/Reclaim.sol +54 -0
  25. package/commands/Redeem.sol +98 -0
  26. package/commands/Remove.sol +41 -0
  27. package/commands/Repay.sol +98 -0
  28. package/commands/Settle.sol +41 -0
  29. package/commands/Stake.sol +129 -0
  30. package/commands/Supply.sol +40 -0
  31. package/commands/Swap.sol +89 -0
  32. package/commands/Transfer.sol +55 -0
  33. package/commands/Unstake.sol +57 -0
  34. package/commands/Withdraw.sol +49 -0
  35. package/commands/admin/Allocate.sol +40 -0
  36. package/commands/admin/AllowAssets.sol +42 -0
  37. package/commands/admin/Authorize.sol +38 -0
  38. package/commands/admin/DenyAssets.sol +42 -0
  39. package/commands/admin/Destroy.sol +38 -0
  40. package/commands/admin/Init.sol +38 -0
  41. package/commands/admin/Relocate.sol +39 -0
  42. package/commands/admin/Unauthorize.sol +38 -0
  43. package/core/Access.sol +79 -0
  44. package/core/Balances.sol +40 -0
  45. package/core/Host.sol +44 -0
  46. package/core/Operation.sol +69 -0
  47. package/core/Validator.sol +46 -0
  48. package/docs/GETTING_STARTED.md +286 -0
  49. package/{contracts/events → events}/Access.sol +7 -0
  50. package/events/Asset.sol +21 -0
  51. package/{contracts/events → events}/Balance.sol +10 -0
  52. package/events/Collateral.sol +24 -0
  53. package/events/Command.sol +24 -0
  54. package/events/Debt.sol +25 -0
  55. package/{contracts/events → events}/Deposit.sol +9 -0
  56. package/events/Emitter.sol +14 -0
  57. package/{contracts/events → events}/Governed.sol +7 -0
  58. package/{contracts/events → events}/HostAnnounced.sol +8 -0
  59. package/{contracts/events → events}/Listing.sol +9 -0
  60. package/{contracts/events → events}/Peer.sol +8 -0
  61. package/{contracts/events → events}/Quote.sol +7 -0
  62. package/{contracts/events → events}/RootZero.sol +5 -0
  63. package/{contracts/events → events}/Withdraw.sol +9 -0
  64. package/interfaces/IHostDiscovery.sol +16 -0
  65. package/package.json +17 -33
  66. package/peer/AllowAssets.sol +44 -0
  67. package/peer/Base.sol +25 -0
  68. package/peer/DenyAssets.sol +44 -0
  69. package/peer/Pull.sol +42 -0
  70. package/peer/Push.sol +42 -0
  71. package/utils/Accounts.sol +90 -0
  72. package/utils/Assets.sol +138 -0
  73. package/utils/ECDSA.sol +58 -0
  74. package/utils/Ids.sol +129 -0
  75. package/utils/Layout.sol +66 -0
  76. package/utils/State.sol +22 -0
  77. package/utils/Utils.sol +194 -0
  78. package/utils/Value.sol +32 -0
  79. package/contracts/Utils.sol +0 -12
  80. package/contracts/blocks/Blocks.sol +0 -818
  81. package/contracts/blocks/Keys.sol +0 -24
  82. package/contracts/blocks/Mem.sol +0 -129
  83. package/contracts/blocks/Schema.sol +0 -105
  84. package/contracts/blocks/Writers.sol +0 -209
  85. package/contracts/combinators/AmountToBalance.sol +0 -25
  86. package/contracts/combinators/AmountToCustody.sol +0 -36
  87. package/contracts/combinators/CustodyToBalance.sol +0 -25
  88. package/contracts/combinators/EachRoute.sol +0 -18
  89. package/contracts/combinators/MapBalance.sol +0 -25
  90. package/contracts/combinators/MapCustody.sol +0 -25
  91. package/contracts/combinators/RouteToBalance.sol +0 -27
  92. package/contracts/commands/Base.sol +0 -40
  93. package/contracts/commands/Borrow.sol +0 -89
  94. package/contracts/commands/Burn.sol +0 -33
  95. package/contracts/commands/Create.sol +0 -32
  96. package/contracts/commands/Credit.sol +0 -36
  97. package/contracts/commands/Debit.sol +0 -46
  98. package/contracts/commands/Deposit.sol +0 -45
  99. package/contracts/commands/Liquidate.sol +0 -101
  100. package/contracts/commands/Liquidity.sol +0 -179
  101. package/contracts/commands/Mint.sol +0 -42
  102. package/contracts/commands/Provision.sol +0 -73
  103. package/contracts/commands/Reclaim.sol +0 -48
  104. package/contracts/commands/Redeem.sol +0 -101
  105. package/contracts/commands/Remove.sol +0 -32
  106. package/contracts/commands/Repay.sol +0 -101
  107. package/contracts/commands/Settle.sol +0 -32
  108. package/contracts/commands/Stake.sol +0 -121
  109. package/contracts/commands/Supply.sol +0 -33
  110. package/contracts/commands/Swap.sol +0 -88
  111. package/contracts/commands/Transfer.sol +0 -44
  112. package/contracts/commands/Unstake.sol +0 -49
  113. package/contracts/commands/Withdraw.sol +0 -37
  114. package/contracts/commands/admin/Allocate.sol +0 -32
  115. package/contracts/commands/admin/AllowAssets.sol +0 -34
  116. package/contracts/commands/admin/Authorize.sol +0 -30
  117. package/contracts/commands/admin/DenyAssets.sol +0 -34
  118. package/contracts/commands/admin/Destroy.sol +0 -27
  119. package/contracts/commands/admin/Init.sol +0 -26
  120. package/contracts/commands/admin/Relocate.sol +0 -30
  121. package/contracts/commands/admin/Unauthorize.sol +0 -30
  122. package/contracts/core/Access.sol +0 -50
  123. package/contracts/core/Balances.sol +0 -23
  124. package/contracts/core/Host.sol +0 -25
  125. package/contracts/core/Operation.sol +0 -32
  126. package/contracts/core/Validator.sol +0 -31
  127. package/contracts/events/Asset.sol +0 -14
  128. package/contracts/events/Collateral.sol +0 -15
  129. package/contracts/events/Command.sol +0 -14
  130. package/contracts/events/Debt.sol +0 -15
  131. package/contracts/events/Emitter.sol +0 -7
  132. package/contracts/interfaces/IHostDiscovery.sol +0 -6
  133. package/contracts/peer/AllowAssets.sol +0 -30
  134. package/contracts/peer/Base.sol +0 -17
  135. package/contracts/peer/DenyAssets.sol +0 -30
  136. package/contracts/peer/Pull.sol +0 -30
  137. package/contracts/peer/Push.sol +0 -30
  138. package/contracts/test/TestBlockHelper.sol +0 -261
  139. package/contracts/test/TestBorrowHost.sol +0 -47
  140. package/contracts/test/TestBurnHost.sol +0 -28
  141. package/contracts/test/TestCreateHost.sol +0 -26
  142. package/contracts/test/TestDiscovery.sol +0 -6
  143. package/contracts/test/TestECDSA.sol +0 -16
  144. package/contracts/test/TestHost.sol +0 -199
  145. package/contracts/test/TestLiquidityHost.sol +0 -145
  146. package/contracts/test/TestMintHost.sol +0 -40
  147. package/contracts/test/TestPeerHost.sol +0 -34
  148. package/contracts/test/TestReclaimHost.sol +0 -48
  149. package/contracts/test/TestRejectEther.sol +0 -8
  150. package/contracts/test/TestRemoveHost.sol +0 -26
  151. package/contracts/test/TestSwapHost.sol +0 -44
  152. package/contracts/test/TestUtils.sol +0 -169
  153. package/contracts/test/TestValidator.sol +0 -10
  154. package/contracts/utils/Accounts.sol +0 -40
  155. package/contracts/utils/Assets.sol +0 -76
  156. package/contracts/utils/Channels.sol +0 -11
  157. package/contracts/utils/ECDSA.sol +0 -36
  158. package/contracts/utils/Ids.sol +0 -75
  159. package/contracts/utils/Layout.sol +0 -22
  160. package/contracts/utils/Utils.sol +0 -126
  161. package/contracts/utils/Value.sol +0 -20
@@ -0,0 +1,304 @@
1
+ // SPDX-License-Identifier: GPL-3.0-only
2
+ pragma solidity ^0.8.33;
3
+
4
+ import { AssetAmount, HostAmount, Tx, Keys, Sizes } from "./Schema.sol";
5
+
6
+ /// @notice Sequential block stream writer backed by a pre-allocated memory buffer.
7
+ struct Writer {
8
+ /// @dev Current write position: number of bytes written so far.
9
+ uint i;
10
+ /// @dev Allocated buffer capacity in bytes.
11
+ uint end;
12
+ /// @dev Destination buffer; final length is set to `i` by `finish`.
13
+ bytes dst;
14
+ }
15
+
16
+ // Fixed-point scaling denominator for output-count allocation.
17
+ // A `scaledRatio` of `ALLOC_SCALE` means 1:1 (one output block per input block).
18
+ // `2 * ALLOC_SCALE` means 2:1; non-integer ratios revert with `BadWriterRatio`.
19
+ uint constant ALLOC_SCALE = 10_000;
20
+
21
+ /// @title Writers
22
+ /// @notice Response block stream builder for the rootzero protocol.
23
+ /// 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.
26
+ library Writers {
27
+ /// @dev `append` or a `write*` function tried to write past the end of `dst`.
28
+ error WriterOverflow();
29
+ /// @dev `finish` called with a writer whose `i` exceeds `dst.length`.
30
+ error IncompleteWriter();
31
+ /// @dev An alloc function received a zero count, or `finish` found no bytes written.
32
+ error EmptyRequest();
33
+ /// @dev Block payload length exceeds `uint32` max; cannot be encoded in the 4-byte header field.
34
+ error BlockLengthOverflow();
35
+ /// @dev `scaledRatio * count` is not evenly divisible by `ALLOC_SCALE`.
36
+ error BadWriterRatio();
37
+
38
+ // -------------------------------------------------------------------------
39
+ // Header encoding
40
+ // -------------------------------------------------------------------------
41
+
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.
58
+ /// @return writer Freshly allocated writer positioned at index 0.
59
+ 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 Append arbitrary bytes to the writer.
64
+ /// @param writer Destination writer; `i` is advanced by `data.length`.
65
+ /// @param data Bytes to append.
66
+ function append(Writer memory writer, bytes memory data) internal pure {
67
+ uint next = writer.i + data.length;
68
+ if (next > writer.dst.length) revert WriterOverflow();
69
+ // Copy `data` into `dst` starting at the current write position.
70
+ assembly ("memory-safe") {
71
+ mcopy(add(add(mload(add(writer, 0x40)), 0x20), mload(writer)), add(data, 0x20), mload(data))
72
+ }
73
+ writer.i = next;
74
+ }
75
+
76
+ /// @notice Allocate a writer sized for exactly `count` BALANCE blocks (1:1 ratio).
77
+ /// @param count Number of balance blocks to allocate space for.
78
+ /// @return writer Allocated writer.
79
+ function allocBalances(uint count) internal pure returns (Writer memory writer) {
80
+ return allocFromScaledCount(count, ALLOC_SCALE, Sizes.Balance);
81
+ }
82
+
83
+ /// @notice Allocate a writer sized for `2 * count` BALANCE blocks (2:1 ratio).
84
+ /// Used when each input produces a paired output (e.g. input balance + output balance).
85
+ /// @param count Number of input blocks; output capacity is doubled.
86
+ /// @return writer Allocated writer.
87
+ function allocPairedBalances(uint count) internal pure returns (Writer memory writer) {
88
+ return allocFromScaledCount(count, ALLOC_SCALE * 2, Sizes.Balance);
89
+ }
90
+
91
+ /// @notice Allocate a writer for BALANCE blocks with a custom output-to-input ratio.
92
+ /// @param count Number of input blocks.
93
+ /// @param scaledRatio Output count multiplier expressed in `ALLOC_SCALE` units
94
+ /// (e.g. `ALLOC_SCALE` = 1:1, `2 * ALLOC_SCALE` = 2:1).
95
+ /// @return writer Allocated writer.
96
+ function allocScaledBalances(uint count, uint scaledRatio) internal pure returns (Writer memory writer) {
97
+ return allocFromScaledCount(count, scaledRatio, Sizes.Balance);
98
+ }
99
+
100
+ /// @notice Allocate a writer sized for exactly `count` TRANSACTION blocks (1:1 ratio).
101
+ /// @param count Number of transaction blocks to allocate space for.
102
+ /// @return writer Allocated writer.
103
+ function allocTxs(uint count) internal pure returns (Writer memory writer) {
104
+ return allocFromScaledCount(count, ALLOC_SCALE, Sizes.Transaction);
105
+ }
106
+
107
+ /// @notice Allocate a writer sized for exactly `count` CUSTODY blocks (1:1 ratio).
108
+ /// @param count Number of custody blocks to allocate space for.
109
+ /// @return writer Allocated writer.
110
+ function allocCustodies(uint count) internal pure returns (Writer memory writer) {
111
+ return allocFromScaledCount(count, ALLOC_SCALE, Sizes.Custody);
112
+ }
113
+
114
+ /// @notice Allocate a writer for CUSTODY blocks with a custom output-to-input ratio.
115
+ /// @param count Number of input blocks.
116
+ /// @param scaledRatio Output count multiplier in `ALLOC_SCALE` units.
117
+ /// @return writer Allocated writer.
118
+ function allocScaledCustodies(uint count, uint scaledRatio) internal pure returns (Writer memory writer) {
119
+ return allocFromScaledCount(count, scaledRatio, Sizes.Custody);
120
+ }
121
+
122
+ /// @notice Core allocation routine used by all typed `alloc*` helpers.
123
+ /// Computes `(count * scaledRatio / ALLOC_SCALE) * blockLen` and allocates that many bytes.
124
+ /// @param count Number of input blocks.
125
+ /// @param scaledRatio Output-to-input ratio in `ALLOC_SCALE` units.
126
+ /// @param blockLen Byte size of each output block (including 8-byte header).
127
+ /// @return writer Allocated writer.
128
+ function allocFromScaledCount(
129
+ uint count,
130
+ uint scaledRatio,
131
+ uint blockLen
132
+ ) internal pure returns (Writer memory writer) {
133
+ if (count == 0) revert EmptyRequest();
134
+ uint scaledCount = count * scaledRatio;
135
+ if (scaledCount % ALLOC_SCALE != 0) revert BadWriterRatio();
136
+ uint len = (scaledCount / ALLOC_SCALE) * blockLen;
137
+ writer = Writer({i: 0, end: len, dst: new bytes(len)});
138
+ }
139
+
140
+ // -------------------------------------------------------------------------
141
+ // Low-level write helpers (direct index into a bytes buffer)
142
+ // -------------------------------------------------------------------------
143
+
144
+ /// @notice Write a BALANCE block directly into `dst` at byte offset `i`.
145
+ /// @param dst Destination buffer; must have at least `i + Sizes.Balance` bytes.
146
+ /// @param i Write offset within `dst`.
147
+ /// @param value Balance fields to encode.
148
+ /// @return next Byte offset immediately after the written block.
149
+ function writeBalanceBlock(bytes memory dst, uint i, AssetAmount memory value) internal pure returns (uint next) {
150
+ next = i + Sizes.Balance;
151
+ if (next > dst.length) revert WriterOverflow();
152
+ uint header = toBlockHeader(Keys.Balance, 96);
153
+ // Write 8-byte header then three 32-byte payload words.
154
+ assembly ("memory-safe") {
155
+ let p := add(add(dst, 0x20), i)
156
+ mstore(p, header)
157
+ mstore(add(p, 0x08), mload(value))
158
+ mstore(add(p, 0x28), mload(add(value, 0x20)))
159
+ mstore(add(p, 0x48), mload(add(value, 0x40)))
160
+ }
161
+ }
162
+
163
+ /// @notice Append a BALANCE block using separate field values.
164
+ /// @param writer Destination writer; `i` is advanced by `Sizes.Balance`.
165
+ /// @param asset Asset identifier.
166
+ /// @param meta Asset metadata slot.
167
+ /// @param amount Token amount.
168
+ function appendBalance(Writer memory writer, bytes32 asset, bytes32 meta, uint amount) internal pure {
169
+ appendBalance(writer, AssetAmount(asset, meta, amount));
170
+ }
171
+
172
+ /// @notice Append a BALANCE block from a struct.
173
+ /// @param writer Destination writer; `i` is advanced by `Sizes.Balance`.
174
+ /// @param value Balance fields to encode.
175
+ function appendBalance(Writer memory writer, AssetAmount memory value) internal pure {
176
+ writer.i = writeBalanceBlock(writer.dst, writer.i, value);
177
+ }
178
+
179
+ /// @notice Append a BALANCE block only if `amount > 0`; silently skips zero amounts.
180
+ /// @param writer Destination writer.
181
+ /// @param asset Asset identifier.
182
+ /// @param meta Asset metadata slot.
183
+ /// @param amount Token amount; block is not written if this is zero.
184
+ function appendNonZeroBalance(Writer memory writer, bytes32 asset, bytes32 meta, uint amount) internal pure {
185
+ if (amount > 0) appendBalance(writer, asset, meta, amount);
186
+ }
187
+
188
+ /// @notice Append a BALANCE block from a struct only if the amount is non-zero.
189
+ /// @param writer Destination writer.
190
+ /// @param value Balance fields; block is not written if `value.amount == 0`.
191
+ function appendNonZeroBalance(Writer memory writer, AssetAmount memory value) internal pure {
192
+ if (value.amount > 0) appendBalance(writer, value);
193
+ }
194
+
195
+ /// @notice Write a BOUNTY block directly into `dst` at byte offset `i`.
196
+ /// @param dst Destination buffer; must have at least `i + Sizes.Bounty` bytes.
197
+ /// @param i Write offset within `dst`.
198
+ /// @param amount Relayer reward amount.
199
+ /// @param relayer Relayer account identifier.
200
+ /// @return next Byte offset immediately after the written block.
201
+ function writeBountyBlock(bytes memory dst, uint i, uint amount, bytes32 relayer) internal pure returns (uint next) {
202
+ next = i + Sizes.Bounty;
203
+ if (next > dst.length) revert WriterOverflow();
204
+ uint header = toBlockHeader(Keys.Bounty, 64);
205
+ // Write 8-byte header then amount and relayer.
206
+ assembly ("memory-safe") {
207
+ let p := add(add(dst, 0x20), i)
208
+ mstore(p, header)
209
+ mstore(add(p, 0x08), amount)
210
+ mstore(add(p, 0x28), relayer)
211
+ }
212
+ }
213
+
214
+ /// @notice Append a BOUNTY block to the writer.
215
+ /// @param writer Destination writer; `i` is advanced by `Sizes.Bounty`.
216
+ /// @param amount Relayer reward amount.
217
+ /// @param relayer Relayer account identifier.
218
+ function appendBounty(Writer memory writer, uint amount, bytes32 relayer) internal pure {
219
+ writer.i = writeBountyBlock(writer.dst, writer.i, amount, relayer);
220
+ }
221
+
222
+ /// @notice Write a CUSTODY block directly into `dst` at byte offset `i`.
223
+ /// @param dst Destination buffer; must have at least `i + Sizes.Custody` bytes.
224
+ /// @param i Write offset within `dst`.
225
+ /// @param value Custody fields to encode.
226
+ /// @return next Byte offset immediately after the written block.
227
+ function writeCustodyBlock(bytes memory dst, uint i, HostAmount memory value) internal pure returns (uint next) {
228
+ next = i + Sizes.Custody;
229
+ if (next > dst.length) revert WriterOverflow();
230
+ uint header = toBlockHeader(Keys.Custody, 128);
231
+ // Write 8-byte header then four 32-byte payload words.
232
+ assembly ("memory-safe") {
233
+ let p := add(add(dst, 0x20), i)
234
+ mstore(p, header)
235
+ mstore(add(p, 0x08), mload(value))
236
+ mstore(add(p, 0x28), mload(add(value, 0x20)))
237
+ mstore(add(p, 0x48), mload(add(value, 0x40)))
238
+ mstore(add(p, 0x68), mload(add(value, 0x60)))
239
+ }
240
+ }
241
+
242
+ /// @notice Append a CUSTODY block using separate field values.
243
+ /// @param writer Destination writer; `i` is advanced by `Sizes.Custody`.
244
+ /// @param host Host node ID.
245
+ /// @param asset Asset identifier.
246
+ /// @param meta Asset metadata slot.
247
+ /// @param amount Token amount.
248
+ function appendCustody(Writer memory writer, uint host, bytes32 asset, bytes32 meta, uint amount) internal pure {
249
+ appendCustody(writer, HostAmount(host, asset, meta, amount));
250
+ }
251
+
252
+ /// @notice Append a CUSTODY block from a struct.
253
+ /// @param writer Destination writer; `i` is advanced by `Sizes.Custody`.
254
+ /// @param value Custody fields to encode.
255
+ function appendCustody(Writer memory writer, HostAmount memory value) internal pure {
256
+ writer.i = writeCustodyBlock(writer.dst, writer.i, value);
257
+ }
258
+
259
+ /// @notice Write a TRANSACTION block directly into `dst` at byte offset `i`.
260
+ /// @param dst Destination buffer; must have at least `i + Sizes.Transaction` bytes.
261
+ /// @param i Write offset within `dst`.
262
+ /// @param value Transaction fields to encode.
263
+ /// @return next Byte offset immediately after the written block.
264
+ function writeTxBlock(bytes memory dst, uint i, Tx memory value) internal pure returns (uint next) {
265
+ next = i + Sizes.Transaction;
266
+ if (next > dst.length) revert WriterOverflow();
267
+ uint header = toBlockHeader(Keys.Transaction, 160);
268
+ // Write 8-byte header then five 32-byte payload words.
269
+ assembly ("memory-safe") {
270
+ let p := add(add(dst, 0x20), i)
271
+ mstore(p, header)
272
+ mstore(add(p, 0x08), mload(value))
273
+ mstore(add(p, 0x28), mload(add(value, 0x20)))
274
+ mstore(add(p, 0x48), mload(add(value, 0x40)))
275
+ mstore(add(p, 0x68), mload(add(value, 0x60)))
276
+ mstore(add(p, 0x88), mload(add(value, 0x80)))
277
+ }
278
+ }
279
+
280
+ /// @notice Append a TRANSACTION block from a struct.
281
+ /// @param writer Destination writer; `i` is advanced by `Sizes.Transaction`.
282
+ /// @param value Transaction fields to encode.
283
+ function appendTx(Writer memory writer, Tx memory value) internal pure {
284
+ writer.i = writeTxBlock(writer.dst, writer.i, value);
285
+ }
286
+
287
+ // -------------------------------------------------------------------------
288
+ // Finalisation
289
+ // -------------------------------------------------------------------------
290
+
291
+ /// @notice Trim `dst` to the number of bytes actually written and return it.
292
+ /// Sets the `bytes` length slot in memory to `writer.i` without copying.
293
+ /// @param writer Completed writer.
294
+ /// @return out The written block stream; length equals `writer.i`.
295
+ function finish(Writer memory writer) internal pure returns (bytes memory out) {
296
+ if (writer.i == 0) revert EmptyRequest();
297
+ if (writer.i > writer.dst.length) revert IncompleteWriter();
298
+ out = writer.dst;
299
+ // Overwrite the memory length word of `out` with the actual written length.
300
+ assembly ("memory-safe") {
301
+ mstore(out, mload(writer))
302
+ }
303
+ }
304
+ }
@@ -0,0 +1,65 @@
1
+ // SPDX-License-Identifier: GPL-3.0-only
2
+ pragma solidity ^0.8.33;
3
+
4
+ import {OperationBase} from "../core/Operation.sol";
5
+ import {Cur} from "../Cursors.sol";
6
+ import {CommandEvent} from "../events/Command.sol";
7
+ import {State} from "../utils/State.sol";
8
+ import {Ids, Selectors} from "../utils/Ids.sol";
9
+
10
+ /// @notice Execution context passed to every command invocation.
11
+ struct CommandContext {
12
+ /// @dev Destination command node ID; zero means "any command on this host".
13
+ uint target;
14
+ /// @dev Caller's account identifier.
15
+ bytes32 account;
16
+ /// @dev Current state block stream (previous command output or initial state).
17
+ bytes state;
18
+ /// @dev Input block stream for this invocation.
19
+ bytes request;
20
+ }
21
+
22
+ /// @title CommandBase
23
+ /// @notice Abstract base for all rootzero command contracts.
24
+ /// Provides access control modifiers, event emission, and the `commandId`
25
+ /// helper used to derive stable identifiers for named commands.
26
+ abstract contract CommandBase is OperationBase, CommandEvent {
27
+ /// @dev Thrown when `onlyActive` finds that `deadline` has already passed.
28
+ error Expired();
29
+ /// @dev Thrown when `onlyAdmin` finds that `account` is not the admin account.
30
+ error NotAdmin();
31
+ /// @dev Thrown when `onlyCommand` finds that `target` does not match this command's ID.
32
+ error UnexpectedEndpoint();
33
+
34
+ /// @dev Restrict execution to calls where `account` is the host's admin account.
35
+ modifier onlyAdmin(bytes32 account) {
36
+ if (account != adminAccount) revert NotAdmin();
37
+ _;
38
+ }
39
+
40
+ /// @dev Restrict execution to trusted callers targeting this specific command.
41
+ /// A zero `target` is treated as a wildcard and matches any command.
42
+ /// @param cid This command's node ID (from `commandId`).
43
+ /// @param target Requested destination from the `CommandContext`.
44
+ modifier onlyCommand(uint cid, uint target) {
45
+ if (target != 0 && target != cid) revert UnexpectedEndpoint();
46
+ enforceCaller(msg.sender);
47
+ _;
48
+ }
49
+
50
+ /// @dev Restrict execution to invocations where `deadline` is in the future.
51
+ /// @param deadline Unix timestamp after which the invocation is considered expired.
52
+ modifier onlyActive(uint deadline) {
53
+ if (deadline < block.timestamp) revert Expired();
54
+ _;
55
+ }
56
+
57
+ /// @notice Derive the deterministic node ID for a named command on this contract.
58
+ /// The ID encodes the ABI selector of `name((uint256,bytes32,bytes,bytes))` and
59
+ /// `address(this)`, making it unique per (function name, contract address) pair.
60
+ /// @param name Command function name (without argument list).
61
+ /// @return Command node ID.
62
+ function commandId(string memory name) internal view returns (uint) {
63
+ return Ids.toCommand(Selectors.command(name), address(this));
64
+ }
65
+ }
@@ -0,0 +1,88 @@
1
+ // SPDX-License-Identifier: GPL-3.0-only
2
+ pragma solidity ^0.8.33;
3
+
4
+ import { CommandContext, CommandBase, State } from "./Base.sol";
5
+ import { AssetAmount, HostAmount, Cur, Cursors, Writer, Writers } from "../Cursors.sol";
6
+
7
+ string constant BABTB = "borrowAgainstBalanceToBalance";
8
+ string constant BACTB = "borrowAgainstCustodyToBalance";
9
+
10
+ using Cursors for Cur;
11
+ using Writers for Writer;
12
+
13
+ /// @title BorrowAgainstCustodyToBalance
14
+ /// @notice Command that issues loans against CUSTODY state positions, emitting BALANCE outputs.
15
+ abstract contract BorrowAgainstCustodyToBalance is CommandBase {
16
+ uint internal immutable borrowAgainstCustodyToBalanceId = commandId(BACTB);
17
+
18
+ constructor(string memory input) {
19
+ emit Command(host, BACTB, input, borrowAgainstCustodyToBalanceId, State.Custodies, State.Balances);
20
+ }
21
+
22
+ /// @dev Override to borrow against a custody position.
23
+ /// `request` is the live auxiliary request cursor for this command;
24
+ /// implementations validate and unpack it as needed.
25
+ function borrowAgainstCustodyToBalance(
26
+ bytes32 account,
27
+ HostAmount memory custody,
28
+ Cur memory request
29
+ ) internal virtual returns (AssetAmount memory);
30
+
31
+ function borrowAgainstCustodyToBalance(
32
+ CommandContext calldata c
33
+ ) external payable onlyCommand(borrowAgainstCustodyToBalanceId, c.target) returns (bytes memory) {
34
+ (Cur memory state, uint stateCount, ) = cursor(c.state, 1);
35
+ Cur memory request = cursor(c.request);
36
+ Writer memory writer = Writers.allocBalances(stateCount);
37
+
38
+ while (state.i < state.bound) {
39
+ HostAmount memory custody = state.unpackCustodyValue();
40
+ AssetAmount memory out = borrowAgainstCustodyToBalance(c.account, custody, request);
41
+ writer.appendNonZeroBalance(out);
42
+ }
43
+
44
+ return state.complete(writer);
45
+ }
46
+ }
47
+
48
+ /// @title BorrowAgainstBalanceToBalance
49
+ /// @notice Command that issues loans against BALANCE state positions, emitting BALANCE outputs.
50
+ abstract contract BorrowAgainstBalanceToBalance is CommandBase {
51
+ uint internal immutable borrowAgainstBalanceToBalanceId = commandId(BABTB);
52
+
53
+ constructor(string memory input) {
54
+ emit Command(host, BABTB, input, borrowAgainstBalanceToBalanceId, State.Balances, State.Balances);
55
+ }
56
+
57
+ /// @dev Override to borrow against a balance position.
58
+ /// `request` is the live auxiliary request cursor for this command;
59
+ /// implementations validate and unpack it as needed.
60
+ function borrowAgainstBalanceToBalance(
61
+ bytes32 account,
62
+ AssetAmount memory balance,
63
+ Cur memory request
64
+ ) internal virtual returns (AssetAmount memory);
65
+
66
+ function borrowAgainstBalanceToBalance(
67
+ CommandContext calldata c
68
+ ) external payable onlyCommand(borrowAgainstBalanceToBalanceId, c.target) returns (bytes memory) {
69
+ (Cur memory state, uint stateCount, ) = cursor(c.state, 1);
70
+ Cur memory request = cursor(c.request);
71
+ Writer memory writer = Writers.allocBalances(stateCount);
72
+
73
+ while (state.i < state.bound) {
74
+ AssetAmount memory balance = state.unpackBalanceValue();
75
+ AssetAmount memory out = borrowAgainstBalanceToBalance(c.account, balance, request);
76
+ writer.appendNonZeroBalance(out);
77
+ }
78
+
79
+ return state.complete(writer);
80
+ }
81
+ }
82
+
83
+
84
+
85
+
86
+
87
+
88
+
@@ -0,0 +1,45 @@
1
+ // SPDX-License-Identifier: GPL-3.0-only
2
+ pragma solidity ^0.8.33;
3
+
4
+ import { CommandBase, CommandContext, State } from "./Base.sol";
5
+ import { Cursors, Cur } from "../Cursors.sol";
6
+ using Cursors for Cur;
7
+
8
+ string constant NAME = "burn";
9
+
10
+ /// @title Burn
11
+ /// @notice Command that irreversibly destroys each BALANCE state block via a virtual hook.
12
+ /// Produces no output state.
13
+ abstract contract Burn is CommandBase {
14
+ uint internal immutable burnId = commandId(NAME);
15
+
16
+ constructor() {
17
+ emit Command(host, NAME, "", burnId, State.Balances, State.Empty);
18
+ }
19
+
20
+ /// @notice Override to burn or consume the provided balance amount.
21
+ /// Called once per BALANCE block in state.
22
+ /// @param account Caller's account identifier.
23
+ /// @param asset Asset identifier.
24
+ /// @param meta Asset metadata slot.
25
+ /// @param amount Amount to burn.
26
+ /// @return Amount actually burned (may differ from `amount` for partial burns).
27
+ function burn(bytes32 account, bytes32 asset, bytes32 meta, uint amount) internal virtual returns (uint);
28
+
29
+ function burn(CommandContext calldata c) external payable onlyCommand(burnId, c.target) returns (bytes memory) {
30
+ (Cur memory state, , ) = cursor(c.state, 1);
31
+
32
+ while (state.i < state.bound) {
33
+ (bytes32 asset, bytes32 meta, uint amount) = state.unpackBalance();
34
+ burn(c.account, asset, meta, amount);
35
+ }
36
+
37
+ state.complete();
38
+ return "";
39
+ }
40
+ }
41
+
42
+
43
+
44
+
45
+
@@ -0,0 +1,41 @@
1
+ // SPDX-License-Identifier: GPL-3.0-only
2
+ pragma solidity ^0.8.33;
3
+
4
+ import { CommandBase, CommandContext, State } from "./Base.sol";
5
+ import { Cursors, Cur } from "../Cursors.sol";
6
+
7
+ string constant NAME = "create";
8
+
9
+ using Cursors for Cur;
10
+
11
+ /// @title Create
12
+ /// @notice Generic command that creates or initializes objects via a virtual hook.
13
+ /// The request schema is constructor-defined; `create` is called once per top-level group.
14
+ /// Produces no output state.
15
+ abstract contract Create is CommandBase {
16
+ uint internal immutable createId = commandId(NAME);
17
+
18
+ constructor(string memory input) {
19
+ emit Command(host, NAME, input, createId, State.Empty, State.Empty);
20
+ }
21
+
22
+ /// @dev Override to create or initialize an object described by `input`.
23
+ /// Called once per top-level request item.
24
+ function create(bytes32 account, Cur memory input) internal virtual;
25
+
26
+ function create(CommandContext calldata c) external payable onlyCommand(createId, c.target) returns (bytes memory) {
27
+ (Cur memory request, , ) = cursor(c.request, 1);
28
+
29
+ while (request.i < request.bound) {
30
+ create(c.account, request);
31
+ }
32
+
33
+ request.complete();
34
+ return "";
35
+ }
36
+ }
37
+
38
+
39
+
40
+
41
+
@@ -0,0 +1,49 @@
1
+ // SPDX-License-Identifier: GPL-3.0-only
2
+ pragma solidity ^0.8.33;
3
+
4
+ import { CommandBase, CommandContext, State } from "./Base.sol";
5
+ import { Cursors, Cur, Schemas } from "../Cursors.sol";
6
+ string constant NAME = "creditAccount";
7
+
8
+ using Cursors for Cur;
9
+
10
+ /// @title CreditAccount
11
+ /// @notice Command that delivers BALANCE state blocks to an account via a virtual hook.
12
+ /// Use for internally recording credits that have already been settled externally.
13
+ /// An optional RECIPIENT block in the request overrides the default `c.account` destination.
14
+ abstract contract CreditAccount is CommandBase {
15
+ uint internal immutable creditAccountId = commandId(NAME);
16
+
17
+ constructor() {
18
+ emit Command(host, NAME, Schemas.Recipient, creditAccountId, State.Balances, State.Empty);
19
+ }
20
+
21
+ /// @notice Override to credit externally managed funds to `account`.
22
+ /// Called once per BALANCE block in state.
23
+ /// @param account Recipient account identifier.
24
+ /// @param asset Asset identifier.
25
+ /// @param meta Asset metadata slot.
26
+ /// @param amount Amount to credit.
27
+ function creditAccount(bytes32 account, bytes32 asset, bytes32 meta, uint amount) internal virtual;
28
+
29
+ function creditAccount(
30
+ CommandContext calldata c
31
+ ) external payable onlyCommand(creditAccountId, c.target) returns (bytes memory) {
32
+ (Cur memory state, , ) = cursor(c.state, 1);
33
+ Cur memory request = cursor(c.request);
34
+ bytes32 to = request.recipientAfter(c.account);
35
+
36
+ while (state.i < state.bound) {
37
+ (bytes32 asset, bytes32 meta, uint amount) = state.unpackBalance();
38
+ creditAccount(to, asset, meta, amount);
39
+ }
40
+
41
+ state.complete();
42
+ return "";
43
+ }
44
+ }
45
+
46
+
47
+
48
+
49
+
@@ -0,0 +1,58 @@
1
+ // SPDX-License-Identifier: GPL-3.0-only
2
+ pragma solidity ^0.8.33;
3
+
4
+ import { CommandContext, CommandBase, State } from "./Base.sol";
5
+ import { Cursors, Cur, Schemas, Writer, Writers } from "../Cursors.sol";
6
+
7
+ string constant NAME = "debitAccount";
8
+
9
+ using Cursors for Cur;
10
+ using Writers for Writer;
11
+
12
+ /// @title DebitAccount
13
+ /// @notice Command that deducts AMOUNT blocks from an account and emits matching BALANCE state.
14
+ /// Use for internally recording debits. The virtual `debitAccount` hook is called once per
15
+ /// AMOUNT block; the default batch implementation handles the full request loop.
16
+ abstract contract DebitAccount is CommandBase {
17
+ uint internal immutable debitAccountId = commandId(NAME);
18
+
19
+ constructor() {
20
+ emit Command(host, NAME, Schemas.Amount, debitAccountId, State.Empty, State.Balances);
21
+ }
22
+
23
+ /// @notice Override to debit externally managed funds from `account`.
24
+ /// Called once per AMOUNT block before a matching BALANCE is emitted.
25
+ /// @param account Source account identifier.
26
+ /// @param asset Asset identifier.
27
+ /// @param meta Asset metadata slot.
28
+ /// @param amount Amount to debit.
29
+ function debitAccount(bytes32 account, bytes32 asset, bytes32 meta, uint amount) internal virtual;
30
+
31
+ /// @notice Override to customize request parsing or batching for debits.
32
+ /// The default implementation iterates AMOUNT blocks, calls
33
+ /// `debitAccount`, and emits matching BALANCE blocks.
34
+ function debitAccount(bytes32 account, bytes calldata request) internal virtual returns (bytes memory) {
35
+ (Cur memory input, uint count, ) = cursor(request, 1);
36
+ Writer memory writer = Writers.allocBalances(count);
37
+
38
+ while (input.i < input.bound) {
39
+ (bytes32 asset, bytes32 meta, uint amount) = input.unpackAmount();
40
+ debitAccount(account, asset, meta, amount);
41
+ writer.appendBalance(asset, meta, amount);
42
+ }
43
+
44
+ return input.complete(writer);
45
+ }
46
+
47
+ function debitAccount(
48
+ CommandContext calldata c
49
+ ) external payable onlyCommand(debitAccountId, c.target) returns (bytes memory) {
50
+ return debitAccount(c.account, c.request);
51
+ }
52
+ }
53
+
54
+
55
+
56
+
57
+
58
+