@rootzero/contracts 1.16.0 → 1.18.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.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,63 @@
3
3
  Until the protocol reaches integration-stable status, minor versions may include
4
4
  breaking API changes. Breaking changes are called out explicitly.
5
5
 
6
+ ## 1.18.0
7
+
8
+ ### Added
9
+
10
+ - Added the non-funded `repay` and funded `repayPayable` commands. Both consume
11
+ `Position` state, settle only its liability side, and return the released
12
+ asset side as `Balance` state.
13
+ - Added the reusable non-funded `RepayHook` settlement primitive and the
14
+ execution-funded `RepayPayableHook`.
15
+ - Added the opt-in `RevokeAsset` guardian endpoint, which accepts `Asset`
16
+ entries and denies each asset through the existing `DenyAssetsHook`.
17
+
18
+ ### Changed
19
+
20
+ - `Settlement.settle` now delegates its liability leg to the virtual `repay`
21
+ primitive. The default behavior remains a nonzero `debitAccount` call, while
22
+ derived settlement implementations can customize repayment in one place.
23
+ - Exported the repayment commands and hooks and the asset-revocation guard from
24
+ their corresponding public barrels.
25
+
26
+ ### Upgrade Compatibility
27
+
28
+ - Hosts inheriting `Settlement` retain the previous default settlement
29
+ behavior. Hosts that already declare an internal
30
+ `repay(bytes32,bytes32,uint)` function may need to mark it as an override or
31
+ rename it when upgrading.
32
+
33
+ ## 1.17.0
34
+
35
+ ### Breaking Changes
36
+
37
+ - `executeDebitAccount`, `executeCreditAccount`, and `executeSettle` now require
38
+ the pipeline step's `uint128 value` argument and reject nonzero value inside
39
+ the internal adapter.
40
+ - `AllowanceHook` implementations must treat an amount of zero as revocation.
41
+
42
+ ### Added
43
+
44
+ - Added the `HostAsset` structural block and complete codec support for
45
+ host-scoped asset references.
46
+ - Added the opt-in `RevokeAllowance` guardian endpoint, which accepts
47
+ `HostAsset` entries and revokes each allowance through `AllowanceHook`.
48
+ - Added the funded `settlePayable` command and `SettlePayableHook` for settlement
49
+ implementations that require access to the command's native-value budget.
50
+
51
+ ### Changed
52
+
53
+ - Completed the endpoint barrel with `CommandBase` and exported the new command,
54
+ guard, hook, internal adapter, and structural type surfaces from their
55
+ corresponding package barrels.
56
+
57
+ ### Upgrade Compatibility
58
+
59
+ - Pipeline hosts using the internal debit, credit, or settle adapters must pass
60
+ each step's assigned value into the adapter. Existing non-funded steps should
61
+ continue to pass zero.
62
+
6
63
  ## 1.16.0
7
64
 
8
65
  ### Breaking Changes
@@ -54,6 +111,8 @@ breaking API changes. Breaking changes are called out explicitly.
54
111
  - Pipeline transaction streams are posted before the next step, and position
55
112
  state is settled separately through the scalar asset, amount, liability, and
56
113
  debt hook.
114
+ - Completed the public barrel exports for internal command adapters,
115
+ input-only endpoint bases, access errors, and low-level utility helpers.
57
116
 
58
117
  ### Upgrade Compatibility
59
118
 
package/Codec.sol CHANGED
@@ -4,7 +4,7 @@ pragma solidity ^0.8.33;
4
4
  // Aggregator: re-exports the complete block encoding and decoding surface.
5
5
  // Import this file for low-level codec extensions and direct stream processing.
6
6
 
7
- import { AssetAmount, AccountAsset, AccountAmount, HostAmount, HostAccountAsset, HostAccountAmount, Position, Tx } from "./core/Types.sol";
7
+ import { AssetAmount, AccountAsset, HostAsset, AccountAmount, HostAmount, HostAccountAsset, HostAccountAmount, Position, Tx } from "./core/Types.sol";
8
8
  import { Keys } from "./codec/Keys.sol";
9
9
  import { Sizes, Specs } from "./codec/Specs.sol";
10
10
  import { Descriptors } from "./codec/Descriptors.sol";
package/Commands.sol CHANGED
@@ -11,4 +11,4 @@ import {Blocks} from "./codec/Blocks.sol";
11
11
  import {Sizes, Specs} from "./codec/Specs.sol";
12
12
  import {Decoders} from "./codec/Decoders.sol";
13
13
  import {Cursors, Cur} from "./utils/Cursors.sol";
14
- import {AssetAmount, AccountAsset, AccountAmount, HostAmount, HostAccountAsset, HostAccountAmount, Position, Tx} from "./core/Types.sol";
14
+ import {AssetAmount, AccountAsset, HostAsset, AccountAmount, HostAmount, HostAccountAsset, HostAccountAmount, Position, Tx} from "./core/Types.sol";
package/Core.sol CHANGED
@@ -7,18 +7,18 @@ pragma solidity ^0.8.33;
7
7
  import { Action } from "./annotations/Action.sol";
8
8
  import { Label } from "./annotations/Label.sol";
9
9
  import { Schema } from "./annotations/Schema.sol";
10
- import { AdminAccess, CallerAccess, CommanderAccess, GuardianAccess, NodeAccess, TrustAccess } from "./core/Access.sol";
10
+ import { AccessDenied, AdminAccess, CallerAccess, CommanderAccess, CommanderNotAllowed, GuardianAccess, NodeAccess, TrustAccess } from "./core/Access.sol";
11
11
  import { Balances, InsufficientFunds } from "./core/Balances.sol";
12
12
  import { Escrows, InsufficientEscrow } from "./core/Escrows.sol";
13
13
  import { NativeAsset, Runtime } from "./core/Runtime.sol";
14
14
  import { Admins, CommandHost, Guardians, Host, HostIntroduction, IHostIntroduction } from "./core/Host.sol";
15
15
  import { CommandCalls, FailedCall, NodeCalls, PortCalls, RawNodeCalls } from "./core/Calls.sol";
16
- import { EndpointBase } from "./core/Endpoint.sol";
16
+ import { EndpointBase, InputEndpointBase } from "./core/Endpoint.sol";
17
17
  import { Pipeline } from "./core/Pipeline.sol";
18
18
  import { Budget, Budgets } from "./execution/Budget.sol";
19
- import { CreditAccountHook, DebitAccountHook, PostHook, SettleHook, Settlement } from "./core/Settlement.sol";
19
+ import { CreditAccountHook, DebitAccountHook, PostHook, RepayHook, SettleHook, Settlement } from "./core/Settlement.sol";
20
20
  import { Portal } from "./core/Portal.sol";
21
- import { AssetAmount, AccountAsset, AccountAmount, HostAmount, HostAccountAsset, HostAccountAmount, Position, Tx } from "./core/Types.sol";
21
+ import { AssetAmount, AccountAsset, HostAsset, AccountAmount, HostAmount, HostAccountAsset, HostAccountAmount, Position, Tx } from "./core/Types.sol";
22
22
  import { Validator } from "./core/Validator.sol";
23
23
 
24
24
 
package/Endpoints.sol CHANGED
@@ -5,19 +5,26 @@ pragma solidity ^0.8.33;
5
5
  // Import this file to inherit from the full rootzero callable host surface without managing individual paths.
6
6
 
7
7
  // Shared endpoint hooks
8
- import { CreditAccountHook, DebitAccountHook, PostHook, SettleHook } from "./core/Settlement.sol";
8
+ import { CreditAccountHook, DebitAccountHook, PostHook, RepayHook, SettleHook } from "./core/Settlement.sol";
9
9
 
10
10
  // Commands
11
+ import { CommandBase } from "./commands/Base.sol";
11
12
  import { Allocate, AllocateHook } from "./commands/Allocate.sol";
12
13
  import { Burn, BurnHook } from "./commands/Burn.sol";
13
- import { CreditAccount } from "./commands/Credit.sol";
14
- import { DebitAccount } from "./commands/Debit.sol";
14
+ import { CreditAccount, InternalCreditAccount } from "./commands/Credit.sol";
15
+ import { DebitAccount, InternalDebitAccount } from "./commands/Debit.sol";
15
16
  import { Deposit, DepositHook, DepositPayable, DepositPayableHook } from "./commands/Deposit.sol";
16
17
  import { Payout, PayoutHook } from "./commands/Payout.sol";
17
18
  import { Provision, ProvisionHook, ProvisionPayable, ProvisionPayableHook } from "./commands/Provision.sol";
18
19
  import { RecoverPayable, RecoverPayableHook } from "./commands/Recover.sol";
20
+ import { Repay, RepayPayable, RepayPayableHook } from "./commands/Repay.sol";
19
21
  import { RelayPayable, RelayBalancePayable, RelayPayableHook } from "./commands/Relay.sol";
20
- import { Settle } from "./commands/Settle.sol";
22
+ import {
23
+ InternalSettle,
24
+ Settle,
25
+ SettlePayable,
26
+ SettlePayableHook
27
+ } from "./commands/Settle.sol";
21
28
  import { Withdraw, WithdrawHook } from "./commands/Withdraw.sol";
22
29
 
23
30
  // Admin commands
@@ -46,7 +53,7 @@ import { PortPost } from "./ports/Post.sol";
46
53
 
47
54
  // Guard endpoints
48
55
  import { GuardBase } from "./guards/Base.sol";
49
- import { Revoke } from "./guards/Revoke.sol";
56
+ import { Revoke, RevokeAllowance, RevokeAsset } from "./guards/Revoke.sol";
50
57
 
51
58
  // Query endpoints
52
59
  import { QueryBase } from "./queries/Base.sol";
package/README.md CHANGED
@@ -219,7 +219,10 @@ pipeline.
219
219
 
220
220
  The built-in surface is also available as two independent feature bundles:
221
221
  `Admins` provides annotate, authorize, unauthorize, and executePayable;
222
- `Guardians` provides appoint, dismiss, and revoke. The full `Host` composes
222
+ `Guardians` provides appoint, dismiss, and node revocation. Hosts that implement
223
+ the allowance hook can additionally inherit the opt-in `RevokeAllowance` guard,
224
+ which accepts `hostAsset { uint host, bytes32 asset }` entries and always applies
225
+ a zero allowance. The full `Host` composes
223
226
  both, while smaller hosts can inherit either bundle separately.
224
227
 
225
228
  Trust is explicit and minimal. Each host has an immutable **commander**
@@ -325,7 +328,8 @@ abstract contract MyCommand is CommandBase {
325
328
  ```
326
329
 
327
330
  The standard commands cover the common ledger movements: `deposit` and
328
- `depositPayable` (external funds in), `withdraw` and `burn` (funds out),
331
+ `depositPayable` (external funds in), `settlePayable` (funded settlement),
332
+ `withdraw` and `burn` (funds out),
329
333
  `debitAccount` and `creditAccount` (internal movements), `payout` (deliver
330
334
  state to other accounts), `allocate` (turn balance state into custody),
331
335
  `provision` (provision custody from an external allocation), `settle` (consume
@@ -378,8 +382,8 @@ Hosts that implement a pipeline locally can inherit `InternalDebitAccount`,
378
382
  `InternalCreditAccount`, and `InternalSettle` to advertise the canonical command
379
383
  endpoints while routing their local command IDs through `executeDebitAccount`,
380
384
  `executeCreditAccount`, and `executeSettle`. These adapters consume the
381
- memory-backed pipeline state directly and avoid an external self-call. The host
382
- dispatcher must reject nonzero step value before invoking them because all three
385
+ memory-backed pipeline state directly and avoid an external self-call. Pass the
386
+ step value into each adapter; all three reject nonzero value because the
383
387
  commands are non-funded.
384
388
 
385
389
  Positions also support backward-composed pipelines. In an exact-output route,
package/Utils.sol CHANGED
@@ -13,7 +13,7 @@ import { Ids } from "./utils/Ids.sol";
13
13
  import { Nodes } from "./utils/Nodes.sol";
14
14
  import { Selectors } from "./utils/Selectors.sol";
15
15
  import { Layout } from "./utils/Layout.sol";
16
- import { addrOr, applyBps, beforeBps, bytes32ToInt, bytes32ToString, divisible, hash32, intToBytes32, isFamily, matchesBase, MAX_BPS, max8, max16, max24, max32, max40, max64, max96, max128, max160, NotDivisible, retryTicket, toLocalBase, toUnspecifiedBase, ValueOverflow } from "./utils/Utils.sol";
16
+ import { addrOr, applyBps, beforeBps, bytes32ToInt, bytes32ToString, clear8, clear16, clear32, clear64, divisible, ensureAddr, hash32, intToBytes32, isFamily, matchesBase, MAX_BPS, max8, max16, max24, max32, max40, max64, max96, max128, max160, NotDivisible, replace8, replace16, replace32, replace64, retryTicket, toLocalBase, toUnspecifiedBase, ValueOverflow, ZeroAddress } from "./utils/Utils.sol";
17
17
 
18
18
 
19
19
 
package/codec/Blocks.sol CHANGED
@@ -335,6 +335,22 @@ library Blocks {
335
335
  }
336
336
  }
337
337
 
338
+ /// @notice Write a HOST_ASSET block at `i`.
339
+ /// @dev DANGER: Unchecked memory write. Reserve `Sizes.B64` bytes first.
340
+ /// @param dst Destination buffer.
341
+ /// @param i Relative write position.
342
+ /// @param host Host identifier to encode.
343
+ /// @param asset Asset identifier to encode.
344
+ function writeHostAsset(bytes memory dst, uint i, uint host, bytes32 asset) internal pure {
345
+ uint spec = Specs.HostAsset;
346
+ assembly ("memory-safe") {
347
+ let p := add(add(dst, 0x20), i)
348
+ mstore(p, spec)
349
+ mstore(add(p, 0x08), host)
350
+ mstore(add(p, 0x28), asset)
351
+ }
352
+ }
353
+
338
354
  // Three-word payloads
339
355
 
340
356
  /// @notice Write an ALLOCATION block at `i`.
@@ -1285,6 +1301,22 @@ library Blocks {
1285
1301
  }
1286
1302
  }
1287
1303
 
1304
+ /// @notice Decode a low-level fixed-width HOST_ASSET block at `abs`.
1305
+ /// @param abs Absolute block position.
1306
+ /// @return host Decoded host identifier.
1307
+ /// @return asset Decoded asset identifier.
1308
+ function unpackHostAsset(uint abs) internal pure returns (uint host, bytes32 asset) {
1309
+ uint head;
1310
+ assembly ("memory-safe") {
1311
+ head := calldataload(abs)
1312
+ }
1313
+ if (head >> 192 != Specs.HostAsset >> 192) revert InvalidBlock();
1314
+ assembly ("memory-safe") {
1315
+ host := calldataload(add(abs, 0x08))
1316
+ asset := calldataload(add(abs, 0x28))
1317
+ }
1318
+ }
1319
+
1288
1320
  /// @notice Decode a low-level fixed-width TRANSACTION block at `abs`.
1289
1321
  /// @param abs Absolute block position.
1290
1322
  /// @return from Decoded debit account.
@@ -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, Position, Tx} from "../core/Types.sol";
4
+ import {AssetAmount, AccountAsset, HostAsset, AccountAmount, HostAmount, HostAccountAsset, Position, Tx} from "../core/Types.sol";
5
5
  import {Blocks} from "./Blocks.sol";
6
6
  import {Sizes, Specs} from "./Specs.sol";
7
7
  import {Cursors, Cur} from "../utils/Cursors.sol";
@@ -276,6 +276,16 @@ library Decoders {
276
276
  (account, asset) = Blocks.unpackAccountAsset(abs);
277
277
  }
278
278
 
279
+ /// @notice Decode and consume one HOST_ASSET block.
280
+ /// @param cur Cursor advanced past the block.
281
+ /// @return host Decoded host identifier.
282
+ /// @return asset Decoded asset identifier.
283
+ function unpackHostAsset(Cur memory cur) internal pure returns (uint host, bytes32 asset) {
284
+ uint abs;
285
+ (cur.state, abs) = cur.state.consume(Sizes.HostAsset);
286
+ (host, asset) = Blocks.unpackHostAsset(abs);
287
+ }
288
+
279
289
  /// @notice Decode and consume one AMOUNT block.
280
290
  /// @param cur Cursor advanced past the block.
281
291
  /// @return asset Decoded asset identifier.
@@ -462,6 +472,13 @@ library Decoders {
462
472
  (value.account, value.asset) = unpackAccountAsset(cur);
463
473
  }
464
474
 
475
+ /// @notice Decode one HOST_ASSET block into its structured value.
476
+ /// @param cur Cursor advanced past the block.
477
+ /// @return value Structured host and asset.
478
+ function unpackHostAssetValue(Cur memory cur) internal pure returns (HostAsset memory value) {
479
+ (value.host, value.asset) = unpackHostAsset(cur);
480
+ }
481
+
465
482
  /// @notice Decode one AMOUNT block into its structured value.
466
483
  /// @param cur Cursor advanced past the block.
467
484
  /// @return value Structured asset amount.
package/codec/Keys.sol CHANGED
@@ -62,6 +62,8 @@ library Keys {
62
62
  bytes4 constant Status = bytes4(keccak256("#status"));
63
63
  /// @dev Structural account asset form - (bytes32 account, bytes32 asset)
64
64
  bytes4 constant AccountAsset = bytes4(keccak256("#accountAsset"));
65
+ /// @dev Structural host asset form - (uint host, bytes32 asset)
66
+ bytes4 constant HostAsset = bytes4(keccak256("#hostAsset"));
65
67
  /// @dev Structural account amount form - (bytes32 account, bytes32 asset, uint amount)
66
68
  bytes4 constant AccountAmount = bytes4(keccak256("#accountAmount"));
67
69
  /// @dev Structural host amount form - (uint host, bytes32 asset, uint amount)
package/codec/Readers.sol CHANGED
@@ -94,6 +94,18 @@ library Readers {
94
94
  }
95
95
  }
96
96
 
97
+ /// @notice Consume a HOST_ASSET block and return its fields.
98
+ /// @param cur Reader; advanced past the block.
99
+ /// @return host Host identifier.
100
+ /// @return asset Asset identifier.
101
+ function unpackHostAsset(Reader memory cur) internal pure returns (uint host, bytes32 asset) {
102
+ uint abs = consume(cur, Keys.HostAsset, 64, 64);
103
+ assembly ("memory-safe") {
104
+ host := mload(abs)
105
+ asset := mload(add(abs, 0x20))
106
+ }
107
+ }
108
+
97
109
  /// @notice Consume a POSITION block and return its fields.
98
110
  function unpackPosition(
99
111
  Reader memory cur
package/codec/Schema.sol CHANGED
@@ -81,6 +81,7 @@ library Schemas {
81
81
  string constant Amount = "{ bytes32 asset, uint amount }";
82
82
  string constant Balance = "{ bytes32 asset, uint amount }";
83
83
  string constant AccountAsset = "{ bytes32 account, bytes32 asset }";
84
+ string constant HostAsset = "{ uint host, bytes32 asset }";
84
85
 
85
86
  // Three-word payloads
86
87
 
package/codec/Specs.sol CHANGED
@@ -27,6 +27,8 @@ library Sizes {
27
27
  uint constant Amount = B64;
28
28
  /// @dev BALANCE block: 8 header + 32 asset + 32 amount = 72 bytes
29
29
  uint constant Balance = B64;
30
+ /// @dev HOST_ASSET block: 8 header + 32 host + 32 asset = 72 bytes
31
+ uint constant HostAsset = B64;
30
32
  /// @dev ALLOCATION/CUSTODY block: 8 header + 32 host + 32 asset + 32 amount = 104 bytes
31
33
  uint constant HostAmount = B96;
32
34
  /// @dev TRANSACTION block: 8 header + 32 from + 32 to + 32 asset + 32 amount = 136 bytes
@@ -88,6 +90,7 @@ library Specs {
88
90
 
89
91
  uint constant Status = uint(bytes32(Keys.Status)) | Exact32;
90
92
  uint constant AccountAsset = uint(bytes32(Keys.AccountAsset)) | Exact64;
93
+ uint constant HostAsset = uint(bytes32(Keys.HostAsset)) | Exact64;
91
94
  uint constant AccountAmount = uint(bytes32(Keys.AccountAmount)) | Exact96;
92
95
  uint constant HostAmount = uint(bytes32(Keys.HostAmount)) | Exact96;
93
96
  uint constant HostAccountAsset = uint(bytes32(Keys.HostAccountAsset)) | Exact96;
package/codec/Writers.sol CHANGED
@@ -204,6 +204,15 @@ library Writers {
204
204
  Blocks.writeAccountAsset(writer.dst, i, account, asset);
205
205
  }
206
206
 
207
+ /// @notice Append a HOST_ASSET block.
208
+ /// @param writer Destination writer.
209
+ /// @param host Host identifier to encode.
210
+ /// @param asset Asset identifier to encode.
211
+ function appendHostAsset(Writer memory writer, uint host, bytes32 asset) internal pure {
212
+ uint i = reserve(writer, Sizes.HostAsset);
213
+ Blocks.writeHostAsset(writer.dst, i, host, asset);
214
+ }
215
+
207
216
  /// @notice Append an ALLOCATION block.
208
217
  /// @param writer Destination writer.
209
218
  /// @param host Host identifier to encode.
package/commands/Base.sol CHANGED
@@ -22,6 +22,8 @@ using Executions for Execution;
22
22
  abstract contract CommandBase is CallerAccess, EndpointBase, ReceivedEvent {
23
23
  /// @dev Thrown when `onlyActive` finds that `deadline` has already passed.
24
24
  error Expired();
25
+ /// @dev Thrown when a non-funded internal command receives native value.
26
+ error ValueNotAllowed();
25
27
 
26
28
  /// @dev Restrict execution to trusted callers.
27
29
  modifier onlyCommand() {
@@ -54,13 +54,16 @@ abstract contract InternalCreditAccount is CreditAccount {
54
54
  /// @param account Account credited by each balance.
55
55
  /// @param state BALANCE block stream held in pipeline memory.
56
56
  /// @param input Empty input required by the command schema.
57
+ /// @param value Native value assigned to the command; must be zero.
57
58
  /// @return output Empty output state.
58
59
  /// @return transactions Empty transaction stream.
59
60
  function executeCreditAccount(
60
61
  bytes32 account,
61
62
  bytes memory state,
62
- bytes calldata input
63
+ bytes calldata input,
64
+ uint128 value
63
65
  ) internal returns (bytes memory, bytes memory) {
66
+ if (value != 0) revert ValueNotAllowed();
64
67
  if (input.length != 0) revert Executions.ZeroStride();
65
68
  if (state.length == 0) revert Blocks.EmptyRun();
66
69
 
@@ -58,13 +58,16 @@ abstract contract InternalDebitAccount is DebitAccount {
58
58
  /// @param account Account whose funds are debited.
59
59
  /// @param state Empty pipeline state required by the command schema.
60
60
  /// @param input AMOUNT block stream.
61
+ /// @param value Native value assigned to the command; must be zero.
61
62
  /// @return output BALANCE block stream matching the debited amounts.
62
63
  /// @return transactions Empty transaction stream.
63
64
  function executeDebitAccount(
64
65
  bytes32 account,
65
66
  bytes memory state,
66
- bytes calldata input
67
+ bytes calldata input,
68
+ uint128 value
67
69
  ) internal returns (bytes memory, bytes memory) {
70
+ if (value != 0) revert ValueNotAllowed();
68
71
  if (state.length != 0) revert Executions.ZeroStride();
69
72
  if (input.length == 0) revert Blocks.EmptyRun();
70
73
 
@@ -0,0 +1,83 @@
1
+ // SPDX-License-Identifier: GPL-3.0-only
2
+ pragma solidity ^0.8.33;
3
+
4
+ import {Execution, Executions, CommandBase, Lanes, Specs} from "./Base.sol";
5
+ import {RepayHook} from "../core/Settlement.sol";
6
+ import {Action} from "../annotations/Action.sol";
7
+ import {Actions} from "../utils/Actions.sol";
8
+
9
+ using Executions for Execution;
10
+
11
+ /// @notice Hook implemented by hosts that repay position liabilities using native value.
12
+ abstract contract RepayPayableHook {
13
+ /// @notice Override to repay one liability for `account` with a shared value budget.
14
+ /// @param account Account whose liability is being repaid.
15
+ /// @param liability Identifier for the liability side.
16
+ /// @param debt Quantity on the liability side.
17
+ /// @param funds Mutable execution used only for its remaining native-value budget.
18
+ function repay(bytes32 account, bytes32 liability, uint debt, Execution memory funds) internal virtual;
19
+ }
20
+
21
+ /// @title Repay
22
+ /// @notice Command that repays POSITION liabilities and returns their assets as BALANCE state.
23
+ abstract contract Repay is CommandBase, RepayHook, Action {
24
+ uint private immutable descriptor;
25
+
26
+ constructor() {
27
+ uint id;
28
+ (id, descriptor) = command("repay", Specs.Position, Specs.Empty, Specs.Balance, 0, false, false);
29
+ action(id, Actions.Settle);
30
+ }
31
+
32
+ /// @notice Repay each POSITION liability and return its asset as BALANCE state.
33
+ /// @param state POSITION block stream.
34
+ /// @return BALANCE output state containing each released asset side.
35
+ /// @return Empty transaction stream.
36
+ function repay(
37
+ bytes32 account,
38
+ bytes calldata state,
39
+ bytes calldata input
40
+ ) external onlyCommand returns (bytes memory, bytes memory) {
41
+ Execution memory exec = openCommand(state, input, descriptor, 0);
42
+
43
+ while (exec.more()) {
44
+ (bytes32 asset, uint amount, bytes32 liability, uint debt) = exec.unpackPosition(Lanes.State);
45
+ repay(account, liability, debt);
46
+ exec.outputBalance(asset, amount);
47
+ }
48
+
49
+ return close(exec, account);
50
+ }
51
+ }
52
+
53
+ /// @title RepayPayable
54
+ /// @notice Funded command that repays POSITION liabilities and returns their assets as BALANCE state.
55
+ abstract contract RepayPayable is CommandBase, RepayPayableHook, Action {
56
+ uint private immutable descriptor;
57
+
58
+ constructor() {
59
+ uint id;
60
+ (id, descriptor) = command("repayPayable", Specs.Position, Specs.Empty, Specs.Balance, 0, true, false);
61
+ action(id, Actions.Settle);
62
+ }
63
+
64
+ /// @notice Repay each POSITION liability and return its asset as BALANCE state.
65
+ /// @param state POSITION block stream.
66
+ /// @return BALANCE output state containing each released asset side.
67
+ /// @return Remaining native value as a refund transaction stream.
68
+ function repayPayable(
69
+ bytes32 account,
70
+ bytes calldata state,
71
+ bytes calldata input
72
+ ) external payable onlyCommand returns (bytes memory, bytes memory) {
73
+ Execution memory exec = openCommand(state, input, descriptor, 0);
74
+
75
+ while (exec.more()) {
76
+ (bytes32 asset, uint amount, bytes32 liability, uint debt) = exec.unpackPosition(Lanes.State);
77
+ repay(account, liability, debt, exec);
78
+ exec.outputBalance(asset, amount);
79
+ }
80
+
81
+ return close(exec, account);
82
+ }
83
+ }
@@ -11,6 +11,25 @@ import {Reader, Readers} from "../codec/Readers.sol";
11
11
  using Executions for Execution;
12
12
  using Readers for Reader;
13
13
 
14
+ /// @notice Hook implemented by hosts that settle positions using native value.
15
+ abstract contract SettlePayableHook {
16
+ /// @notice Override to settle one position for `account` with a shared value budget.
17
+ /// @param account Account whose position is being settled.
18
+ /// @param asset Identifier for the asset side.
19
+ /// @param amount Quantity on the asset side.
20
+ /// @param liability Identifier for the liability side.
21
+ /// @param debt Quantity on the liability side.
22
+ /// @param funds Mutable execution used only for its remaining native-value budget.
23
+ function settle(
24
+ bytes32 account,
25
+ bytes32 asset,
26
+ uint amount,
27
+ bytes32 liability,
28
+ uint debt,
29
+ Execution memory funds
30
+ ) internal virtual;
31
+ }
32
+
14
33
  /// @title Settle
15
34
  /// @notice Command that consumes POSITION state blocks through a virtual hook.
16
35
  abstract contract Settle is CommandBase, SettleHook, Action {
@@ -47,6 +66,37 @@ abstract contract Settle is CommandBase, SettleHook, Action {
47
66
  }
48
67
  }
49
68
 
69
+ /// @title SettlePayable
70
+ /// @notice Funded command that consumes POSITION state blocks through a virtual hook.
71
+ abstract contract SettlePayable is CommandBase, SettlePayableHook, Action {
72
+ uint private immutable descriptor;
73
+
74
+ constructor() {
75
+ uint id;
76
+ (id, descriptor) = command("settlePayable", Specs.Position, Specs.Empty, Specs.Empty, 0, true, false);
77
+ action(id, Actions.Settle);
78
+ }
79
+
80
+ /// @notice Settle each POSITION block with access to a shared native-value budget.
81
+ /// @param state POSITION block stream.
82
+ /// @return Empty output state.
83
+ /// @return Remaining native value as a refund transaction stream.
84
+ function settlePayable(
85
+ bytes32 account,
86
+ bytes calldata state,
87
+ bytes calldata input
88
+ ) external payable onlyCommand returns (bytes memory, bytes memory) {
89
+ Execution memory exec = openCommand(state, input, descriptor, 0);
90
+
91
+ while (exec.more()) {
92
+ (bytes32 asset, uint amount, bytes32 liability, uint debt) = exec.unpackPosition(Lanes.State);
93
+ settle(account, asset, amount, liability, debt, exec);
94
+ }
95
+
96
+ return close(exec, account);
97
+ }
98
+ }
99
+
50
100
  /// @title InternalSettle
51
101
  /// @notice Extends the advertised settle command with memory-state pipeline dispatch.
52
102
  /// @dev This adapter is not a separate command. It uses the command ID and settlement hook
@@ -56,13 +106,16 @@ abstract contract InternalSettle is Settle {
56
106
  /// @param account Account for which each position is settled.
57
107
  /// @param state POSITION block stream held in pipeline memory.
58
108
  /// @param input Empty input required by the command schema.
109
+ /// @param value Native value assigned to the command; must be zero.
59
110
  /// @return output Empty output state.
60
111
  /// @return transactions Empty transaction stream.
61
112
  function executeSettle(
62
113
  bytes32 account,
63
114
  bytes memory state,
64
- bytes calldata input
115
+ bytes calldata input,
116
+ uint128 value
65
117
  ) internal returns (bytes memory, bytes memory) {
118
+ if (value != 0) revert ValueNotAllowed();
66
119
  if (input.length != 0) revert Executions.ZeroStride();
67
120
  if (state.length == 0) revert Blocks.EmptyRun();
68
121
 
@@ -12,7 +12,7 @@ abstract contract AllowanceHook {
12
12
  /// or another host-specific authorization record.
13
13
  /// @param peer Host node receiving the allowed cap.
14
14
  /// @param asset Asset identifier.
15
- /// @param amount Allowed cap amount.
15
+ /// @param amount Allowed cap amount. A zero amount MUST revoke the allowance.
16
16
  function allowance(uint peer, bytes32 asset, uint amount) internal virtual;
17
17
  }
18
18
 
@@ -28,22 +28,23 @@ abstract contract PostHook {
28
28
  function post(bytes32 from, bytes32 to, bytes32 asset, uint amount) internal virtual;
29
29
  }
30
30
 
31
+ /// @title RepayHook
32
+ /// @notice Hook for repaying one account liability.
33
+ abstract contract RepayHook {
34
+ /// @notice Override to repay `debt` denominated in `liability` for `account`.
35
+ function repay(bytes32 account, bytes32 liability, uint debt) internal virtual;
36
+ }
37
+
31
38
  /// @title SettleHook
32
39
  /// @notice Hook for settling one asset-liability position.
33
40
  abstract contract SettleHook {
34
41
  /// @notice Override to settle one position for `account`.
35
- function settle(
36
- bytes32 account,
37
- bytes32 asset,
38
- uint amount,
39
- bytes32 liability,
40
- uint debt
41
- ) internal virtual;
42
+ function settle(bytes32 account, bytes32 asset, uint amount, bytes32 liability, uint debt) internal virtual;
42
43
  }
43
44
 
44
45
  /// @title Settlement
45
46
  /// @notice Default account-hook implementation for transaction posting and position settlement.
46
- abstract contract Settlement is PostHook, SettleHook, DebitAccountHook, CreditAccountHook {
47
+ abstract contract Settlement is PostHook, SettleHook, RepayHook, DebitAccountHook, CreditAccountHook {
47
48
  /// @notice Post one transaction by debiting its source and crediting its destination.
48
49
  /// Returns without calling either hook when `amount` is zero and skips either
49
50
  /// operation when the corresponding account is zero.
@@ -53,6 +54,12 @@ abstract contract Settlement is PostHook, SettleHook, DebitAccountHook, CreditAc
53
54
  if (to != 0) creditAccount(to, asset, amount);
54
55
  }
55
56
 
57
+ /// @notice Repay one liability by debiting it from the account.
58
+ /// Skips the debit when `debt` is zero.
59
+ function repay(bytes32 account, bytes32 liability, uint debt) internal virtual override {
60
+ if (debt != 0) debitAccount(account, liability, debt);
61
+ }
62
+
56
63
  /// @notice Settle one position by crediting its asset and debiting its liability.
57
64
  /// Skips either operation when its corresponding amount is zero.
58
65
  function settle(
package/core/Types.sol CHANGED
@@ -27,6 +27,14 @@ struct AccountAmount {
27
27
  uint amount;
28
28
  }
29
29
 
30
+ /// @notice Host-scoped asset shape.
31
+ struct HostAsset {
32
+ /// @dev Host node identifier.
33
+ uint host;
34
+ /// @dev Asset identifier.
35
+ bytes32 asset;
36
+ }
37
+
30
38
  /// @notice Host-scoped asset and amount shape.
31
39
  struct HostAmount {
32
40
  /// @dev Host node identifier.
@@ -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, Position, 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";
@@ -287,6 +287,31 @@ library Executions {
287
287
  (value.account, value.asset) = unpackAccountAsset(exec, lane);
288
288
  }
289
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
+
290
315
  /// @notice Decode and consume one AMOUNT block from `lane`.
291
316
  /// @param exec Execution whose decoder is advanced.
292
317
  /// @param lane Decoder lane to consume.
@@ -805,6 +830,15 @@ library Executions {
805
830
  Blocks.writeAccountAsset(exec.output, i, account, asset);
806
831
  }
807
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
+
808
842
  /// @notice Append an ALLOCATION block to execution output.
809
843
  /// @param exec Execution receiving the block.
810
844
  /// @param host Host identifier to encode.
package/guards/Revoke.sol CHANGED
@@ -1,6 +1,8 @@
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";
5
+ import {DenyAssetsHook} from "../commands/admin/DenyAssets.sol";
4
6
  import {GuardBase} from "./Base.sol";
5
7
  import {Specs} from "../Codec.sol";
6
8
  import {Execution, Executions, Lanes} from "../execution/Execution.sol";
@@ -25,6 +27,47 @@ abstract contract Revoke is GuardBase {
25
27
  uint node = exec.unpackNode(Lanes.Input);
26
28
  setNode(node, false);
27
29
  }
30
+ }
31
+ }
32
+
33
+ /// @title RevokeAllowance
34
+ /// @notice Guardian action that revokes host-scoped asset allowances.
35
+ /// @dev Opt-in guard. Hosts expose it by inheriting this contract and implementing AllowanceHook.
36
+ abstract contract RevokeAllowance is GuardBase, AllowanceHook {
37
+ uint private immutable descriptor;
38
+
39
+ constructor() {
40
+ (, descriptor) = guard("revokeAllowance", Specs.HostAsset);
41
+ }
42
+
43
+ /// @notice Revoke every HOST_ASSET allowance in `input` as the active guardian.
44
+ function revokeAllowance(bytes calldata input) external onlyGuardian {
45
+ Execution memory exec = openInput(input, descriptor, 0);
28
46
 
47
+ while (exec.more()) {
48
+ (uint peer, bytes32 asset) = exec.unpackHostAsset(Lanes.Input);
49
+ allowance(peer, asset, 0);
50
+ }
51
+ }
52
+ }
53
+
54
+ /// @title RevokeAsset
55
+ /// @notice Guardian action that denies assets through the host's existing asset hook.
56
+ /// @dev Opt-in guard. Hosts expose it by inheriting this contract and implementing DenyAssetsHook.
57
+ abstract contract RevokeAsset is GuardBase, DenyAssetsHook {
58
+ uint private immutable descriptor;
59
+
60
+ constructor() {
61
+ (, descriptor) = guard("revokeAsset", Specs.Asset);
62
+ }
63
+
64
+ /// @notice Deny every ASSET block in `input` as the active guardian.
65
+ function revokeAsset(bytes calldata input) external onlyGuardian {
66
+ Execution memory exec = openInput(input, descriptor, 0);
67
+
68
+ while (exec.more()) {
69
+ bytes32 asset = exec.unpackAsset(Lanes.Input);
70
+ denyAsset(asset);
71
+ }
29
72
  }
30
73
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rootzero/contracts",
3
- "version": "1.16.0",
3
+ "version": "1.18.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",